EmbCCdocs Repository

Language extensions#

This page describes the extensions to ISO C that EmbCC accepts: the GNU C language extensions, attributes, builtin functions, the atomic builtins, the feature-test operators, preprocessor extensions and pragmas. For each one it says what EmbCC does, on which targets, and the exact diagnostic when EmbCC refuses it. It is for people porting code written for GCC or Clang and for people writing code for EmbCC directly. Standard C is described in C language, C++ in C++, and the asm statement in Inline assembly.

Overview#

Extensions are always enabled#

Every extension on this page is available under every --std= standard, as in GCC: under a strict cNN standard only the keywords asm and typeof are names, and __asm__ and __typeof__ still work. --pedantic reports each use of an extension, and --pedantic-errors refuses it, except in a system header or after __extension__.

When EmbCC does not implement an extension, it refuses the program with an error that names the construct, rather than compiling it to do something else. The few places where a construct is accepted and has no effect, without an error, are stated where they occur and collected in Accepted without effect.

Target names used on this page#

Where support depends on the code generator, this page names targets as follows. Each name covers every triple of that family; see Targets.

Name Triples
x86-64 x86_64-elf, x86_64-emblink, x86_64-linux-gnu, x86_64-apple-darwin, x86_64-windows-gnu
AArch64 aarch64-elf, aarch64-emblink, aarch64-linux-gnu, aarch64-apple-darwin
Cortex-M thumbv7m-none-eabi, thumbv7em-none-eabi, thumbv7em-none-eabihf, thumbv8m.main-none-eabi, thumbv8m.main-none-eabihf
RV32, RV64 riscv32-unknown-elf, riscv64-unknown-elf
AVR avr

A construct that a code generator cannot lower is refused when the function containing it is compiled, with a message of the form

embcc: f.c:1: error: the MIPS64 backend cannot lower a 16-byte atomic (MIPS64's lld/scd are a doubleword; there is no 128-bit ll/sc) yet (function f) [cas16 w=16 size=16]

The bracketed part names the internal operation that could not be lowered.

Detecting EmbCC#

EmbCC predefines __EMBCC__ as 1. A C translation unit does not define __GNUC__, __GNUC_MINOR__ or __clang__, so a header that enables GNU features under #ifdef __GNUC__ takes its other path when compiled by EmbCC, unless -fgnuc-version= asks for them (CMSIS needs that; see Invoking). A C++ translation unit presents itself as g++ 16.2; see C++.

To test for a feature, use the feature-test operators, with the limits described there.

GNU C language extensions#

Summary#

Extension Status
Statement expressions ({ ... }) Supported
typeof, __typeof__, __typeof, typeof_unqual Supported for a type name and for most expressions
__auto_type Supported at block scope
Labels as values and computed goto Supported, in static tables too
Local labels (__label__) Supported
Case ranges (case 1 ... 5:) Supported
Designated range initializers ([2 ... 5] = x) Supported
Zero-length arrays Supported
Static initialization of a flexible array member Supported
__int128, __real__, __imag__ and other types See the section
Alternate keywords (__asm__, __inline__, ...) Supported
__alignof__ of an expression Supported
\e escape (ESC, 27) Supported
Binary integer constants (0b1011) Supported
__FUNCTION__, __PRETTY_FUNCTION__ Supported; both are the function's name, as __func__ is
Anonymous structures and unions as members Supported
Conditional with the middle operand omitted, x ?: y Supported; x is evaluated once
A structure or union with no members Supported; its size is 0
An extra ; at file scope (}; after a function) Supported; an empty declaration
A ?: with one void arm (c ? f() : 1) Supported; the result is void
A forward reference to an enum (enum e *p; before its body) Supported
An object of type void, extern void _text;, taken the address of Supported, with GCC's warning on &_text unless it is const
An asm label, int f(void) __asm__("name"); Supported
Arithmetic on void *, casts to a union and other small forms Supported
Vector types, __attribute__((vector_size(N))) Supported, lane by lane on every target
Constructs EmbCC does not support Nested functions, a conditional or cast as an lvalue

Statement expressions#

A compound statement in parentheses is an expression. Its value is the value of its last statement, when that statement is an expression statement; otherwise its type is void. The value may be of any type, including a structure.

#define max(a, b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); \
                     _a > _b ? _a : _b; })

Statement expressions are supported on every target. One cannot appear in a static initializer (a static initializer must be a constant, a string literal, or the address of a global). break and continue inside one refer to a loop or switch inside the same statement expression; one that would leave it is refused ('break' outside of a loop or switch).

typeof#

typeof, __typeof__ and __typeof are one keyword. Each accepts a type name or an expression; the expression is not evaluated. C23's typeof_unqual gives the same type without its qualifiers.

The type of an expression is determined while the declaration is parsed, which covers the forms that macros use. The operand may be:

  • an integer, floating or character constant, or a string literal without an encoding prefix other than u8;
  • the name of an object: a variable in scope or one at file scope;
  • a cast, a conditional (?:), unary -, ~ and !, *, &, array subscripting, . and ->;
  • a binary arithmetic, shift, comparison or logical operator, including pointer arithmetic and the difference of two pointers;
  • a call to a function by name, declared earlier, or through a function pointer.

Any other expression is refused:

embcc: t.c:1: error: typeof of an unsupported expression

This includes the name of a function or its address (typeof(f), typeof(&f)), an enumeration constant, an assignment, ++ and --, the comma operator, sizeof, _Generic, a compound literal, a statement expression, and a string literal with an L, u or U prefix. For a function, write its type as a type name.

__auto_type#

__auto_type name = initializer; declares name with the type of its initializer, after an array is converted to a pointer. The type is determined as for typeof, so the same expressions are accepted:

embcc: a.c:1: error: __auto_type cannot see the type of this initializer

The declaration must have exactly one plain identifier and an initializer (__auto_type needs a plain variable name, __auto_type needs an initializer: there is nothing else to take the type from). It is accepted at block scope only; at file scope it is a syntax error:

embcc: a.c:1:1: error: expected a type before ?

C23's auto x = expression; means the same thing, and is also accepted at block scope only.

Labels as values and computed goto#

&&label is the address of a label in the current function, of type void *, and goto *expression; jumps to such an address.

int dispatch(int op)
{
    void *table[] = { &&add, &&sub };
    goto *table[op];
add:
    return 1;
sub:
    return 2;
}

This is supported on every target. A label's address is a code address, the same kind of value a function pointer holds: on Cortex-M it has bit 0 set (Thumb state, ready for bx), and on AVR it is a word address in program memory, as gs() makes it.

A label address may also initialize an object with static storage duration inside its function, which is how a threaded interpreter keeps its dispatch table, and the difference of two label addresses is a constant there, as in GCC:

int step(int op)
{
    static void *const table[] = { &&add, &&sub };
    static const int off[] = { &&add - &&add, &&sub - &&add };
    if (op < 0)
        goto *(&&add + off[-op - 1]);
    goto *table[op];
add:
    return 1;
sub:
    return 2;
}

Arithmetic on a label address -- &&b - &&a, &&a + n -- is in bytes, as on a char * (on AVR, in words, since the address is one), the way arithmetic on a void * is (see Smaller GNU C forms). A label address cannot initialize an object outside its function: at file scope it is refused with a static initializer must be a constant, a string literal, or the address of a global. A label whose address is taken must be defined (label 'x' used but not defined). A function that contains a computed goto is never inlined; see Inlining. C++ refuses both forms (see C++).

Local labels#

__label__ name; at the start of a block, or anywhere a statement may start, declares name as a label local to that block. A macro that contains a label can then be expanded more than once in a function.

#define RETRY(stmt) do { __label__ again; again: if (!(stmt)) goto again; } while (0)

Several names may be declared at once (__label__ a, b;).

Case ranges#

case low ... high: matches every value from low to high inclusive. Write spaces around ...: case 1...5: is read as a number and refused (malformed floating constant).

switch (c) {
case '0' ... '9': return DIGIT;
case 'a' ... 'z': return LOWER;
}

A range whose bounds are reversed is an error (case range 5 ... 1 runs backwards), and so is a value that two labels cover (duplicate case label 3).

Designated range initializers#

In an array initializer, [first ... last] = value initializes every element from first to last:

int widths[10] = { [2 ... 5] = 8, [7] = 16 };

GCC's older spellings of a designator are accepted too, with a --pedantic warning: { x: 1 } for { .x = 1 }, and [3] 7, without the =, for [3] = 7.

Zero-length and flexible arrays#

A structure member may be declared as an array of length zero, the pre-C99 form of a flexible array member. It contributes no size.

An object may be an array of length zero too: int z[0];, or int z[] = {};, whose empty initializer gives it no elements. Its size is 0, and it still has an address: a local takes one byte of the frame and a global one byte of its section, so two of them never compare equal. int z[]; with no initializer is still an incomplete type. --pedantic warns about each (an array of size 0 is a GNU extension, not ISO C).

A structure with a flexible array member may be initialized statically, including its trailing array:

struct msg { int len; char data[]; };
struct msg hello = { 5, { 'h', 'e', 'l', 'l', 'o' } };

Additional types#

