Status and known limitations#
This page is a snapshot of what EmbCC supports and what it does not: how complete each target is, which language features, attributes, builtins and options it refuses (with the exact diagnostic for each), where its calling conventions differ from the platform's, which defects are known, which optimizations GCC and Clang perform that EmbCC does not, and how to report a bug. It is for anyone deciding whether EmbCC can build their code, and for people changing EmbCC who need the list of open work. The per-target reference is Targets; the language levels are in C language and C++ support.
Summary#
- x86-64 and AArch64 ELF (bare metal, EmbLinkOS, Linux) are the
complete targets: C and C++ with exceptions, the full optimizer,
register allocation, DWARF debug information, and EmbCC's own C, C++
and compiler-runtime libraries. Linux programs are static images.
embldlinks x86-64; AArch64 objects need another linker. - The embedded targets -- Cortex-M (ARMv6-M to ARMv8-M), ARMv7-A,
RISC-V (RV32 and RV64), MIPS32 and MIPS64, LoongArch64, Xtensa,
TriCore, PowerPC, RX, SPARC, ColdFire and AVR -- are bare-metal
targets with EmbCC's compiler runtime, C library (on AVR,
lib/avr's smaller one), assembler and linker. C++ compiles on every one but AVR, without exceptions, and the 32-bit ones have a C++ runtime (make libcxx-embedded). - Apple arm64 and macOS x86-64 compile C and C++ for the system
linker, and the result runs natively.
-gis accepted and writes no debug information yet. - Windows x86-64 produces COFF objects that are not yet compatible with the Microsoft x64 ABI. EmbCC warns on every such compile.
What each target compiles, construct by construct, is in
What each target compiles: a page generated from
the compiler's own answers and checked against it by
tests/golden/limits.sh, so it cannot drift as this page did.
How EmbCC refuses#
A construct EmbCC does not implement is an error that names it. EmbCC does not compile an unsupported construct into code that does something else. The exceptions to that rule, where something is accepted and silently has a different effect, are listed under Known defects.
A diagnostic has this form:
embcc: FILE:LINE:COL: error: MESSAGE
Some diagnostics carry no column, and those about the command line carry
no file (embcc: error: MESSAGE). The tables on this page quote
MESSAGE. Names such as f, x, g and 'd' in a quoted message
stand for the function, variable or type in your program.
A backend that cannot lower an operation names itself, the operation and the IR instruction:
embcc: f.c:3: error: the ARMv7-M backend cannot lower __builtin_frame_address or __builtin_return_address (this backend keeps no frame-pointer chain) yet (function f) [frameaddr w=8 size=4]
The backend names are ARMv7-M (every Cortex-M target), RV32, RV64,
MIPS32, LoongArch64 and AVR.
An attribute EmbCC does not know is a warning, not an error, and is ignored (see Attributes).
Targets#
| Target | Code generation | Optimizer | Register allocation | Debug information | Linking | Run-time libraries |
|---|---|---|---|---|---|---|
x86-64 ELF: x86_64-elf, x86_64-emblink, x86_64-linux-gnu |
Complete. SSE2 floating point, x87 long double, __int128. C and C++ with exceptions. |
Every pass, including the vectorizer. | Graph colouring; integer and SSE classes. | DWARF 4. | embcc links in-process with embld. Static images only. EMBX through embld --embx. |
libc.a, libcxx.a; crt1.o and librt.a on Linux. |
AArch64 ELF: aarch64-elf, aarch64-emblink, aarch64-linux-gnu |
Complete. FP/SIMD, binary128 long double in software, __int128. C and C++ with exceptions. |
Every pass except vectorization. | Graph colouring; integer and FP/SIMD classes. | DWARF 4. | An external AArch64 linker. embld refuses AArch64 objects. |
libc.a, libcxx.a; crt1.o and librt.a on Linux. |
Apple arm64: aarch64-apple-darwin |
AArch64 backend, Apple's arm64 convention, Mach-O. C and C++ with exceptions. | As AArch64. | As AArch64. | None yet: -g is accepted with a warning. |
The system linker. | The system's. EmbCC's C headers do not match it; see Darwin. |
macOS x86-64: x86_64-apple-darwin |
x86-64 backend, Mach-O. C++ objects that use exceptions do not link. | As x86-64. | As x86-64. | None yet: -g is accepted with a warning. |
The system linker. | The system's. |
Windows x86-64: x86_64-windows-gnu |
x86-64 backend, COFF, part of the Microsoft x64 convention. C++ only without exceptions and unwind tables. | As x86-64. | As x86-64. | Refused. | An external linker. | None. |
Cortex-M: thumbv6m-none-eabi, thumbv8m.base-none-eabi, thumbv7m-none-eabi, thumbv7em-none-eabi[hf], thumbv8m.main-none-eabi[hf] |
Thumb-1 for ARMv6-M and ARMv8-M Baseline (with Baseline's divides and exclusives), Thumb-2 for ARMv7-M, ARMv7E-M and ARMv8-M Mainline. Soft float, or a single-precision FPU with double in software. The code generator uses no DSP instructions; the assembler and inline asm accept them. TrustZone-M's Secure side (-mcmse, soft float) on both ARMv8-M profiles. C, and C++ without exceptions. |
Every pass except vectorization. ARMv6-M, which has no long multiply, divides by a constant through __aeabi_uidiv, as GCC does. |
Graph colouring; register pairs for 64-bit values; s16-s31 with an FPU. |
DWARF 4, with known problems. | embld. |
librt.a, libc.a and libcxx.a, one per triple (make rt-embedded libc-embedded libcxx-embedded). |
RV32: riscv32-unknown-elf |
RV32IMAC (with F and D: the ilp32f/ilp32d runtimes), binary128 long double in software. C, and C++ without exceptions. |
Every pass except vectorization. | Graph colouring; register pairs for 64-bit values. | DWARF 4, with known problems. | embld. |
librt.a, libc.a and libcxx.a. |
RV64: riscv64-unknown-elf |
RV64IMAC (with F and D: the lp64f/lp64d runtimes), __int128 and binary128 long double. C, and C++ without exceptions (untested). |
Every pass except vectorization. | Graph colouring. | DWARF 4, with known problems. | embld. |
librt.a and libc.a; no C++ runtime. |
MIPS32: mipsel-none-elf |
MIPS32r2, little-endian, o32, soft float. Delay slots filled from the instruction before the transfer where safe, else a nop; jump tables. C, and C++ without exceptions. |
Every pass except vectorization. | Graph colouring; register pairs for 64-bit values. | DWARF 4, with known problems. | embld, which also links clang's objects. |
librt.a, libc.a and libcxx.a (make rt-embedded libc-embedded libcxx-embedded). |
LoongArch64: loongarch64-unknown-elf |
LA64 base integer ISA, LP64S soft float, the normal code model; jump tables; __int128 and binary128 long double in software; atomics of every width but 16 bytes. C, and C++ without exceptions. |
Every pass except vectorization. | Graph colouring. | DWARF 4, with known problems. | embld, which also links clang's objects (its medium code model and GOT accesses included). |
librt.a and libc.a (make rt-embedded libc-embedded). |
AVR: avr |
ATmega328P (AVR5). 16-bit int, 32-bit double. C only: C++ is refused. |
Every pass except vectorization; division by a constant is a call; a few more passes do nothing on AVR. | Graph colouring over register runs; each function is generated under several allocation modes and the shortest result kept. Off under -g. |
DWARF 4: addresses, line rows, the Y frame base, globals. | embld. |
librt.a, and lib/avr's library (<avr/pgmspace.h>, <avr/eeprom.h>, printf, strings). |
ARMv7-A (armv7a-none-eabi[hf]), MIPS64 (mips64[el]-none-elf), Xtensa (xtensa-none-elf), TriCore (tricore-none-elf), PowerPC (powerpc-none-eabi), RX (rx-none-elf), SPARC (sparc-none-elf), ColdFire (m68k-none-elf) |
C, and C++ without exceptions; each target's ISA, ABI and limits are in its section of Targets. | Every pass except vectorization. | Graph colouring. | DWARF 4, with known problems. | embld, with -Ttext/-Tdata; linker scripts only on ARMv7-A. |
librt.a and libc.a; libcxx.a on all but little-endian MIPS64. |
The register allocator is described in Register allocation, the passes and their target dependencies in The optimizer, the libraries in Libraries, and how each target is tested (QEMU boards, a Linux kernel, native macOS runs) in Testing. What the Darwin and Windows targets refuse is listed under ABI limitations.
Linking#
The driver links x86-64 ELF programs and, for the embedded targets,
images whose memory map the build gives: a linker script (-T, on ARM,
RISC-V, AVR and MIPS32) or -Wl,-Ttext/-Tdata (on every one).
A firmware link without one stops with embcc: error: linking a TRIPLE image needs its memory map. Every other target (AArch64 ELF, Mach-O,
COFF) stops with embcc: error: cannot link for TRIPLE, because embld
does not read those objects. AArch64 objects are refused by embld with:
embld: FILE: a 64-bit object for machine 183; only x86-64, RV64 (EM_RISCV), LoongArch64 (EM_LOONGARCH) and MIPS64 (EM_MIPS) are supported
The driver compiles several sources in one command by running itself
once for each (-j N at a time), on a host that can run a program
(macOS, Linux). A build with PROCESS=none -- EmbLinkOS, which has no
fork/exec -- compiles one source per command and refuses a second:
embcc: error: more than one source file ('a.c' and 'b.c'), and this host cannot run a compiler for each (EmbCC was built with PROCESS=none): compile each with -c and link the objects (embcc a.o b.o -o OUT)
embar is EmbCC's archiver (ar and ranlib); the library builds
use it.
There is no position-independent code, no shared library and no dynamic
linking. -fPIC, -shared and the related options are refused (see
Options). EmbLD reads GNU ld linker scripts (embld -T) for
ARM, RISC-V, AVR and MIPS32 images, not yet for the other targets:
embld: -T: a linker script is supported for ARM, RISC-V, AVR and MIPS32 images only (this one is machine 62)
Where it reads one, the driver links firmware
with them (embcc -T board.ld a.o b.o -o fw.elf). --gc-sections drops
the sections nothing reaches, on every machine, for objects built with
-ffunction-sections -fdata-sections; -Map and
--print-memory-usage report the layout as ld does.
Assembly#
| Input or output | x86-64 | Every other target |
|---|---|---|
.s and .S files (GNU syntax) |
Refused: no assembly-file support for x86_64-elf yet; its instruction encoder exists (inline __asm__ works) but this driver has not been wired to it |
Assembled, with GNU as's directives, macros, conditionals, sections, expressions, literal pools and branch relaxation; ARM's CMSIS and ST's startup files assemble to clang's object. On AArch64 the instructions are those of its inline-asm vocabulary, the kernel's (no ldp/stp or arithmetic yet) (embas) |
.asm files (NASM syntax) |
Assembled | Assembled as x86-64; see Known defects |
-S |
.byte directives with the disassembly in comments |
Elsewhere .byte directives without mnemonics; on MIPS a relocated instruction is written symbolically (jal f, lui $2, %hi(g)), because llvm-mc's MIPS .reloc knows none of those relocations. Every target's text reassembles into the object: checked with EmbCC's own assembler on all but x86-64, and with llvm-mc where it has the target |
File-scope __asm__ |
Labels, .globl, .byte/.long/.quad and the instructions and, call, jmp, ret |
Every other target: the GNU-syntax assembler, as in a .S file, into .text. On AArch64 its vocabulary is the kernel's: the system instructions, mov, cmp, ldr, b, br, cbz, ret, nop, .inst; no ldp/stp or arithmetic yet. Into Mach-O (Apple arm64) a block holds labels and data only |
Any other instruction in a file-scope asm block is refused. On x86-64:
file-scope asm instruction not supported: "movq %rdi, %rax" (EmbCC assembles .global/labels/.byte/.long/.quad and and/call/jmp/ret)
On Apple arm64, every instruction in a block is refused, including the four that x86-64 accepts, because the Mach-O writer takes no relocation type from an assembler:
file-scope asm instruction "ret": EmbCC assembles a block's instructions on x86-64 (a few) and on every ELF target, not yet into Mach-O objects. On this target write the block as .byte/.long data (see lib/libc/src/setjmp).
Inline asm inside a function is assembled on every target; its
vocabulary is in Inline assembly.
On MIPS32 a .S file, a file-scope block, a naked function and an
inline-asm template start in .set reorder mode, as with GNU as, GCC and
clang: the assembler puts a nop in each delay slot unless the code says
.set noreorder.
Thread-local storage#
__thread and _Thread_local use the local-exec model on x86-64 and
AArch64 ELF (R_X86_64_TPOFF32, R_AARCH64_TLSLE_*). On the
embedded targets a thread-local object is placed in .tbss or .tdata
but addressed with ordinary absolute or PC-relative relocations, so
there is one instance, not one per thread. On Darwin and Windows it is
refused (see ABI limitations).
Target-specific refusals#
Which constructs each target refuses -- __int128, long double,
_Float16, the atomics of each width, the frame builtins, C++ and its
exceptions, -S, -g, unwind tables and the rest -- is the generated
page What each target compiles, with the message
for each. Beyond those:
| Target | Construct | Diagnostic |
|---|---|---|
| x86-64 | Floating point under -mno-sse |
floating point needs SSE, which -mno-sse forbids |
| AArch64 | Floating point under -mgeneral-regs-only |
floating point used under -mgeneral-regs-only (in 'f') |
| Cortex-M | An asm output wider than 4 bytes |
the ARMv7-M backend cannot lower an asm output wider than a register yet (function f) [asm w=4 size=4] |
| RV32 | An asm output wider than a register |
the RV32 backend cannot lower an asm output wider than a register yet (function f) [asm w=4 size=4] |
| AVR | An 8-byte asm operand |
an asm operand of 8 bytes needs 8 consecutive registers, which is more than this backend keeps free across an asm |
| MIPS32 | -mhard-float, -mabicalls, -mabi=n32, -G8, a core that is not MIPS32r2 |
each refused by name; see Invoking EmbCC |
| LoongArch64 | -mabi=lp64d, -mabi=lp64f, -mfpu=64, -mdouble-float, -mcmodel=extreme, -mstrict-align, -mlsx, -mlasx, an LA32 -march |
each refused by name; see Targets |
| LoongArch64 | In assembly: la.abs, the TLS, extreme-model and absolute-GOT operators, floating-point and vector instructions |
each refused by name, with the statement |
Each target's other refused options are listed in its section of Targets.
Runtime libraries#
- EmbCC ships a C library for
x86_64-elf,aarch64-elf, both Linux triples and EmbLinkOS, and a C++ library for the x86-64 and AArch64 ELF triples (make libc,make libcxx,make libc-linux). - For the embedded targets,
make rt-embeddedbuilds the compiler runtime (librt.a) for every triple, andmake libc-embeddedthe C library for every one but AVR, whose library islib/avr's (<avr/pgmspace.h>,<avr/eeprom.h>, a smallprintfand the string functions).make libcxx-embeddedbuilds the C++ runtime (operator new, the guards, RTTI) for the 32-bit ones; RV64, little-endian MIPS64 and LoongArch64 have none. - The runtime routines have libgcc's names (
__divdi3,__addsf3,__multi3, ...), and on ARM the run-time ABI's__aeabi_*routines as well. - On Linux, time functions enter the kernel on every call (the vDSO is
not used), and the file-status functions use
statx, so they need Linux 4.11 or later.
C language features#
EmbCC compiles one C dialect, with the GNU extensions and many C23 features; C language describes what is supported. This section lists what is not.
Types#
| Construct | Diagnostic |
|---|---|
A _Float16 through ... |
'g': passing a _Float16 through '...' is not supported yet: it is not promoted there, ... |
A struct holding a _Float16, by value, with the ARM or RISC-V hard-float convention |
'f': a struct holding a _Float16, passed or returned by value, is not supported yet with the hard-float calling convention, ... |
_Float128, __float128 on x86-64 |
_Float128 is not supported on x86-64: `long double` here is x87's 80-bit extended format, not IEEE binary128, so it is not the same type |
_Float128 on Cortex-M, AVR and Apple arm64 |
_Float128 is not supported on this target: it has no 128-bit floating-point type |
_BitInt(N) wider than 64 bits |
_BitInt(65) is wider than BITINT_MAXWIDTH, which is 64 here |
_Decimal32, _Decimal64, _Decimal128 |
expected a type before '_Decimal32' |
_Imaginary |
expected a type before '_Imaginary' |
_Atomic on anything but an integer or a pointer |
reading an _Atomic struct s is not supported: EmbCC makes the operators atomic for integers and pointers only (the first words name the operation) |
C23 constexpr of a non-integer type |
constexpr 'd' of type double is not supported: EmbCC takes integer constants |
_Float128 is accepted where long double is binary128 (AArch64 ELF,
RISC-V).
Declarations, statements and expressions#
| Construct | Diagnostic |
|---|---|
asm goto |
`asm goto` is not supported: its template branches to a label, which needs a patchable placeholder in each backend's inline assembler and CFG edges the optimizer honours. Use a normal asm that sets a value and branch on that |
| Nested function definitions | A syntax error: expected ';' before '{' |
| K&R (old-style) function definitions | A syntax error: expected a parameter type before 'a' |
C23 u8 character constants (u8'a') |
A syntax error |
C23 storage-class specifiers in a compound literal ((static int[]){1, 2}) |
expected an expression, got 'static' |
A range designator followed by further designators ([0 ... 1].a = 5) |
a range designator [lo ... hi] followed by more designators is not supported |
| An unsized array whose element count depends on brace elision that only the types can resolve | cannot size 'g' from its initializer: the braces it leaves out read as 2 elements without the types and 1 with them; brace each element |
Implicit int and implicit function declarations are errors, as C99
requires. Trigraphs are not replaced.
Preprocessor#
| Construct | Diagnostic |
|---|---|
#ident, #sccs, #assert, #unassert |
unknown directive '#ident' |
#embed parameters (limit, prefix, suffix, if_empty) |
#embed parameters (limit, prefix, suffix, if_empty) are not supported; ignoring one would embed the wrong bytes |
#pragma pack(push, NAME, ...) |
#pragma pack(push, name) is not supported |
#pragma pack(pop, ...) with a name or count |
#pragma pack(pop, ...) with a name or count is not supported |
#pragma pack inside a struct body |
#pragma pack inside a struct body is not supported: put it before the struct |
#pragma pack is the only pragma EmbCC acts on, in either the directive
or the _Pragma form. Every other pragma is ignored without a
diagnostic, including #pragma once; see
Known defects. The C23 unreachable() macro is not
defined by <stddef.h>. __STDC_VERSION__ is 201710L whatever
-std= says.
Attributes#
These attributes would change the generated code in a way EmbCC does not implement, so they are errors:
| Attribute | Diagnostic |
|---|---|
naked (x86-64 and Apple arm64; supported on every other target) |
__attribute__((naked)) is not supported: on this target the body could only be assembled by the file-scope assembler's few instructions; it is supported on every target but x86-64 and AArch64 for Darwin or Windows |
interrupt (except on Cortex-M, ARMv7-A, AVR, RISC-V, MIPS, RX, ColdFire and TriCore) |
__attribute__((interrupt)) is not supported: the handler would return with an ordinary return instead of the interrupt return the CPU needs, and without saving the registers (on ARMv7-M it needs neither, and is accepted; on AVR, RISC-V, MIPS, RX, ColdFire, TriCore and ARMv7-A it is implemented) |
interrupt("user") (RISC-V) |
__attribute__((interrupt("user"))) is not supported: user-mode interrupts (the N extension and its uret) were never ratified and are gone from the privileged spec, and GCC and clang no longer accept them |
keep_interrupts_masked (except on MIPS32 and MIPS64) |
__attribute__((keep_interrupts_masked)) is not supported: it modifies a MIPS interrupt handler, and only the MIPS targets implement those |
use_shadow_register_set |
__attribute__((use_shadow_register_set)) is not supported: EmbCC does not switch register sets: the handler would save into and run on a shadow set's stack pointer it never read with rdpgpr |
use_debug_exception_return |
__attribute__((use_debug_exception_return)) is not supported: the handler would return with eret where the debug exception needs deret, and save DEPC as EPC |
interrupt, signal in C++ (except interrupt on Cortex-M) |
__attribute__((interrupt)) is not supported in C++ yet: the handler would return with an ordinary return instead of the interrupt return, without saving the registers; write it in C |
signal (except on AVR) |
__attribute__((signal)) is not supported: an interrupt handler needs the machine's own return instruction and every register saved, which only the AVR backend does |
cleanup |
__attribute__((cleanup)) is not supported: the cleanup function would never run |
ms_abi |
__attribute__((ms_abi)) is not supported: the arguments would be passed in System V's registers |
sysv_abi |
__attribute__((sysv_abi)) is not supported: the arguments would be passed in the other convention's registers |
mode |
__attribute__((mode)) is not supported: the declaration would keep its written type, so a typedef that asks for a specific width would silently get another |
transparent_union |
__attribute__((transparent_union)) is not supported: the union would be passed as a union rather than as its first member, which is a different calling convention |
target |
__attribute__((target)) is not supported: EmbCC selects its instruction set per compilation; a function asking for another would be compiled for the wrong one |
weakref |
__attribute__((weakref)) is not supported: the symbol would be emitted as an ordinary reference, so a missing target would fail to link instead of being null |
ifunc |
__attribute__((ifunc)) is not supported: the resolver would never run and calls would go to it rather than to the implementation it picks |
aligned on an enum |
an aligned enum is not supported (an enum here is int, or the type its values need) |
section on an automatic variable |
section attribute on 'x', which is on the stack: only a static local can be placed in a section (GCC refuses it too) |
error and warning are accepted with a warning that the check they
request will not happen:
warning: __attribute__((error)) is accepted but does nothing here: it makes a CALL to this function a compile error unless the optimizer removes the call, so the diagnostic has to wait until after optimisation and EmbCC issues its own before then; a build-time assertion written with it will not fire [--warn=attributes]
An attribute EmbCC does not know is ignored with a warning:
warning: attribute 'no_such_attribute' is not one EmbCC knows, and is ignored [--warn=attributes]
A further set of attributes (hot, cold, pure, const, nonnull,
flatten, optimize, returns_twice and others) is accepted and has
no effect, because what it asks for is either already true or used only
by an analysis EmbCC does not have. The source table in
src/parse/parse.c gives the reason for each. For returns_twice the
reason is compatibility with newlib's <setjmp.h>; the source notes
that ignoring it can be wrong above -O0, where a value kept in a
register across the call may not survive the second return.
Builtins#
A builtin EmbCC does not provide is an undeclared function:
error: '__builtin_cpu_supports' is not declared in 'f' — for a call, add a prototype or define it first [E0001]
__has_builtin answers 0 for these. Among the GCC and Clang builtins
that are missing:
__builtin_va_arg_pack.- Vector builtins:
__builtin_elementwise_*,__builtin_reduce_*,__builtin_nontemporal_load(__builtin_shufflevectoris provided). - Target builtins:
__builtin_cpu_supports,__builtin_cpu_init,__builtin_ia32_*; on AVR__builtin_avr_flash_segmentand the fixed-point ones.
Code generation#
| Construct | Diagnostic |
|---|
C++#
C++ is compiled by lowering it to C (D-013). Its status feature by feature is in C++ support. By target:
| Target | C++ |
|---|---|
| x86-64 and AArch64 ELF | Supported, with exceptions, RTTI and libcxx.a. |
| Apple arm64 | Supported, with exceptions, against the system's C++ runtime. |
| macOS x86-64 | An object that uses exceptions does not link: Apple's linker reports ld: fixup error (kind=x86_64_rip) ... target '___cxa_allocate_exception' does not have address. Compile with -fno-exceptions. |
| Windows x86-64 | Only with -fno-exceptions -fno-unwind-tables. Otherwise every unit is refused: C++ exceptions are not supported for a Windows target yet: the unwind tables go in .pdata and .xdata and neither is written |
| 32-bit ARM (Cortex-M, ARM state), RV32 | Supported with -fno-exceptions, with or without RTTI: the data model is the target's, the Itanium ABI's 32-bit form with the ARM C++ ABI's changes on ARM, checked against clang++ by tests/golden/cxx-abi-ilp32.sh and run on six boards by tests/golden/cxx-embedded.sh; runtime from make libcxx-embedded. Exceptions are refused: C++ exceptions are not supported for thumbv7m-none-eabi yet: EmbCC writes no ARM EHABI unwind tables (.ARM.exidx); compile with -fno-exceptions. |
MIPS (mipsel-none-elf, mips-none-elf, mips64-none-elf), PowerPC, SPARC, ColdFire, Xtensa, TriCore, RX |
Supported with -fno-exceptions, as on 32-bit ARM and RV32; runtime from make libcxx-embedded. |
| AVR | Not supported: C++ is not yet supported for avr: the C++ lowering assumes a 32-bit int and 4-byte pointers (integral promotions, member pointers, RTTI), and this target's are 16 bits. -fsyntax-only, -E and -c -M still run. |
RV64, mips64el-none-elf, LoongArch64 |
Not supported. Code is generated with -fno-exceptions, but no libcxx.a is built, and on RV64 a function that needs a landing pad is refused: the RV64 backend cannot lower this operation yet (function F) [landing w=8 size=4]. |
Language refusals:
| Construct | Diagnostic |
|---|---|
| Modules | modules are not supported |
asm goto |
asm goto in C++ is not supported yet |
File-scope asm |
file-scope asm in C++ is not supported yet |
&&label and computed goto |
label addresses are not supported in C++ |
_Atomic |
_Atomic is not supported in C++ |
An initializer on an array new |
an initializer for new[] is not supported yet |
| A structured binding at namespace scope | a structured binding at namespace scope is not supported yet |
| Explicit instantiation of a variable template | explicit instantiation of a variable template is not supported yet |
#pragma pack |
#pragma pack is not supported in C++: it would change the layout and EmbCC would ignore it. Use __attribute__((packed)) on the struct |
auto(x) (C++23) |
expected an expression before 'auto' |
Multidimensional operator[] (C++23) |
'M' has no viable operator[] |
Two-phase name lookup is not implemented: a template is parsed again at each instantiation, so a non-dependent name is looked up at the point of instantiation. Several ill-formed constructs are accepted without a diagnostic. Both are described in C++ support.
ABI limitations#
Windows#
Every compile for x86_64-windows-gnu (or its alias
x86_64-w64-mingw32) prints this warning. It is on by default and
-Wno-windows-abi silences it:
embcc: f.c: warning: x86_64-windows-gnu is not yet the Microsoft x64 ABI: `long` is 8 bytes and wchar_t 4 (Windows has 4 and 2), and rsi, rdi, xmm6 and xmm7 are not preserved across a call: objects EmbCC compiles agree with each other and with nothing else [--warn=windows-abi]
Argument registers, shadow space, by-reference passing of aggregates and
the Microsoft va_list are implemented; see
Windows. No program built for this
target has run on Windows, and EmbCC provides no C library for it.
These are refused:
| Construct | Diagnostic |
|---|---|
long double in a function's signature |
long double in the signature of 'f' is not supported for a Windows target yet: there it travels by reference and returns through a hidden pointer, and EmbCC passes it on the stack by value |
__int128 in a function's signature |
__int128 in the signature of 'f' is not supported for a Windows target yet: EmbCC lowers its arithmetic to libgcc helpers whose arguments it places in the System V registers |
va_arg of a structure |
va_arg of a struct is not supported for a Windows target yet |
va_arg of long double or __int128 |
va_arg of long double is not supported for a Windows target yet: there it travels by reference |
-g |
-g is not supported for a Windows target yet: its debug information goes in CodeView records this does not write, and emitting DWARF under a COFF name would be worse than refusing |
__thread |
__thread is not supported for a Windows target yet: Windows reaches a thread-local through a _tls_index and a TLS directory this writer does not emit |
constructor, destructor |
__attribute__((constructor)) is not supported for a Windows target yet: it needs the .ctors/.dtors sections this COFF writer does not emit |
File-scope asm with labels or symbol references |
a file-scope asm block with labels or symbol references is not supported for a Windows target yet |
alias |
alias attribute on 'h' is not supported for COFF output |
section on a function |
a function's section attribute is not supported for COFF output |
section on a variable |
'v': a variable's section attribute is not supported for COFF output |
Darwin#
Both Darwin triples produce Mach-O objects for the system linker.
aarch64-apple-darwin follows Apple's arm64 convention and data model,
and x86_64-apple-darwin follows System V; see
Apple arm64. These are refused:
| Construct | Diagnostic |
|---|---|
-g |
Warning, and the object is compiled without debug information: -g: no debug information for aarch64-apple-darwin yet; d.c is compiled without it |
__thread |
__thread is not supported for a Darwin target yet: Mach-O addresses a thread-local through a __thread_vars descriptor, which this writer does not emit |
constructor, destructor |
__attribute__((constructor)) is not supported for a Darwin target yet: it needs a __DATA,__mod_init_func section this Mach-O writer does not emit |
File-scope asm with labels or symbol references |
a file-scope asm block with labels or symbol references is not supported for a Darwin target yet: its bytes would be emitted but its symbols and relocations dropped |
alias |
alias attribute on 'h' is not supported for Mach-O output |
section on a function |
a function's section attribute is not supported for Mach-O output |
EmbCC compiles against its own C headers on Darwin too. They declare
stdin, stdout and stderr as objects of those names and make
errno a call to __errno_location; the macOS C library defines none
of these, so a program that uses them does not link with the system
library. The macOS SDK headers do not compile with EmbCC: sys/cdefs.h
stops with #error Unsupported architecture, because EmbCC does not
predefine __arm64__. Calls to C library functions declared in EmbCC's
headers, such as printf, link and run.
Embedded targets#
- Cortex-M objects carry
.ARM.attributes, andembldrefuses to link objects whose float ABI (Tag_ABI_VFP_args) or enumeration size (Tag_ABI_enum_size) disagree. An enumeration isint-sized unless one of its values does not fit inint, as with Clang, or the smallest type that holds its values under-fshort-enumsor__attribute__((packed)). EmbCC's libc and librt say nothing about enum size (-fenum-size-neutral) and link with both. - On AVR, structures are passed and returned by avr-gcc's documented rules. Clang's AVR target passes structure arguments differently.
- MIPS32 objects carry
.MIPS.abiflags(soft float), andembldrefuses to link objects whose floating-point ABI disagrees. EmbCC's objects and clang's (--target=mipsel-unknown-elf -msoft-float) call each other correctly in both directions (tests/golden/mips-abi.sh), including clang's_Complexresults inv0..a1; clang's PIC and small-data objects (-fPIC,-mno-abicalls -G8) are refused byembldby relocation name. - C++ on the 32-bit embedded targets follows the Itanium C++ ABI's
32-bit form, with the ARM C++ ABI's changes on ARM; C++ is refused on
AVR, whose
intand pointers are 16 bits (see C++).
Options#
The options below are refused, each with the message shown. The full list, with the options that are accepted and have no effect, is in Invoking EmbCC.
| Option | Diagnostic |
|---|---|
-fPIC, -fpic, -fPIE, -fpie, -flto, -fprofile-*, --coverage, -pg, -fstack-clash-protection, -fcf-protection |
embcc: error: -fPIC is not supported; EmbCC would emit ordinary code and the flag's promise would not hold (naming the option) |
-shared, -static-pie |
embcc: error: -shared needs position-independent code, which EmbCC does not emit |
-fsanitize= with a check other than undefined, signed-integer-overflow, integer-divide-by-zero, shift, shift-exponent |
-fsanitize=address is not supported: EmbCC's sanitizer inserts checks that TRAP, and this one needs a runtime library to report through. The ones it has are undefined, signed-integer-overflow, integer-divide-by-zero and shift |
-fstack-protector, -fstack-protector-strong, ... |
embcc: '-fstack-protector' is not supported (EmbCC emits no stack protection); -fno-stack-protector is |
-gdwarf-5, -gsplit-dwarf, -gz |
embcc: error: -gdwarf-5 is not supported; EmbCC emits DWARF 4, uncompressed and in one piece |
-O4 and up |
embcc: unknown optimization flag '-O4' |
-mno-unaligned-access (ARM) |
-mno-unaligned-access is not supported: EmbCC's ARMv7-M code uses word and halfword loads and stores at unaligned addresses (packed struct members; copies and by-value passing of structs aligned below 4), which the architecture allows and this flag forbids |
-mabi= other than aapcs, aapcs-linux (ARM) or ilp32/lp64 (RISC-V), -mbig-endian |
-mabi=apcs-gnu is not supported: EmbCC emits the AAPCS ...; embcc: error: -mbig-endian is not supported: every target EmbCC emits for is little-endian |
-fdump-*, -fcallgraph-info |
embcc: error: -fdump-rtl-expand is not supported: it dumps GCC's internal representation, which EmbCC does not have; ... |
-fcommon (Mach-O, COFF), -fsingle-precision-constant (C++) |
embcc: error: -fcommon is not supported for x86_64-apple-darwin: EmbCC writes COMMON symbols into ELF objects only, ... |
These are unknown arguments (embcc: error: unknown argument '-march=native',
followed by the usage summary): -march=, -mtune=, -m32, -m64,
-mabi= on x86-64, AArch64 and AVR, -mavx2 and the other x86
feature flags; -imacros, -iquote, -idirafter, -undef; -ansi;
-ffp-contract=, -funroll-loops, -ftrapv, -fvisibility=,
-fopenmp; -v. The flags GCC builds for Cortex-M pass (-ffast-math,
-fcommon, -fno-jump-tables, -save-temps, -specs=, -mabi=aapcs
and the rest) are accepted, implemented or refused as
Invoking EmbCC lists.
-Wl,OPTION and -Xlinker OPTION reach the driver's link. EmbLD's own
options (-T, -L, -u, -e, -Ttext, -Tdata, -Tstack,
--rom-limit, --lma-offset, --orphan-handling, --gc-sections,
--print-gc-sections, -Map, --print-memory-usage) are applied, a
set that changes nothing about the image (-s, -z now and others) is
accepted, and any other is refused before anything is linked:
embcc: error: linker option '-T' is not one EmbLD has (it takes -e, -Ttext, -Tdata, -Tstack, --rom-limit and --lma-offset); dropping it could build a different image from the one asked for
-Wa,OPTION accepts --noexecstack, -g, --gdwarf* and -mrelax
and refuses any other
(embcc: error: assembler option '-al' is not one the integrated assembler has).
The full rules are in
Invoking EmbCC.
-pedantic and -pedantic-errors report what the -std= standard does
not have, for C and C++ (Invoking EmbCC).
Any -W name that is not one of EmbCC's warnings
(--help-warnings lists them) is accepted with:
embcc: warning: -Wcast-align is not a warning EmbCC has, so it turns nothing on (--help-warnings lists them)
-std= selects the language, C89 to C2y and C++98 to C++26, as GCC
does (Invoking EmbCC).
Known defects#
These are cases where EmbCC accepts something and does not do what was asked, without an error. Each is a defect, not intended behavior.
- Pragmas.
pack,once,push_macro,pop_macroandweakhave an effect. Every other pragma is dropped without a diagnostic, and there is no-Wunknown-pragmas. In particular,#pragma GCC diagnosticdoes not change any warning,#pragma GCC poisonpoisons nothing, and#pragma redefine_extnamedoes not rename the symbol. - Debug information. On AVR,
-gproduces a compile unit with no functions, variables or line-table rows. Enumerations,typedefnames and lexical blocks are not described on any target. The full list is in Debugging.
Optimizer gaps#
The optimizer is described in The optimizer and the user-visible levels in Optimization. These are the transformations GCC and Clang make that EmbCC does not, as the source states them:
- No machine-level IR. Instruction selection writes machine code
directly from EmbIR (
embcc inspect mirsays so). There is no instruction scheduling and no machine-level optimization pass. - SSA only inside one pass. EmbIR is linear three-address code.
mem2regbuilds SSA form and immediately leaves it again, turning each phi into copies; every other pass works on the non-SSA form and recomputes the control-flow and data-flow facts it needs. - Alias analysis by base object only. Two references based on
different objects do not alias, and an unknown pointer cannot reach a
stack slot whose address is never taken. There is no type-based
aliasing (so
-fstrict-aliasingchanges nothing), no use ofrestrict, and no field sensitivity. - Interprocedural optimization is limited to one translation unit:
inlining, inference of which functions read or write memory a caller
can see, and replacing loads from a
staticglobal that nothing in the unit writes with its value. There is no link-time optimization, and no call is ever eliminated: two calls to aconstorpurefunction stay two calls. - The inliner declines a callee that is not defined in the unit, is
variadic, recursive,
noinlineor too large for the budget, has exception regions, returns a structure or a value wider than a register, takes a parameter that is not a simple scalar, contains inlineasm,va_start, a variable-length array or a computed goto, computes in__int128, or calls a function that returns a structure.-fremarksnames the reason at each call (remark: not-inlined 'g' [inline/callee-is-varargs]). - Vectorization is done on x86-64 only, and not at
-Os. A floating-point sum reduction is not vectorized, because that would change the order of the additions; there is no option that permits it. - Division by a constant becomes a multiply only on 64-bit targets. On Cortex-M the hardware divide is used; on AVR it is a library call.
- No profile-guided optimization:
-fprofile-*is refused, andhotandcolddo not affect code placement. - No fast-math mode. Floating-point arithmetic is not reassociated or contracted into fused multiply-adds.
- Functions with exception regions or computed
goto. The passes that need a complete control-flow graph are skipped in a function with live exception regions, and the passes that place code on an edge are skipped in a function withgoto *. On x86-64 and AArch64 such a function keeps every value in memory; the other backends allocate registers in it, the allocator givinggoto *pan edge to every label whose address is taken. -O3is-O2.-Ogis-O1with every source variable kept in its stack slot.
Milestones#
The project's original milestones, and their state:
Roadmap#
The releases up to 1.0.0, and what each must achieve, are in the roadmap. These are the open items in the code that it schedules, each checked against the compiler when this page was last audited:
- Position-independent code, shared libraries and dynamic linking.
embld: linking AArch64 objects, and GNU linker scripts on the targets other than ARM, RISC-V, AVR and MIPS32.- Debug information, thread-local storage and constructors in Mach-O
and COFF objects; the Microsoft x64 data model and callee-saved
registers;
.pdata/.xdataunwind tables for Windows. - GNU-syntax assembly files for x86-64; on AArch64, an assembler for the whole instruction set (its vocabulary is the kernel's), and blocks in Mach-O objects.
- Unwind tables, and so C++ exceptions, on every target but x86-64 and AArch64.
__attribute__((interrupt))on ARMv7-A, MIPS64, LoongArch64, Xtensa, TriCore, PowerPC, RX, SPARC and ColdFire; GCC's MIPSuse_shadow_register_setanduse_debug_exception_return.- A
_Float16through..., and a struct of them by value with the ARM or RISC-V hard-float convention;asm goto, nested functions, and sixteen-byte atomics off x86-64 and AArch64 (see What each target compiles). - On MIPS32, a delay slot filled from anywhere but the instruction just
before the transfer (the branch target's first instruction, or one
from before the
slta compare-and-branch needs). - Linker relaxation: on RISC-V a call between sections stays an
eight-byte
auipc/jalr. - A machine-level IR below EmbIR; SSA as the form of EmbIR between
passes; alias information on memory references (types,
restrict, fields). - Vectorization on targets other than x86-64.
- Reading the vDSO on Linux.
- The C++ items marked "not supported yet" above and in C++ support.
- M4 on EmbLinkOS.
Reporting a bug#
Is it a bug?#
- An error that says something "is not supported" is a limitation listed on this page, not a bug.
- These are bugs: an
internal error(EmbCC addsnote: this is a bug in EmbCC, not in the program being compiled), a crash, a program that compiles and computes a wrong result, an object that another toolchain's assembler, linker or debugger rejects, a construct that is accepted and silently ignored or changed, and a disagreement with GCC or Clang about a calling convention or a data layout that the target's ABI defines.
Narrowing it down#
- Compile at
-O0,-O1and-O2. A failure only at-O2points at the optimizer or the register allocator. - Turn off one pass at a time with
-fno-NAME(for example-fno-gcse); the names are in Optimization. - Set
EMBCC_VERIFY=1. The optimizer then checks the IR before and after it runs and stops at the first inconsistency. embcc inspect ir -O2 FILEprints the IR the backend receives, and-fremarksreports what the optimizer did and declined to do.- Reduce the program to the smallest file that still fails.
What to include#
- The first line of
embcc --version, and the host system. - The complete command line, including
--target=, or the output of-dumpmachinewith the same options. - The source, preprocessed with the same options
(
embcc -E OPTIONS FILE.c > FILE.i), or the reduced file. - The complete output of the compiler.
- What you expected and what happened. For a calling-convention or layout problem, the other compiler, its version and its command line.
Report it as an issue in the EmbCC repository,
https://github.com/EmbLinkOs/EmbCC/issues.
A fix comes with a test that fails without it; see
Testing. A construct that
EmbCC should refuse is added to tests/compile/reject-unimplemented.sh
(A refusal test).