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]]meanunused,warn_unused_resultandnoreturn; the others coincide with their GNU names. The scope is dropped whatever it is, so[[clang::packed]]also meanspacked.
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,unionorenum, 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, whereweak,noreturn,format,packed,aligned,sectionandpcsapply 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:
errorandwarning. 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 withKEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)))beforeKEEP(*(.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_arraybackwards. - 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 everydma_buf_tobject on a 32-byte boundary.typedef uint32_t u32_una __attribute__((aligned(1)));is the idiom for an unaligned access. Through au32_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
.textmakes the section executable, and one beginning.rodataread-only. Any other name gives a writable section. .bssand 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
constobject 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_endand__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,weakandGCC diagnostic. packedandsectionon a single structure member.pcson a function-pointer parameter.- The memory-order arguments of the atomic builtins.
- The payload string of
__builtin_nan. weakandvisibilityon COFF output, andvisibilityon Mach-O output.