Type Status
__int128, unsigned __int128, __int128_t, __uint128_t x86-64 and AArch64. On RV64 the type exists but operations on it are refused (the RV64 backend cannot lower a 128-bit value yet ...). On Cortex-M, RV32 and AVR: __int128 does not exist on this target (it needs 64-bit registers; use long long)
_Complex and __complex__, __real__, __imag__ Supported for float, double and long double on every target
Imaginary constants with an i or j suffix (2.0i, 3.0fi, 1.0j, 2i) Supported; an integer one is a _Complex int
Integer complex types (_Complex int) Supported; see Complex types
_Float32, _Float64 The same types as float and double
_Float128, __float128 AArch64 ELF and Linux targets, where long double is IEEE binary128. Refused on x86-64 (_Float128 is not supported on x86-64: ...) and on Apple arm64, Cortex-M and AVR (_Float128 is not supported on this target: it has no 128-bit floating-point type); on RISC-V its operations are refused like those of long double
_Float16, __fp16 Supported on every target; see Half-precision floating point. _Float16 through ... is refused
__bf16 (bfloat16), and the bf16 constant suffix Supported on every target, where GCC has it on x86-64, AArch64 and ARM; see Half-precision floating point
_Float32x, _Decimal32 and the other interchange and decimal types Not supported (expected a type before '_Float32x')

The size, alignment and format of each type per target are in Targets.

Alternate keywords#

These spellings are accepted wherever the standard keyword is, so that headers written for -ansi work:

Spelling Means
__asm__, __asm asm
__inline__, __inline inline
__volatile__, __volatile volatile
__restrict__, __restrict restrict
__typeof__, __typeof typeof
__alignof__, __alignof _Alignof; the operand may also be an expression
__complex__, __complex _Complex
__real, __imag __real__, __imag__
__attribute __attribute__
__thread _Thread_local
__signed__, __signed signed
__const__, __const const
__extension__ Nothing; it is predefined as an empty macro

Smaller GNU C forms#

Form Meaning
Arithmetic on a void * or a function pointer (p + 1, p++, p - q) In bytes, as on a char *
sizeof(void); sizeof of a function type, of a function's name, or of a void expression 1
return f(); in a void function, where f returns void f is called, and nothing is returned
$ in an identifier Part of the name, at its start, middle or end
A cast to a union type, (union u)x The union u with x in its first member of x's type. The result is a value, not an lvalue. An x whose type no member has is refused: cannot cast double to union u: no member has that type
A cast of a structure to its own type, (struct s)x x's value

--pedantic warns about each of these (... is a GNU extension, not ISO C), except in system headers and after __extension__; --pedantic-errors refuses them. $ gets the warning under --std=c89 and gnu89 only, as in GCC: C99 allows an implementation to accept other characters in identifiers.

GNU extensions that are not supported#

Each of these is refused with the diagnostic shown.

Construct Diagnostic
Nested function definitions expected ';' before '{' [E0002], at the inner function's body
A conditional or cast as an lvalue assignment target must be a variable, *pointer, or member

Attributes#

Syntax#

EmbCC reads attributes in two spellings:

  • GNU: __attribute__((name)) or __attribute__((name(arguments))), with several attributes separated by commas;
  • C23: [[name]], [[gnu::name]] or [[__gnu__::name]]. The standard names [[maybe_unused]], [[nodiscard]] and [[_Noreturn]] mean unused, warn_unused_result and noreturn; the others coincide with their GNU names. The scope is dropped whatever it is, so [[clang::packed]] also means packed.

Each name may also be written with two leading and two trailing underscores (__packed__). The two spellings share one parser, so an attribute means the same in both.

An attribute may appear:

  • at the start of a declaration, or among its specifiers, after a storage class or a qualifier (static const __attribute__((aligned(4))) char t[4];), at any scope, and so in a structure member's or a parameter's specifiers;
  • after a declarator, including after a function's parameter list;
  • after struct, union or enum, before the tag, or after the closing brace of the definition (not between the tag and {);
  • at the start of a structure member's declaration, or after the member's declarator;
  • at the start of a parameter's declaration, before its type;
  • after a * in a declarator, where weak, noreturn, format, packed, aligned, section and pcs apply to the declaration;
  • as a statement on its own (__attribute__((fallthrough));, [[fallthrough]];), and after a label.

These positions are not accepted:

  • after a parameter's name, int f(int x __attribute__((unused))): expected ')' before '__attribute__';
  • a C23 attribute after a declarator's name, int x [[gnu::aligned(16)]];: expected an expression, got '[';
  • on an enumerator: expected '}' before '__attribute__';
  • after the * of a structure member's declarator, for an attribute that changes layout or linkage: __attribute__((weak)) is not supported in this position (after a declarator it is; on a struct or union, put it right after the keyword or after the closing '}');
  • in the type name of a cast or sizeof, for an attribute that changes layout or linkage, which has no declaration there to apply to: __attribute__((aligned)) is not supported in a type name: there is no declaration here to carry it.

How EmbCC treats an attribute#

Every attribute name EmbCC knows has one of four dispositions:

  • Honored: EmbCC acts on it, as described below.
  • Accepted with no effect: it asks for something that is already true of EmbCC's output, or that only matters to an analysis EmbCC does not perform. It is accepted without a diagnostic.
  • Accepted with a warning: error and warning. Ignoring them is not a miscompilation, but the check they ask for does not happen, so EmbCC says so under --warn=attributes.
  • Refused: ignoring it would generate different code from what the program asks for, so it is an error that names the attribute.

An attribute EmbCC does not know is ignored with a warning, which --no-warn=attributes silences:

embcc: attr.c:1: warning: attribute 'frobnicate' is not one EmbCC knows, and is ignored [--warn=attributes]

Function attributes#

Attribute Effect
aligned(N), aligned The function's first instruction is at an address that is a multiple of N (16 with no argument). It counts on any declaration, and the strictest wins. __alignof__ of the function gives N, or without the attribute the instruction set's own boundary: 1 on x86-64, AVR and RX, 2 on Thumb, ColdFire, TriCore and RISC-V with compressed instructions, and 4 elsewhere, as GCC answers
alias("target") The function or variable is another name for target, which must be a function, or a variable, defined in the same file and not itself an alias. The declaration with alias cannot have a body or an initializer, and a variable alias has no storage of its own: inside the file it is target, to the optimizer as well. Combine with weak for a weak alias. ELF only; see Object formats
always_inline Lifts the inliner's size limit for calls to this function, at every level including --opt=none; see Inlining
constructor, destructor The function's address is placed in .init_array or .fini_array, for the startup code to call before main or at exit. On a bare-metal target the program's own startup code walks the tables; see Startup code. A static one is kept although nothing calls it. ELF only
deprecated, deprecated("message") A call, a read, or taking the address warns under --warn=deprecated-declarations, on by default. The message text is not printed
embcc_sret EmbCC's own. On the first parameter, before its type, it marks that parameter as the address of the returned aggregate, passed where the target's ABI passes it. EmbCC's C++ lowering writes it; hand-written C has no need of it
format(archetype, string-index, first-to-check) Calls are checked under --warn=format, which --warn=all enables. The archetypes checked are printf, gnu_printf, scanf and gnu_scanf; any other (strftime, strfmon) is accepted and not checked. Both indexes are 1-based; first-to-check is 0 for a function that takes a va_list
gnu_inline GNU89 inline semantics for this function: a definition that says extern inline is used only for inlining and never emitted, and one that says inline alone is an external definition. See Inline functions
noinline The function is never inlined
no_instrument_function --instrument-functions leaves the function alone. It counts on any declaration: a plain prototype followed by a definition with it works
noreturn, _Noreturn, [[noreturn]] The function does not return. A call to it ends a path for EmbCC's check that every path through a non-void function returns a value (E0008). EmbCC does not check that the function itself never returns
nothrow The function throws no C++ exception: a call to it inside a C++ try region gets no landing pad
pcs("aapcs"), pcs("aapcs-vfp") ARM only. See pcs
section("name") The function's code is placed in section name. See section
unused Suppresses --warn=unused-function for this function
used The function is emitted even if nothing in the translation unit refers to it, as for a handler reached only from a table or from assembly
visibility("default" | "hidden" | "protected" | "internal") Sets the symbol's ELF visibility. Any other value is refused: visibility attribute wants "default", "hidden", "protected" or "internal"
warn_unused_result, [[nodiscard]] Discarding the result warns under --warn=unused-result, on by default
weak On a definition, the symbol is weak and another definition overrides it at link time. On a declaration, the reference is weak: the function's address is null if no definition is linked. weak on any declaration of a function makes it weak
interrupt, signal Interrupt handlers. See Interrupt handlers
keep_interrupts_masked MIPS32 and MIPS64, with interrupt: interrupts stay disabled in the handler. See Interrupt handlers

constructor(N) and destructor(N) take a priority, as in GCC. The address goes in a section of its own, .init_array.NNNNN or .fini_array.NNNNN (.init_array.00101 for 101), and the link orders the arrays:

  • EmbLD's default layout places the numbered sections ascending, ahead of the plain .init_array, as GNU ld's default script does. A linker script orders them with KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*))) before KEEP(*(.init_array)).
  • So constructor(101) runs before constructor(200), and both before every constructor without a priority. Destructors run in the reverse order, since the exit code walks .fini_array backwards.
  • A priority from 0 to 100 is reserved for the implementation and warns under -Wprio-ctor-dtor (on by default, as in GCC); one above 65535 is refused. Objects from clang, which names the sections .init_array.101, sort with EmbCC's.

Declarations of one function that disagree are refused: two different sections ('f' is placed in section '.a' here and '.b' before) or two different pcs conventions.

Variable attributes#

Attribute Effect
aligned(N), aligned The object is aligned to at least N bytes; with no argument, to 16 bytes on every target. Applies to file-scope and local variables. See aligned
deprecated A use of the variable warns under --warn=deprecated-declarations
section("name") The object is placed in section name. File scope only. See section
unused Suppresses --warn=unused-variable
used A static variable is emitted even if nothing refers to it
visibility("...") As for functions
weak As for functions: a weak definition, or a weak reference whose address is null when nothing defines it
alias("target") As for functions: another name for target, a variable defined in this file, with no storage of its own (extern int b __attribute__((alias("a")));). ELF only

Type attributes#

Attribute On Effect
packed struct, union Every member's alignment becomes 1: no padding between members, and the structure's alignment is 1. A member's own aligned(N) still applies
aligned(N) struct, union The type's alignment is raised to at least N, and its size is rounded up to a multiple of it
aligned(N) a member The member's alignment is raised to at least N, which raises the structure's
aligned(N) typedef The type's alignment becomes N, larger or smaller than its own; its size does not change. See below
packed enum Supported: the smallest integer type that holds every value, before the body or right after it
aligned enum Refused: an aligned enum is not supported (an enum here is int, or the type its values need)
deprecated a type Accepted; using the type does not warn
may_alias a type Accepted with no effect; EmbCC performs no type-based alias analysis
mode(M) an integer type: among or before a declaration's specifiers, between them and the declarator, on a typedef, or after a file-scope variable's name The integer type of mode M's width, keeping the written signedness: QI 1 byte, HI 2, SI 4, DI 8, TI 16, byte 1, word and pointer the target's register and pointer width (glibc's <sys/types.h> writes these; typedef int __attribute__((mode(SI))) int_t;, a member E __attribute__((mode(byte))) e;, an enum). Another mode is refused: __attribute__((mode(V4SI))) is not supported: EmbCC takes the integer modes QI, HI, SI, DI, TI, byte, word and pointer. After a local's, a member's or a parameter's name it is refused: __attribute__((mode)) is not supported here
transparent_union a union A parameter of the union type takes any of its members' types, and is passed as its first member is (glibc's <sys/socket.h> under _GNU_SOURCE). Defining a function that receives one is refused off x86-64 and AArch64, where that is not yet checked: a transparent_union parameter, which EmbCC receives only on x86-64 and AArch64

packed applies to a whole structure or union. Written on a single member, before or after its declarator, it is accepted and has no effect.

An alignment on a typedef name is the type's, as in GCC and Clang. It can be larger or smaller than the type's own, and the size does not change:

  • typedef uint8_t dma_buf_t[64] __attribute__((aligned(32))); places every dma_buf_t object on a 32-byte boundary.
  • typedef uint32_t u32_una __attribute__((aligned(1))); is the idiom for an unaligned access. Through a u32_una *, EmbCC reads and writes as it does a packed structure's member: byte by byte where the core would trap on a misaligned word (Cortex-M0, SPARC, MIPS).
  • A structure member of such a type is placed by that alignment, so struct { char c; u32_una x; } is five bytes.

An array of a type whose alignment is greater than its size is refused, as GCC and Clang refuse it, since its second element could not be aligned. A function parameter of such a type is passed, and has its address, as its type would without the attribute.

A packed attribute on a typedef name, rather than on the structure it names, is refused inside a function. A typedef of a structure that carries its own aligned or packed keeps that layout.

Statement and label attributes#

fallthrough (as __attribute__((fallthrough)); or [[fallthrough]];) is accepted and has no effect; EmbCC does not warn about a case that falls through. unused after a label is accepted.

aligned#

The argument of aligned is an integer constant expression, evaluated as the operand of _Alignas is. aligned(2*32) and aligned((64)) give 64, and aligned(sizeof(long long)) gives the size of long long:

#define CACHE_LINE 64
struct ring { unsigned head, tail; } __attribute__((aligned(CACHE_LINE)));
struct line { char bytes[CACHE_LINE]; } __attribute__((aligned(2 * CACHE_LINE)));

The value must be a positive power of two. Any other value, including 0, and an argument that is not an integer constant expression are refused:

embcc: a.c:1: error: aligned wants a constant power of two

_Alignas makes the same check (_Alignas requires a constant power of two), except that _Alignas(0) is accepted and has no effect, as C11 specifies.

A local whose alignment exceeds what the stack pointer guarantees (16 bytes on x86-64, AArch64 and RISC-V, 8 on Cortex-M) is placed in storage that EmbCC aligns at function entry, so its address has the requested alignment at any call depth. That holds for a scalar as well as an array, structure or union; a scalar so aligned is read and written in that storage, as a variable whose address is taken is, rather than kept in a register.

On AVR, whose stack pointer has no alignment at all, every local with an alignment greater than 1 is placed that way.

_Alignas and #pragma pack interact with aligned as in GCC: the stricter of aligned(N) and _Alignas(N) applies, and #pragma pack(N) caps every member's alignment at N, including one raised by a member's aligned.

section#

section("name") places a file-scope variable or a function in the named section of the object file.

For a variable:

  • A name beginning .text makes the section executable, and one beginning .rodata read-only. Any other name gives a writable section.
  • .bss and names beginning .bss. give a section with no file contents (NOBITS). An object placed there cannot have an initializer: 'z' has an initializer but is placed in NOBITS section '.bss.x'.
  • A const object with a section goes to that section, not to .rodata.
  • An object without an initializer is zero-filled in its section.
  • A thread-local object cannot have a section: 'z' is __thread and also names a section: a thread-local object has to be in .tdata or .tbss, which is what makes it per-thread.

For a function, the code is placed in the named section, and the functions that share a section are emitted together. With -g, a unit whose code is in more than one section describes it with DW_AT_ranges. A function and a variable cannot share a section name (section '.shared' holds a function, and 'd' cannot share it: one is code, the other data).

section on a static local places it as on a file-scope object, as GCC does: static uint32_t boots __attribute__((section(".noinit"))); inside a reset handler goes to .noinit, under the local symbol function.boots. On an automatic variable, which lives on the stack, it is refused:

embcc: s.c:1: error: section attribute on 'x', which is on the stack: only a static local can be placed in a section

On a structure member it is accepted and has no effect.

pcs#

pcs("aapcs") and pcs("aapcs-vfp") select the ARM procedure-call standard for one function, whatever -mfloat-abi selects for the rest of the program: aapcs passes floating-point values in core registers, and aapcs-vfp in VFP registers. Runtime libraries put pcs("aapcs") on helpers that must keep the base convention under --float-abi=hard. See Calling convention: AAPCS32.

Condition Diagnostic
Not an ARM target __attribute__((pcs)) names an ARM calling convention, and this is not an ARM target
Any other value pcs wants "aapcs" or "aapcs-vfp"
aapcs-vfp without an FPU pcs("aapcs-vfp") passes floating point in VFP registers, and this part has no FPU: add --fpu=
On a variable, parameter, member or typedef pcs is only supported on a function declaration, not on a variable (or a parameter, a member, a typedef)
On a function pointer that is a structure member pcs is only supported on a function declaration, not on a pointer to one: a call through the pointer would use the default convention
Two declarations disagree 'f' is declared with a different pcs than on line 3
Taking the address of a function whose pcs differs from the build's convention taking the address of 'f' is not supported: it is declared with a pcs attribute that is not this build's convention, and a call through the pointer would use this build's

On a function-pointer parameter, pcs is accepted and has no effect.

Attributes accepted with no effect#

These are accepted without a diagnostic. The reason is the one EmbCC records for each.

Attribute Why it has no effect
access It describes pointer-argument accesses for warnings EmbCC does not issue
alloc_align, alloc_size, counted_by EmbCC has no object-size checking
artificial It marks a function for a debugger's stepping
assume_aligned (attribute) It promises a returned pointer's alignment, which only a pass that widens accesses could use
cold, hot EmbCC does not reorder code by frequency
const, pure EmbCC does not eliminate repeated calls; it infers whether a function reads or writes memory by itself
copy The attributes worth copying are recorded on the declaration itself
designated_init It asks for a warning; the layout is unaffected
externally_visible It keeps a symbol visible under whole-program optimisation, and EmbCC has none: every non-static definition is in the object's symbol table already
fallthrough EmbCC does not warn about a case falling through
flatten EmbCC's inliner works from the call site
leaf Nothing in EmbCC reasons across a call this way
malloc It says the result aliases nothing, which only an alias analysis could use
may_alias EmbCC does no type-based alias analysis
no_sanitize, no_sanitize_address, no_sanitize_undefined EmbCC has no sanitizers of these kinds
noclone EmbCC never clones a function
noipa The only interprocedural pass is the inliner, which always_inline and noinline control
nonnull, returns_nonnull EmbCC does not track or check null pointers
optimize The optimization level applies to the whole compilation, not per function
returns_twice See below
sentinel EmbCC does not check variadic terminators
tls_model EmbCC emits one thread-local model

returns_twice is accepted because newlib's <setjmp.h> puts it on setjmp, and is not acted on. Above --opt=none, a local variable that the optimizer keeps in a register across a call to such a function can hold a stale value after the second return. A volatile local is kept in memory at every optimization level, so a value stored in it between the two returns is still there after the second, as C requires; declare a local volatile when it is changed after setjmp and read after longjmp.

error("message") and warning("message") are accepted with a warning, because GCC reports a call to such a function only after optimization, and EmbCC does not:

embcc: e.c:1: 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]

A build-time assertion written with error therefore does not fail the build. Use _Static_assert.

Attributes that are refused#

Each of these is an error of the form __attribute__((NAME)) is not supported: REASON:

Attribute Reason given
cleanup the cleanup function would never run
ifunc the resolver would never run and calls would go to it rather than to the implementation it picks
interrupt On every target but Cortex-M, ARMv7-A, AVR, RISC-V, MIPS, RX, ColdFire and TriCore; see Interrupt handlers
interrupt_handler it is the m68k port's interrupt handler, implemented on the ColdFire target only (on every target but ColdFire)
interrupt_thread it makes a Fido interrupt thread, which switches register banks a ColdFire does not have; use __attribute__((interrupt))
keep_interrupts_masked it modifies a MIPS interrupt handler, and only the MIPS targets implement those (on every target but MIPS32 and MIPS64)
ms_abi the arguments would be passed in System V's registers
naked On x86-64 and Apple arm64; see Naked functions
signal On every target but AVR; see Interrupt handlers
sysv_abi the arguments would be passed in the other convention's registers
target EmbCC selects its instruction set per compilation; a function asking for another would be compiled for the wrong one
use_debug_exception_return the handler would return with eret where the debug exception needs deret, and save DEPC as EPC
use_shadow_register_set 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
weakref the symbol would be emitted as an ordinary reference, so a missing target would fail to link instead of being null

ms_abi and sysv_abi are refused on every target, including x86_64-windows-gnu.

Object formats#

Some attributes need support from the object-file writer.

Attribute ELF Mach-O COFF
section on a variable Supported The object goes in segment __DATA, in a section with the name as written. A "segment,section" name is not split 'b': a variable's section attribute is not supported for COFF output
section on a function Supported a function's section attribute is not supported for Mach-O output a function's section attribute is not supported for COFF output
alias Supported alias attribute on 'f' is not supported for Mach-O output alias attribute on 'f' is not supported for COFF output
constructor, destructor .init_array, .fini_array __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 __attribute__((constructor)) is not supported for a Windows target yet: it needs the .ctors/.dtors sections this COFF writer does not emit
weak Supported Supported Accepted with no effect
visibility Supported Accepted with no effect Accepted with no effect

Embedded programming attributes#

This section collects the attributes used in firmware. How to write a vector table, a startup routine and handlers for each board is in Embedded programming.

Interrupt handlers#

Target __attribute__((interrupt)) __attribute__((signal))
AVR Implemented. The handler saves r0, SREG, r1, the call-clobbered registers and the frame pointer, clears r1, re-enables interrupts with sei on entry, and returns with reti Implemented, as interrupt without the sei: interrupts stay disabled in the body
Cortex-M Accepted; the code is the same as without it, because the processor saves the caller-saved registers on exception entry and an ordinary return performs the exception return. An argument such as interrupt("IRQ") is accepted Refused
ARMv7-A Implemented in ARM state, with GCC's "IRQ" (the default), "FIQ", "ABORT", "SWI" and "UNDEF": the handler pushes r0-r3, r12 and lr with the callee-saved registers it uses, IRQ, FIQ and ABORT first take 4 off lr, and it returns with ldm sp!, {..., pc}^. A handler built for an FPU is refused (its VFP registers would not be saved) Refused
RISC-V Implemented, with "machine" (the default, returning with mret) and "supervisor" (sret). The handler saves the caller-saved registers it writes, and all of them, floating point included, when it calls; it is 4-byte aligned. "user" is refused Refused
MIPS32, MIPS64 Implemented, with "eic" (the default), "vector=sw0".."vector=hw5" and keep_interrupts_masked. The handler saves EPC, Status, the caller-saved registers it writes and HI/LO, all of them and gp when it calls, and returns with eret; on MIPS64 every save is a doubleword and EPC moves with dmfc0/dmtc0, as GCC's n64 handlers have it (clang ignores the attribute there). use_shadow_register_set and use_debug_exception_return are refused Refused
Renesas RX Implemented, as rx-elf-gcc makes it: the handler pushes the caller-saved r1-r5, r14 and r15 and the callee-saved registers it uses with pushm, and returns with rte. GCC's interrupt(N) (a vector-table slot for GNU ld to fill) and fast_interrupt are refused Refused
TriCore Implemented, as HighTec's GCC makes it: the handler saves the lower context with svlcx (the hardware has saved the upper one) and returns with rslcx and rfe; it serves a trap as well. An argument is refused Refused
ColdFire Implemented, as GCC's m68k port makes it, and spelled interrupt_handler too: the handler saves d0, d1, a0 and a1 with the callee-saved registers it uses in one movem, and returns with rte. interrupt_thread (the Fido's) and an argument are refused Refused
x86-64, AArch64 and the other targets Refused Refused

On RISC-V, MIPS, RX, ColdFire, TriCore and ARMv7-A a handler with parameters or a non-void result is refused, and so is a function given two different kinds; the attribute may be on a prototype, on the definition or on both. In C++ the attribute is refused except on Cortex-M. The refusals read:

embcc: isr.c:2: error: __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)
embcc: isr.c:2: error: __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
embcc: isr.c:2: error: interrupt handler 'isr' takes parameters: the hardware calls it, so nothing passes them, and they would be read out of whatever the interrupted code left in the argument registers
embcc: isr.c:2: error: interrupt handler 'isr' returns a value: the interrupt return goes back to the interrupted instruction, and nothing there receives it -- it must return void

How to install a handler on each board, and what each one saves, is in Embedded programming: RISC-V and MIPS32. For the AVR vector names (__vector_N), see AVR.

naked is supported on every target but x86-64 and Apple arm64: the body is assembled as a block of the target's assembly (on AArch64, from its inline-asm vocabulary); see Naked functions. On those two it is refused, and a function that must run without a prologue is written in assembly.

Placing code and data#

section puts a vector table, a function that must run from RAM, or a table collected by the linker into a named section:

void reset(void);

__attribute__((section(".vectors"), used))
void (*const vectors[])(void) = { (void (*)(void))0x20010000u, reset };

__attribute__((section(".ramfunc"))) void flash_write(void) { /* ... */ }

Add used to a table that nothing in the program refers to by name, so that it is emitted.

Default handlers#

weak together with alias gives the CMSIS pattern in which every interrupt handler defaults to one function and the application overrides the ones it needs:

void Default_Handler(void) { for (;;) { } }
void SysTick_Handler(void) __attribute__((weak, alias("Default_Handler")));

The target of alias must be defined in the same file.

AVR program memory#

On AVR, __flash is GCC's address space 1: const data kept in program memory and read with lpm. It is predefined as clang defines it, __attribute__((__address_space__(1))), and EmbCC takes that attribute as a qualifier wherever const may stand. See Data in program memory for the rules. Another address space, or address space 1 on another target, is refused by name. __memx is not defined. __attribute__((progmem)) (avr-libc's PROGMEM) places a const static object in .progmem.data on AVR, without changing its type; it is read with pgm_read_byte() and the rest of <avr/pgmspace.h>. On other targets it is ignored with a --warn=attributes warning.

avr-gcc's __builtin_avr_* are there, each with the __BUILTIN_AVR_* macro avr-gcc predefines for it:

Builtin Effect
__builtin_avr_sei(), __builtin_avr_cli() sei, cli, as memory barriers (avr-libc's sei() and cli())
__builtin_avr_sleep(), __builtin_avr_wdr(), __builtin_avr_nop() sleep, wdr, nop
__builtin_avr_nops(n) n nops, n a constant up to 4096
__builtin_avr_delay_cycles(n) Exactly n cycles of an ATmega's clock, n a constant up to 4294967295: a loop on a counter of one to four bytes in r24 up, and rjmp .+0 and nop for the remainder
__builtin_avr_swap(x) x's nibbles exchanged, an unsigned char
__builtin_avr_fmul(a, b), fmuls, fmulsu (a * b) << 1, the product of two 1.7 fractions: unsigned, signed, signed by unsigned; unsigned int or int
__builtin_avr_insert_bits(map, bits, val) Bit i of the result is bit map[i] of bits (nibble i of the constant map, 0 to 7), or bit i of val where the nibble is 0xf

They are AVR's alone: on another target __has_builtin says no, and a call is an undeclared function. __builtin_avr_flash_segment (it needs __memx) and the fixed-point ones (_Fract, _Accum) are not there.

Builtin functions#

General rules#

A name is a builtin only in its __builtin_ spelling (and, for the atomics, its __atomic_ or __sync_ spelling). EmbCC treats no library function name, such as memcpy or printf, as a builtin, with or without --no-builtins.

A __builtin_ name EmbCC does not implement is an undeclared function:

embcc: f.c:1:22: error: '__builtin_cpu_supports' is not declared in 'f' โ€” for a call, add a prototype or define it first [E0001]

Whether a builtin is available does not depend on the target, but its code generation can: the tables below give the targets where it compiles. Elsewhere it is refused when the function that uses it is compiled.

Integer bit operations#

Builtin Result
__builtin_clz(x), clzl, clzll Number of leading zero bits in an unsigned int, unsigned long, unsigned long long
__builtin_ctz(x), ctzl, ctzll Number of trailing zero bits
__builtin_popcount(x), popcountl, popcountll Number of one bits
__builtin_parity(x), parityl, parityll Number of one bits, modulo 2
__builtin_ffs(x), ffsl, ffsll One plus the index of the least significant one bit of an int, long, long long; 0 when x is 0
__builtin_clrsb(x), clrsbl, clrsbll Number of leading bits equal to the sign bit, not counting the sign bit, of an int, long, long long

All return int, take an integer argument, and compile on every target. The operand width is the target's: long is 32 bits on Cortex-M, RV32 and AVR, and int is 16 bits on AVR. GCC leaves clz and ctz of zero undefined; EmbCC returns the operand's width, as GCC folds them, which code should not depend on.

GCC 14's type-generic forms count at the operand's own width, whatever unsigned integer type it is, unsigned char to unsigned __int128 (and _BitInt up to 64 bits); ffsg and clrsbg take a signed one. bool and an enumeration are refused, as GCC refuses them (__builtin_clzg takes an unsigned integer type, as in GCC, not int). All return int:

Builtin Result
__builtin_clzg(x), __builtin_clzg(x, z) Leading zero bits; z, an int, when x is 0 (without it, the width)
__builtin_ctzg(x), __builtin_ctzg(x, z) Trailing zero bits; z when x is 0
__builtin_popcountg(x), __builtin_parityg(x) One bits; their number modulo 2
__builtin_ffsg(x), __builtin_clrsbg(x) As ffs and clrsb, at x's width

C23's <stdbit.h> operations as builtins (GCC 15's), type-generic the same way -- EmbCC's own <stdbit.h> needs none of them, but code written for GCC calls them by name:

Builtin Result
__builtin_stdc_leading_zeros(x), _leading_ones, _trailing_zeros, _trailing_ones The count, the width for an x that is all one or the other
__builtin_stdc_first_leading_zero(x), _first_leading_one, _first_trailing_zero, _first_trailing_one One plus that bit's position, counted from its end; 0 when there is none
__builtin_stdc_count_zeros(x), _count_ones The number of such bits
__builtin_stdc_has_single_bit(x) Whether x is a power of two, as bool
__builtin_stdc_bit_width(x) The bits needed to represent x: 0 for 0
__builtin_stdc_bit_floor(x), _bit_ceil(x) The largest power of two not above x (0 for 0) and the smallest not below it (1 for 0 and 1; 0 when it is past the type), in x's type
__builtin_stdc_rotate_left(x, n), _rotate_right(x, n) x rotated by n modulo the width -- a negative n the other way -- in x's type

The counts are unsigned int. A width that is not a power of two is refused for the rotates, and above 64 bits only __int128 is taken.

Every builtin in this section -- and the byte swaps and clang's bit reversals and rotates below -- is a constant when its operands are, as in GCC and clang: _Static_assert(__builtin_popcount(MASK) == 4, ""), an array size, a case label, or a register field's shift written __builtin_ctz(FIELD_MASK) in a static table's initializer.

clang's bit reversals and rotates take and return the unsigned type of their width (uint8_t to uint64_t):

Builtin Result
__builtin_bitreverse8(x), 16, 32, 64 x with the order of its bits reversed
__builtin_rotateleft8(x, n), 16, 32, 64 x rotated left by n modulo the width
__builtin_rotateright8(x, n), 16, 32, 64 x rotated right by n modulo the width

Byte swapping#

Builtin Result
__builtin_bswap16(x) x with its two bytes reversed, as unsigned short
__builtin_bswap32(x) x with its four bytes reversed, as the 4-byte unsigned type: unsigned int, or unsigned long on AVR
__builtin_bswap64(x) x with its eight bytes reversed, as the 8-byte unsigned type: unsigned long where long is 8 bytes, unsigned long long on Cortex-M, RV32 and AVR

The result has the builtin's size on every target, and the argument is converted to the result type before its bytes are reversed. All three compile on every target. On Cortex-M, RV32 and AVR the swap is computed with shifts, masks and ORs.

Checked arithmetic#

__builtin_add_overflow(a, b, res), __builtin_sub_overflow and __builtin_mul_overflow compute a + b, a - b or a * b as if with unlimited range, store the result converted to the type *res points to, and return 1 if the stored value differs from the mathematical result and 0 otherwise. a and b are integers of any type; res points to an integer. They compile on every target.

__builtin_add_overflow_p(a, b, c), __builtin_sub_overflow_p and __builtin_mul_overflow_p return whether the same operation overflows the type of c. The value of c is not used; it is evaluated for its side effects only.

The result is int, not _Bool as in GCC. A wrong operand is refused: __builtin_add_overflow: operand 1 must be an integer, got double, or __builtin_add_overflow: the third argument points at the integer to store the result in.

Floating-point constants#

Builtin Value Type
__builtin_inf(), __builtin_huge_val() Positive infinity double
__builtin_inff(), __builtin_huge_valf() Positive infinity float
__builtin_infl(), __builtin_huge_vall() Positive infinity long double
__builtin_nan(""), nanf, nanl A quiet NaN double, float, long double

These are constants and may appear in static initializers. The string argument of __builtin_nan is not used: the result is always the same NaN, whatever payload the string names. The long double forms compile wherever long double values do; on RISC-V, operations on long double are refused (the RV32 backend cannot lower a 128-bit value yet ...).

Floating-point classification and sign#

Builtin Result
__builtin_fabs(x), fabsf, fabsl x with its sign bit cleared
__builtin_copysign(x, y), copysignf, copysignl x with the sign of y
__builtin_signbit(x), signbitf, signbitl Nonzero if the sign bit of x is set, including for -0.0 and a negative NaN
__builtin_isnan(x) 1 if x is a NaN
__builtin_isinf(x) 1 if x is an infinity
__builtin_isinf_sign(x) 1 for positive infinity, -1 for negative infinity, 0 otherwise
__builtin_isfinite(x) 1 if x is neither infinite nor NaN
__builtin_isnormal(x) 1 if x is a normal number: not zero, subnormal, infinite or NaN
__builtin_isinff(x), isinfl, isnanf, isnanl, finite, finitef, finitel The same as __builtin_isinf, __builtin_isnan and __builtin_isfinite
__builtin_fpclassify(nan, inf, normal, subnormal, zero, x) The argument naming x's class: nan for a NaN, inf for an infinity, and so on. x is evaluated once; the five classes are int (glibc's fpclassify is this builtin)

fabs and copysign return the floating type their suffix names; the others return int. The classification builtins take a floating argument of any type, and refuse an integer (__builtin_isnan takes a floating-point argument). All of them operate on the bits of the value: they do not round, raise no floating-point exception, and call no library.

__builtin_powi(x, n), __builtin_powif and __builtin_powil raise a double, float or long double x to the int power n, by squaring and multiplying over n's bits and taking 1 / x^-n for a negative n, as libgcc does. They are calls to libgcc's names, __powidf2, __powisf2 and __powixf2 (x87 long double) or __powitf2 (binary128), which EmbCC's runtime defines on every target, so an object of GCC's that calls them links too. GCC documents no precision for them, and neither does EmbCC: a result can differ from pow's in the last bit.

__builtin_complex(re, im) is the complex of re's and im's type -- both one real floating type, as GCC requires (__builtin_complex takes two parts of one real floating type, as in GCC, not double and float) -- made of those two parts, whatever they are: an infinite or NaN imaginary part and a negative zero survive, which re + im * I does not keep. Of constants it is a constant, so EmbCC's <complex.h> defines C11's CMPLX, CMPLXF and CMPLXL with it, and they initialize a static object, as C11 asks.

Floating-point comparisons#

Builtin Result
__builtin_isgreater(x, y), isgreaterequal, isless, islessequal x > y, x >= y, x < y, x <= y: 0 when either is a NaN
__builtin_islessgreater(x, y) x < y || x > y: 0 when either is a NaN
__builtin_isunordered(x, y) 1 when either is a NaN
__builtin_iseqsig(x, y) x == y

The operands are compared in their common real type, and each is evaluated once. They are <math.h>'s comparison macros; EmbCC does not track floating-point exceptions, so the quiet and signaling forms are the same comparisons.

Type classification#

__builtin_classify_type(x) is GCC's class of x's type, after an array or function decays and an integer is promoted: 1 for an integer (an enum and a _Bool among them), 5 for a pointer, 8 for a real floating type, 9 for a complex one, 12 for a structure, 13 for a union and 0 for void. x is not evaluated.

They compile on every target. A 16-byte long double (x87's 80-bit format on x86-64, IEEE binary128 on AArch64 ELF and Linux) is stored to a stack slot and its sign and exponent are read and written there; the other widths stay in registers. On RISC-V, where every operation on a long double is refused, so are these.

Square root#

Builtin Result
__builtin_sqrt(x) Square root, double
__builtin_sqrtf(x) Square root, float
__builtin_sqrtl(x) Square root, long double

Where the target has a square-root instruction for the type, the builtin is that instruction, and its result is correctly rounded:

Target Instruction for
x86-64 float, double (sqrtss, sqrtsd)
AArch64 float, double (fsqrt)
Cortex-M with the FPU enabled (-eabihf triples, or --fpu= with --float-abi=softfp or hard) float (vsqrt.f32)
Cortex-M7 with --fpu=fpv5-d16 (or --cpu=cortex-m7 with an -eabihf triple) float, double (vsqrt.f32, vsqrt.f64)

Everywhere else -- a double on a single-precision Cortex-M FPU, soft-float Cortex-M, RISC-V, AVR, and a 16-byte long double -- the builtin is a call to sqrt, sqrtf or sqrtl, as with GCC, and the program must link a math library that defines it. The precision is then that library's; EmbCC's own sqrtl computes in double (see Libraries). On RISC-V, __builtin_sqrtl is refused with every other long double operation.

Library functions#

__builtin_memcpy, __builtin_memmove, __builtin_memset, __builtin_memcmp, __builtin_memchr, __builtin_strlen, __builtin_strcmp, __builtin_strncmp, __builtin_strcpy, __builtin_strncpy, __builtin_strcat and __builtin_strchr are the library functions under another name: each becomes a call to the function without the prefix, which the program must provide at link time. memcpy, memmove and memset may also be expanded inline. See What the compiler emits calls to.

The other C and POSIX library functions GCC has a builtin for are the same, without the inline expansion: __builtin_abort, __builtin_exit, __builtin_printf, __builtin_malloc, __builtin_stpcpy, __builtin_isdigit, __builtin_sin and __builtin_sinf, among the functions of <stdlib.h>, <stdio.h>, <string.h>, <strings.h>, <ctype.h>, <math.h> (each with its f and l variant) and <complex.h>. When the program has no declaration of the function, EmbCC declares it with its C prototype, so __builtin_printf needs no <stdio.h>. __builtin_memcmp_eq is memcmp.

The _FORTIFY_SOURCE forms, such as __builtin___memcpy_chk(d, s, n, size) and __builtin___sprintf_chk(s, flag, size, fmt, ...), are the call without the object-size arguments. They are what GCC calls when the size is unknown, and EmbCC has no sizes to check.

Object size#

__builtin_object_size(ptr, type) and __builtin_dynamic_object_size(ptr, type) return the number of bytes reachable through ptr, as a size_t constant. EmbCC answers with the object's size when ptr is the address of a named object that is not an array or a pointer (&s). For every other argument it returns the value that means "unknown": (size_t)-1 for type 0 and 1, and 0 for type 2 and 3. A _FORTIFY_SOURCE header reads that value as "do not check".

Compile-time builtins#

Builtin Result
__builtin_constant_p(expr) 1 if expr is an integer constant expression, else 0. It is decided when the expression is checked, before optimization: a const variable, a floating constant, and a parameter that is constant after inlining all give 0. expr is not evaluated
__builtin_types_compatible_p(type1, type2) 1 if the two types are the same after top-level qualifiers are removed, else 0. An integer constant expression. An array of unknown size and one of known size give 0
__builtin_choose_expr(const, expr1, expr2) expr1 if const is nonzero, else expr2. The arm not chosen is not type-checked. A condition that is not constant is refused: __builtin_choose_expr needs a constant condition
__builtin_offsetof(type, designator) The byte offset of a member, as an integer constant expression. The designator may use .member and [constant]. This is what <stddef.h>'s offsetof expands to
__builtin_LINE() The line of the call, as an int constant
__builtin_FILE() The name of the source file, as a string literal
__builtin_FUNCTION() The name of the enclosing function, as __func__ is; "" outside a function
__builtin_assume(cond) Nothing: cond is not evaluated, and the promise is not used (clang's)
__builtin_speculation_safe_value(v), (v, failval) v, an integer or a pointer. EmbCC hardens nothing against speculative execution, so there is no barrier and failval is not used
__builtin_expect(expr, value), __builtin_expect_with_probability(expr, value, p) expr; the hint is discarded
__builtin_assume_aligned(ptr, align, ...) ptr; the hint is discarded

Control flow#

Builtin Effect
__builtin_unreachable() Emits the target's trap instruction. Reaching it stops the program; it is not undefined behavior
__builtin_trap() Emits the target's trap instruction
__builtin_prefetch(addr, ...) Evaluates its arguments and emits nothing
__builtin___clear_cache(begin, end) Makes the instructions stored in [begin, end) the ones fetched, after a loader or a JIT writes code: a call to libgcc's __clear_cache -- each architecture's sequence in EmbCC's runtime, the system's on macOS and with glibc -- and nothing on x86-64, AVR, RX and ColdFire, as GCC emits on x86. The arguments are pointers (__builtin___clear_cache takes two pointers, as in GCC, not int)
__builtin_debugtrap() Emits the target's breakpoint instruction, inline: under a debugger the program stops there and can carry on; without one it stops with the breakpoint's signal or exception (clang's)

The trap is ud2 on x86-64, udf on AArch64 and Cortex-M, unimp on RISC-V and break on MIPS32. The breakpoint is the one each target's debugger plants: int3 on x86-64, brk #0xf000 on AArch64 (lldb steps over it, as for clang's), bkpt #0 on Cortex-M and ARMv7-A, ebreak on RISC-V, break on MIPS and AVR, break 0 on LoongArch, debug on TriCore, break 1, 15 on Xtensa, trap on PowerPC, ta 1 on SPARC, brk on RX and halt on ColdFire. AVR has no trapping instruction, and the trap is a jump to itself. A call to either builtin ends a path for the missing-return check, as a call to a noreturn function does.

Stack and frames#

Builtin Result Targets
__builtin_alloca(size) A pointer to size bytes in the current function's frame, freed when the function returns All
__builtin_alloca_with_align(size, align) As __builtin_alloca, aligned to align bits, which must be a constant power of two of at least 8 All
__builtin_frame_address(level) The frame address of the current function (level 0) or of a caller, found by following the saved frame pointers Any level: x86-64, AArch64, ColdFire. Level 0: Cortex-M, ARMv7-A, RISC-V, MIPS32, MIPS64, LoongArch, SPARC, PowerPC, Xtensa, TriCore, RX, AVR
__builtin_return_address(level) The return address of the current function (level 0) or of a caller As __builtin_frame_address
__builtin_extract_return_addr(addr), __builtin_frob_return_addr(addr) The address a function returns to, from __builtin_return_address's, and back: addr itself, but on SPARC, whose return is past its call, addr + 8 -- addr + 12 in a function returning a struct or a long double, which returns past the caller's unimp word -- and frob takes it off, as GCC's All

level must be a non-negative integer constant (__builtin_frame_address needs a non-negative constant level).

A frame chain. x86-64, AArch64 and ColdFire code keeps a chain of saved frame pointers, so any level can be walked.

Level 0 only. Code for the other targets keeps no chain, and only the current function's own frame can be found. A higher level is refused:

embcc: r.c:1: error: __builtin_return_address(1) is not supported on riscv32-unknown-elf: code for this target keeps no frame-pointer chain, so only level 0 (this function's own frame) can be found

At level 0, a function that asks for its return address saves it, as a function that calls does. __builtin_return_address(0) is:

Target Return address
Cortex-M, ARMv7-A lr as the function was entered, with the Thumb bit, as GCC and clang return it: in lr still in a function that pushes nothing, else the pushed word
RISC-V, MIPS, LoongArch, PowerPC The return register (ra or LR) as the function was entered
TriCore A11, which a call keeps for the whole body
SPARC %i7, the address of the call itself (the return goes to %i7 + 8), as GCC and clang return it
Xtensa a0 with its top two bits, the windowed ABI's call increment, replaced by those of the function's own address, as GCC does
RX The word the call pushed
AVR The word address the call pushed, as an AVR function pointer holds it (2-byte program counters)

__builtin_frame_address(0) is:

Target Frame address
Cortex-M, ARMv7-A, RISC-V, MIPS, LoongArch, Xtensa, TriCore The stack pointer at entry, which is what GCC and clang return on RISC-V. GCC and clang return the frame pointer r7 on Cortex-M, which EmbCC's code does not keep
SPARC %fp, the same address
RX and AVR The stack pointer at entry, which points at, or just below, the return address the call pushed
PowerPC r1 after the prologue, the frame's back-chain word, as GCC and clang return it

Interrupt handlers. In a RISC-V, MIPS or AVR interrupt handler, __builtin_return_address is refused: a handler was not called, and what it returns to is the trap's. AVR refuses __builtin_frame_address there too.

A function that calls alloca is never inlined.

Variable arguments#

__builtin_va_list is the type char *, the representation of va_list on every target. __builtin_va_start(ap, last), __builtin_va_arg(ap, type), __builtin_va_copy(dest, src) and __builtin_va_end(ap) are what <stdarg.h>'s macros expand to. Using va_start in a function that is not variadic is refused (va_start in 'f', which is not variadic). Passing and retrieving structures through ... is described in C language.

__builtin_setjmp and __builtin_longjmp#

GCC's pair, for a jump with no C library behind it (a language runtime, an SJLJ unwinder): __builtin_setjmp(buf) returns 0, and 1 when __builtin_longjmp(buf, 1) comes back to it, from anywhere below while the function that called __builtin_setjmp has not returned. buf is GCC's five words. EmbCC keeps an ordinary jmp_buf in that function's frame, stores its address in buf[0], and calls the runtime's own setjmp and longjmp on it (lib/rt's, built from lib/libc's for every target; the system C library's _setjmp and _longjmp on Darwin and with --libc=system). As with setjmp, a local changed between the two and read after the jump must be volatile. __builtin_longjmp takes only the constant 1, as in GCC; both are refused on Windows (x86_64-windows-gnu), whose _setjmp takes a frame for its unwinder. clang has the pair on fewer targets (not on AArch64 macOS).

Builtins that are not supported#

These are refused as undeclared functions, as shown in General rules: __builtin_va_arg_pack, clang's __builtin_elementwise_*, __builtin_reduce_* and __builtin_nontemporal_load, __builtin_cpu_supports and __builtin_cpu_init, the x86 family (__builtin_ia32_*). __builtin_addressof, __builtin_bit_cast and __builtin_is_constant_evaluated exist in C++ only; see C++.

Atomic builtins#

The builtins#

EmbCC implements GCC's two families.

Builtin Operation
__atomic_load_n(p, order) Returns *p
__atomic_store_n(p, v, order) *p = v
__atomic_exchange_n(p, v, order) *p = v, returns the old value
__atomic_compare_exchange_n(p, expected, desired, weak, success, failure) If *p == *expected, stores desired and returns 1; otherwise copies *p into *expected and returns 0
__atomic_load(p, ret, order), __atomic_store(p, val, order), __atomic_exchange(p, val, ret, order), __atomic_compare_exchange(p, expected, desired, weak, success, failure) The generic forms: the values are passed through pointers, so the object may be of any type of 1, 2, 4, 8 or 16 bytes, such as a double or a small structure
__atomic_fetch_OP(p, v, order) *p = *p OP v, returns the old value. OP is add, sub, and, or, xor or nand
__atomic_OP_fetch(p, v, order) As above, returns the new value
__atomic_test_and_set(p, order) Sets the byte at p, returns whether it was set
__atomic_clear(p, order) Clears the byte at p
__atomic_thread_fence(order), __atomic_signal_fence(order) A full memory barrier
__atomic_always_lock_free(size, p), __atomic_is_lock_free(size, p) See below
__sync_fetch_and_OP(p, v, ...), __sync_OP_and_fetch(p, v, ...) As the __atomic forms, sequentially consistent
__sync_val_compare_and_swap(p, old, new, ...) Returns the old value
__sync_bool_compare_and_swap(p, old, new, ...) Returns 1 if the swap happened
__sync_lock_test_and_set(p, v, ...) Stores v, returns the old value
__sync_lock_release(p, ...) Stores 0
__sync_synchronize() A full memory barrier

The _n and arithmetic forms take an integer or a pointer of 1, 2, 4, 8 or 16 bytes. A wrong argument count or type is refused, for example __atomic_load_n takes 2 arguments, not 3 or __atomic_fetch_add works on an integer or pointer of 1, 2, 4, 8 or 16 bytes, not double. The __sync builtins accept, and ignore, the trailing list of variables GCC allows.

clang's __c11_atomic_* family -- init, load, store, exchange, compare_exchange_strong and _weak, fetch_add, _sub, _and, _or, _xor and _nand, thread_fence, signal_fence and is_lock_free -- are the __atomic_* builtins of the same names with clang's arguments (init is a relaxed store, and a compare-exchange's strength is in its name). __c11_atomic_fetch_add and _fetch_sub of an atomic pointer count elements, as C11's atomic_fetch_add does (7.17.7.5), where GCC's __atomic_fetch_add counts bytes; EmbCC's <stdatomic.h> adds and subtracts with them, so atomic_fetch_add(&p, 1) on an _Atomic(int *) moves p by one int, as with clang -- GCC's <stdatomic.h> moves it by one byte.

On RISC-V the memory order of a read-modify-write or compare-exchange selects its .aq and .rl bits as clang's does: relaxed is bare, acquire .aq, release .rl, and acq_rel and seq_cst .aqrl on an AMO; an lr/sc loop takes the acquire on the lr and the release on the sc, and seq_cst is lr.aqrl/sc.rl. A compare-exchange's failure order strengthens its success order, as clang merges them. An order that is not a constant is seq_cst, as are the __sync builtins and the operators on an _Atomic object. On every other target every operation is sequentially consistent: the memory-order arguments are accepted and do not change the code. __atomic_signal_fence emits the same barrier as __atomic_thread_fence. No operation of 1, 2 or 4 bytes, or of 8 on a 64-bit target, calls a library; an eight-byte one on a 32-bit target does (below).

__atomic_always_lock_free and __atomic_is_lock_free are integer constant expressions, usable in _Static_assert. The size must be a constant (__atomic_is_lock_free needs a constant size). They answer 1 for sizes 1, 2, 4 and 8 and 0 otherwise, on every target; the answer does not reflect the table below.

Sizes per target#

Operation x86-64 AArch64 Cortex-M RV32 RV64 AVR
Load, store 1, 2, 4, 8, 16 1, 2, 4, 8, 16 1, 2, 4; 8 by a call 1, 2, 4; 8 by a call 1, 2, 4, 8 1, 2, 4, 8
Exchange, fetch-and-op, compare-exchange, test-and-set 1, 2, 4, 8, 16 1, 2, 4, 8, 16 1, 2, 4; 8 by a call 1, 2, 4; 8 by a call 1, 2, 4, 8 1, 2, 4, 8
Fences Yes Yes Yes Yes Yes Yes (no instruction)

16-byte operations need __int128 or a 16-byte object through the generic forms. Other sizes are refused, for example:

embcc: a.c:1: error: an atomic access of 16 bytes is not one access on this target (it moves 8 at once): the halves could be split by an interrupt or another core

Eight bytes on a 32-bit target#

Every 32-bit target -- Cortex-M, ARMv7-A, RV32, MIPS32, SPARC, PowerPC, ColdFire, TriCore, Xtensa and RX -- does an eight-byte atomic by calling libatomic's sized routine, as GCC and clang do there (none of them moves eight bytes atomically):

Operation Called
__atomic_load_n, __atomic_load, reading an _Atomic u64 __atomic_load_8(const volatile void *p, int order)
__atomic_store_n, __atomic_store, __sync_lock_release, = void __atomic_store_8(volatile void *p, u64 v, int order)
__atomic_exchange_n, __atomic_exchange, __sync_lock_test_and_set u64 __atomic_exchange_8(volatile void *p, u64 v, int order)
__atomic_compare_exchange_n, __atomic_compare_exchange, the __sync compare-and-swaps, *= and the other operators without a fetch form bool __atomic_compare_exchange_8(volatile void *p, void *expected, u64 desired, int success, int failure)
__atomic_fetch_OP, __atomic_OP_fetch, __sync_fetch_and_OP, __sync_OP_and_fetch, ++, --, +=, -=, &=, |=, ^= u64 __atomic_fetch_OP_8(volatile void *p, u64 v, int order), OP one of add, sub, and, or, xor, nand

The orders are the __ATOMIC_* values as the call wrote them -- a variable order is passed as its value -- and seq_cst (5) for the __sync builtins and the operators, except __sync_lock_release's release (3), as clang passes them. lib/rt defines every one, weak, by masking interrupts; see Embedded programming for what that does and does not cover, and how to replace them.

The instructions used on each embedded target are described in Embedded programming. _Atomic objects and the <stdatomic.h> interface are described in C language.

Vector extensions#

__attribute__((vector_size(N))) makes a vector of N bytes of an integer or floating type, as in GCC:

typedef int v4si __attribute__((vector_size(16)));

v4si scale(v4si a, int k)
{
    return (a << 1) + k;        /* k goes to every lane */
}

The attribute is taken on a typedef, a variable, a member, a parameter and in a type name, before the type or after the declarator, so the usual macro spelling, #define vector __attribute__((vector_size(16))) with vector float x, works too. N is a power of two and a whole number of lanes; _Bool and long double lanes are refused.

Operation What it does
+ - * / % & | ^ << >> Lane by lane. Both operands are vectors of the same number and size of lanes, or one is a scalar that converts to a lane without loss (an integer constant that fits its bits, or a type no wider than the lane): conversion of scalar ... involves truncation otherwise. %, the bitwise operators and the shifts take integer lanes
== != < <= > >= A vector of signed integers of the lanes' size, each lane -1 (every bit set) or 0. It converts to any vector of its size, as GCC's opaque comparison type does: unsigned_v = a == b
- ~ +, ++ --, op= Lane by lane
v[i] The lane, an lvalue when v is; i may be any integer
(T)v Between two vectors, or a vector and an integer, of the same size: the same bytes as the other type
__builtin_shuffle(a, mask), __builtin_shuffle(a, b, mask) Lane k is lane mask[k] of a, or of a and b side by side, the index taken modulo the lanes there are
__builtin_shufflevector(a, b, i...) Lane k is lane i_k of a and b side by side; the indices are constants, and -1 is a lane of no value (0)
__builtin_convertvector(v, T) Each lane converted to the lanes of the vector type T, which has as many

A vector is assigned, passed, returned and placed in structures and arrays like a structure of its lanes. Its alignment is its size, up to 16 bytes on x86-64 and AArch64 and 8 on Arm, as GCC and clang align vectors there. A vector cannot be the condition of if, ?:, && or ||, as in GCC (used vector type where scalar is required).

EmbCC computes the operations lane by lane with the target's scalar instructions, so they run on every target, AVR and Cortex-M0 included. They are not translated to SIMD instructions; the optimizer's automatic vectorization is separate, see Optimization. A vector argument is passed as that structure would be. On x86-64 and AArch64, where GCC passes a vector in a SIMD register, a function taking or returning one by value does not interoperate with GCC-compiled code. Arithmetic on vectors in a static initializer is refused; a braced initializer, v4si g = {1, 2, 3, 4};, is fine. Clang's ext_vector_type is not recognized; it draws the unknown-attribute warning, and the type stays a scalar.

Feature-test operators#

__has_include and __has_include_next#

__has_include(<header>) and __has_include("header") are 1 if the header can be found by the search an #include of the same name would make, and 0 otherwise. __has_include_next asks the same about the search #include_next would make.

__has_builtin#

__has_builtin(name) is 1 for:

  • the integer bit operations, byte swaps, checked arithmetic, floating-point constants, classification and square-root builtins, __builtin_alloca, __builtin_alloca_with_align, __builtin_assume_aligned, __builtin_constant_p, __builtin_expect, __builtin_expect_with_probability, __builtin_frame_address, __builtin_return_address, the memory and string builtins, __builtin_object_size, __builtin_dynamic_object_size, __builtin_offsetof, __builtin_prefetch, __builtin_trap, __builtin_unreachable, __builtin_va_arg, __builtin_va_copy, __builtin_va_end and __builtin_va_start;
  • every __atomic_ and __sync_ builtin listed in Atomic builtins.

It is 0 for __builtin_types_compatible_p, __builtin_choose_expr, __builtin_LINE, __builtin_FILE and __builtin_va_list, although EmbCC implements them, and for every name it does not implement. The answer does not depend on the target: a builtin that the target's code generator refuses still answers 1.

__has_attribute#

__has_attribute(name) accepts name, __name__ and a gnu:: or __gnu__:: scope. It is 1 for: access, aligned, alloc_align, alloc_size, always_inline, artificial, cold, const, deprecated, fallthrough, flatten, format, gnu_inline, hot, leaf, malloc, may_alias, maybe_unused, no_sanitize, no_sanitize_address, nodiscard, noinline, noipa, nonnull, noreturn, nothrow, packed, pure, returns_nonnull, section, sentinel, unused, used, visibility, warn_unused_result and weak. It is also 1 for abi_tag, format_arg, likely, unavailable and unlikely, which C does not recognize (each draws the unknown attribute warning), and for externally_visible, which C takes with no effect.

It is 0 for every other name, including attributes EmbCC implements: alias, constructor, destructor, embcc_sret, interrupt, pcs and signal. Test for those with __EMBCC__ and the target macros.

__has_c_attribute and __has_cpp_attribute#

__has_c_attribute(name) is defined in C and is 0 for every name, although EmbCC accepts the C23 attributes [[noreturn]], [[deprecated]], [[nodiscard]], [[maybe_unused]] and [[fallthrough]]. __has_cpp_attribute is defined only in C++.

__has_feature and __has_extension#

Both answer the same. They are 1 for c_static_assert, c_generic_selections, c_atomic, c_alignas, c_alignof, c_thread_local, tls, attribute_deprecated_with_message, attribute_unavailable_with_message and enumerator_attributes, and 0 for every other name. Three of these answers overstate what C accepts: tls is 1 on Mach-O and COFF targets, where __thread is refused; unavailable is not recognized; and an attribute on an enumerator is a syntax error.

Using the operators#

Each operator counts as defined, so the guarded form used by portable headers works:

#if defined(__has_include) && __has_include(<stdatomic.h>)
#include <stdatomic.h>
#endif

#ifdef and #elifdef see them the same way. In C, an operator is evaluated only where it is written in the #if line itself. One that a macro expands to is not:

#define HAS(x) __has_builtin(x)
#if HAS(__builtin_clz)        /* error: trailing junk in #if expression */
#endif

In C++ both forms work.

Preprocessor extensions#

Supported#

Feature Notes
#include_next Continues the search after the directory the current file was found in. A missing file is cannot find a NEXT include file "h.h"
#warning text Prints warning: #warning: text and continues
#elifdef, #elifndef C23
#embed "file" C23; expands to the file's bytes as a comma-separated list of integers
__VA_OPT__(tokens) C23; the tokens appear only when the variadic arguments are not empty
_Pragma("...") The operator form of #pragma, usable in a macro
GNU line markers, # 12 "file.c" Read as #line
Named variadic macro parameters, #define F(args...) GNU; args names the variable arguments
GNU comma elision, , ## __VA_ARGS__ With no variable arguments the comma is removed, so LOG("x") with #define LOG(f, ...) printf(f, ## __VA_ARGS__) is printf("x")
__COUNTER__ GNU; 0, 1, 2, ... at each use in the translation unit
__FILE_NAME__, __BASE_FILE__, __INCLUDE_LEVEL__ GNU; the current file's last path component, the main file's name, and the current #include depth (0 in the main file)
#include MACRO The operand is macro-replaced, and must then be "..." or <...>
defined on the feature-test operators See Using the operators

Not supported#

Feature Behavior
#ident, #sccs, #assert, #unassert, #import unknown directive '#ident'
__TIMESTAMP__ Not defined; the name is left as written
$ in a macro name Not accepted as part of an identifier

Pragmas#

#pragma pack#

#pragma pack sets the maximum alignment of the members of the structures and unions defined after it. It takes these forms:

Form Effect
#pragma pack(N) Members are aligned to at most N bytes. N is 1, 2, 4, 8 or 16; 0 removes the limit
#pragma pack() Removes the limit
#pragma pack(push) Saves the current limit
#pragma pack(push, N) Saves the current limit and sets N
#pragma pack(pop) Restores the last saved limit
#pragma pack(show) Accepted; does nothing

The limit applies to a structure as a whole, at the point where its definition starts, including a definition inside a function. _Pragma("pack(1)") is the same as #pragma pack(1). The argument is not macro-expanded, as in GCC on ELF targets.

Refused Diagnostic
A value other than 0, 1, 2, 4, 8 or 16, or a macro name #pragma pack wants 1, 2, 4, 8 or 16
A named push or pop, pack(push, name, N) #pragma pack(push, name) is not supported
pack(pop, ...) with arguments #pragma pack(pop, ...) with a name or count is not supported
pack(pop) with nothing saved #pragma pack(pop) without a matching push
More than 32 nested pushes #pragma pack(push) nested too deeply
Inside a structure body #pragma pack inside a struct body is not supported: put it before the struct
Without parentheses, #pragma pack 1 #pragma pack needs its arguments in parentheses
In C++ #pragma pack is not supported in C++: it would change the layout and EmbCC would ignore it. Use __attribute__((packed)) on the struct

#pragma once#

A file containing #pragma once is not read again by a later #include. The same file is recognized by its path, or, when it is reached by another spelling of the path, by its contents, as GCC does.

#pragma push_macro and #pragma pop_macro#

#pragma push_macro("NAME") saves the current definition of the macro NAME, or the fact that it has none. #pragma pop_macro("NAME") restores the most recently saved one: NAME is redefined as it was, or undefined if it was undefined then. Saves nest. A pop_macro with no matching push_macro does nothing, as in GCC. Both work as _Pragma operators, _Pragma("push_macro(\"NAME\")").

#pragma weak#

#pragma weak NAME makes NAME a weak symbol, as __attribute__((weak)) on its declaration does: a weak definition, or a weak reference that resolves to 0 when nothing defines it. The pragma may come before or after the declaration. #pragma weak NAME = TARGET also makes the function NAME an alias of TARGET, which must be defined in the same file. Refused forms:

Refused Diagnostic
An alias for a variable #pragma weak v = t: an alias is supported for a function, and 'v' is a variable
An alias for an undeclared name #pragma weak f = t: 'f' is not declared in this file, and the alias needs its type
Inside a structure body #pragma weak inside a struct body is not supported: put it before the struct
In C++ #pragma weak is not supported in C++: use __attribute__((weak)) on the declaration

#pragma GCC diagnostic#

#pragma GCC diagnostic KIND "-WNAME" sets the warning NAME from the pragma's position on; #pragma clang diagnostic and the _Pragma operator form are the same. Each warning is decided by the pragmas before it in the preprocessed text, so a pragma in a header applies after the #include unless the header pops it.

Form Effect
#pragma GCC diagnostic ignored "-WNAME" NAME is not reported
#pragma GCC diagnostic warning "-WNAME" NAME is reported as a warning, even when the command line does not enable it, and even under -Werror
#pragma GCC diagnostic error "-WNAME" NAME is reported as an error
#pragma GCC diagnostic push Saves the state of every warning
#pragma GCC diagnostic pop Restores the state last saved; with nothing saved, returns every warning to the command line's setting

A pragma that names a warning EmbCC does not have is ignored without a diagnostic; EmbCC never reports that warning, so the pragma would change nothing. Other kinds, such as ignored_attributes, are ignored. --no-warnings and the suppression of warnings in system headers apply whatever the pragmas say. A warning located in a header that is included more than once is decided at the header's last inclusion.

Every other pragma is ignored#

Every other #pragma and _Pragma is removed without a diagnostic, and does not appear in -E output. This includes pragmas that change a program's meaning or its diagnostics in GCC and Clang:

Pragma Use instead
#pragma GCC visibility push(...), pop __attribute__((visibility(...))) on each declaration
#pragma GCC poison, #pragma GCC system_header, #pragma message, #pragma GCC optimize, #pragma redefine_extname None
#pragma STDC FP_CONTRACT, FENV_ACCESS, CX_LIMITED_RANGE None

Accepted without effect#

These constructs are accepted, have no effect, and draw no diagnostic. Each is described in its section above.

  • Every pragma except pack, once, push_macro, pop_macro, weak and GCC diagnostic.
  • packed and section on a single structure member.
  • pcs on a function-pointer parameter.
  • The memory-order arguments of the atomic builtins.
  • The payload string of __builtin_nan.
  • weak and visibility on COFF output, and visibility on Mach-O output.