Invoking EmbCC#
This page is the reference for the embcc command line: how a command is
put together, and every option in detail, grouped by what it is for. The
option reference lists all of them in one table, printed by
the compiler from its own option table; this page says what each one does.
EmbCC's own spellings are used throughout. Where GCC spells an option differently, GCC's spelling is given under the heading, and works too: a build written for GCC builds unchanged (Migrating from GCC). GCC flags that EmbCC has no name for are gathered in GCC flags EmbCC accepts.
Each option is one of three kinds, and its entry says which:
- Honoured. The option does what its entry says.
- Accepted. The option is taken without error because EmbCC already behaves the way it asks, or because doing nothing is harmless. Each such entry says which.
- Refused. The option names a promise about the generated code that EmbCC cannot keep (position independence, stack protection, profiling, link-time optimization, and so on). The driver stops with an error that names the option, so a build never receives an object that silently lacks what it asked for.
An option that is none of these stops the driver with
embcc: error: unknown argument 'ARG', followed by the usage line.
Synopsis#
embcc [OPTION...] [FILE] [OBJECT|ARCHIVE|-lLIB...]
compile and link (x86-64 ELF; ARM,
RISC-V, MIPS and AVR firmware given
its memory map)
embcc -c [OPTION...] FILE [-o OBJECT] compile or assemble to an object
embcc -S [OPTION...] FILE [-o FILE.s] write assembly
embcc -E [OPTION...] FILE preprocess to standard output
embcc inspect STAGE FILE [OPTION...] print one stage of the pipeline
embcc why DECISION [SUBJECT] FILE [OPTION...]
explain an optimizer decision
embcc --version | --help[=gcc] | --help-warnings | --print-target
embcc --dump-predef | --print-search-dirs | --explain=ID
embcc --reference=options|warnings|targets|man
embcc --emit-empty-object FILE
EmbCC compiles any number of source files in one command, and links them
with any number of objects, archives and -l libraries (see
Several source files). A command line with no
input file and none of the query options ends with
embcc: error: no input file. Running embcc with no arguments prints the
usage line and exits with status 1.
Options may appear in any order, before or after the input file, with the
exceptions noted under --lang and
--emit-empty-object. An option that takes a
value accepts it after an = (--cpu=cortex-m4) or as the next argument
(--cpu cortex-m4). POSIX's letters take theirs attached or next, as POSIX
says (-o FILE, -DNAME).
What to build#
Input files#
The driver decides what to do with the input file from its suffix, unless
-x says otherwise.
| Suffix | Treated as |
|---|---|
.c |
C source |
.cc .cpp .cxx .C .c++ .cp .CPP .ii |
C++ source (lowered to C internally; see C++) |
.s |
GNU-syntax assembly, assembled for the selected target without preprocessing |
.S |
GNU-syntax assembly, preprocessed first, with __ASSEMBLER__ defined |
.asm |
NASM/Intel-syntax x86-64 assembly (the embas assembler) |
.ir |
EmbIR text; meaningful only to embcc inspect ir |
.o .obj .a |
an object or an archive, handed to the link as it is |
Several source files#
embcc --opt=speed main.c uart.c timer.c -lm -o prog # compile all three, link
embcc --opt=speed -j4 -c *.c # main.o uart.o timer.o ...
Each source is compiled by a run of embcc of its own, with the same
options, as GCC's driver runs its compiler proper. Without -c, -S or
-E the objects are temporaries beside the output (OUT.embcc-tmp-N.o),
linked in command-line order among the other inputs and then removed;
with -c or -S each source gets its own NAME.o or NAME.s in the
working directory, and -o is refused, since it names one file; with
-E they are printed one after the other. If any source fails, nothing
is linked and the temporaries are removed. --syntax-only and --fix
check each one. embcc inspect, --why, --emit-c and
--emit-interfaces take one source.
-j N (or -jN) compiles N sources at once; -j alone, one per
processor. The default is one at a time, so the diagnostics come out in
command-line order. The image does not depend on -j.
This needs a host that can run a program. EmbCC built with
PROCESS=none -- as it is for EmbLinkOS, which has no fork/exec --
compiles one source per command, and refuses a second with
embcc: error: more than one source file ('a.c' and 'b.c'), and this host cannot run a compiler for each (EmbCC was built with PROCESS=none): compile each with -c and link the objects.
Anything else, including .i and .h, is not an input file to EmbCC.
Without -x such an argument is refused as
embcc: error: unknown argument 'NAME'. EmbCC does not read source from
standard input; - is not accepted as a file name.
What happens to the input depends on the mode options:
| Mode | Input | Result | Default output |
|---|---|---|---|
| none | C or C++ | compiled and linked (x86-64 ELF, and ARM, RISC-V, MIPS and AVR firmware with a memory map) | a.out |
| none | .s .S |
assembled and linked, as above | a.out |
| none | only objects, archives and -l |
linked | a.out |
-c |
C or C++ | relocatable object | the input's file name with its suffix replaced by .o |
-S |
C or C++ | assembly text | the input's file name with its suffix replaced by .s |
-E |
C, C++ or .S |
preprocessed text | standard output |
--syntax-only |
C or C++ | nothing; diagnostics only | none |
-c |
.s .S .asm |
object | see Assembling and linking |
| any | .asm |
object (NASM syntax is never linked by the driver) | see Assembling and linking |
As with GCC, a default output is written in the current directory, not
next to the input: embcc -c src/foo.c writes foo.o, and
embcc -S src/foo.c writes foo.s.
A C++ input is compiled for every target but AVR: with exceptions on
x86-64 and AArch64, and with --no-exceptions on the embedded targets
(see C++ targets). For AVR, every mode but -E, -M,
--deps-only and --syntax-only stops with embcc: error: C++ is not yet supported for avr: the C++ lowering assumes a 32-bit int and 4-byte pointers ....
When several mode options are given, -E takes precedence over the
others, then --syntax-only, then -S, then -c.
-c#
Compile the input to a relocatable object and do not link. The object
format follows the target: ELF for the bare-metal, EmbLinkOS and Linux
targets, Mach-O for the -apple-darwin targets and COFF for
x86_64-windows-gnu (see Targets).
-S#
Write the code the backend generated as GNU assembler text instead of an
object. Supported on every target. Without -o the output is
NAME.s in the current directory; -o - writes it to standard output.
-S implies -c.
Each instruction is written as its encoded bytes in a .byte directive,
with every relocation attached explicitly through .reloc, so that
assembling the file reproduces the object -c writes. On x86-64 each
line also carries the disassembly as a comment; the other targets show
the bytes only. The file is a record of what was compiled, not a starting
point for hand editing.
add:
.byte 0x03,0xfe # add %esi,%edi
.byte 0x48,0x89,0xf8 # mov %rdi,%rax
.byte 0xc3 # ret
.size add, .-add
File-scope asm blocks and naked functions are written the same way on
Cortex-M, RISC-V and AVR: their bytes, their .globl and .weak labels
typed as the object types them, and a .reloc for every field they
relocate. A label the object does not export is written as a local label
of its block, .Lasm<N>.<name>. On Cortex-M a relocated data word is
written .long sym+N instead, because an ARM assembler keeps a
relocation's addend in the word, where .reloc does not put it. EmbCC
reads the file back (embcc -c FILE.s), and so does llvm-mc.
-E#
Preprocess the input and write the result to standard output, or to
FILE with -o FILE, as GCC does. -E applies to C, C++ and .S
files. For a .asm file it is refused with
embcc: error: -E does not apply to assembly.
Combined with --deps-only or -M, -E prints only the dependency rule (see
--deps-only).
With -dM, -E prints every macro defined at the end of the file
instead of the text. That covers the predefined macros, those from -D,
and the file's own, one #define each, as GCC prints them. GCC promises
no order, and neither does EmbCC.
Standard input. The input - is read from standard input, so the
usual way to ask a compiler for its macros works:
echo | embcc --target=thumbv7em-none-eabi -E -dM -. As with GCC,
standard input is C under -E, and otherwise needs -x to say what it
is: -E or -x required when input is from standard input.
-o FILE#
Write the output to FILE. The argument is the next word on the command
line; -oFILE is not accepted. If -o is the last argument the driver
stops with embcc: -o needs a FILE. When -o appears more than once, the
last one wins.
-o - means standard output for the text-producing modes (-E, -S,
--emit-interfaces, --emit-c). An object cannot be written to standard
output, and -c -o - is refused:
embcc: error: `-o -` writes to stdout, which -E, -S and --emit-interfaces support but an object file does not; name a file
-j N, -jN, -j#
With several source files, compile N of them at once; -j alone, one
per processor. The default is one at a time. The objects and the image
are the same whatever N is; only the order the diagnostics come out in
can differ. See Several source files.
--lang=LANGUAGE#
GCC: -x LANGUAGE.
Treat the input as LANGUAGE regardless of its suffix. The language may
follow an = (--lang=c++) or be the next argument (--lang c++); GCC's
-x takes it attached (-xc++) or next (-x c++).
LANGUAGE |
Meaning |
|---|---|
c, cpp-output |
C |
c++, c++-cpp-output |
C++ |
none |
go back to deciding by suffix |
assembler |
GNU assembly, as a .s file is |
assembler-with-cpp |
GNU assembly, preprocessed first, as a .S file is |
The two assembly languages are what a CubeMX-generated Makefile passes:
it assembles its startup_*.s with gcc -x assembler-with-cpp, so the
lowercase .s is preprocessed. An object or an archive given in the same
command (.o, .a, .obj) is still linked, not assembled.
Any other value is refused with embcc: error: unknown language 'LANGUAGE' for -x (c, c++, assembler or assembler-with-cpp).
Unlike GCC's -x, --lang applies to the one input file wherever it appears. The
exception is an input whose suffix EmbCC does not recognize (prog.txt,
prog.i): such a file is only taken as the input when --lang=c or
--lang=c++ comes before it on the command line.
--syntax-only#
GCC: -fsyntax-only.
Run the preprocessor, parser and semantic analysis, report diagnostics,
and write nothing. Implies -c. The exit status is 1 if any error was
reported.
--keep-temps, --keep-temps=cwd, --keep-temps=obj#
GCC: -save-temps, -save-temps=cwd, -save-temps=obj.
Honoured. Keep the preprocessed source (.i, or .ii for C++) and the
assembly (.s) of each C or C++ source, named as GCC 11 and later name
them:
| Command | Files kept |
|---|---|
embcc --keep-temps -c x.c -o build/x.o |
build/x.i, build/x.s |
embcc --keep-temps -c x.c |
x.i, x.s |
embcc --keep-temps -S x.c -o build/y.s |
build/y.i (the .s is the output) |
embcc --keep-temps x.c y.c -o fw.elf |
fw-x.i, fw-x.s, fw-y.i, fw-y.s |
--keep-temps=obj is the same as --keep-temps. --keep-temps=cwd puts
the files in the current directory, named as above without the
directory. GCC's -dumpbase NAME names them NAME.i and NAME.s. EmbCC
passes nothing between its stages through files, so each is produced by
running the compiler again on the same source with -E and with -S;
the .s is the assembly of the object the command writes. A host that
cannot run a program (EmbCC built with PROCESS=none) refuses the
option. Nothing is kept for assembly inputs.
--emit-c#
For a C++ input, write the C that EmbCC lowers the C++ unit to, instead
of compiling it. The output goes to -o FILE or to standard output. For a
C input the driver stops with embcc: error: --emit-c lowers C++; 'FILE' is C.
--emit-interfaces#
Write the unit's interface record instead of an object: one line per
entity the unit provides or uses, each with a stable name (a USR) and a
hash of what dependents can observe of it. The output goes to -o FILE
or to standard output; --emit-interfaces implies -c. The record is
the input to the embidx cross-unit index.
; EmbCC interfaces v1
; t.c
file 21c568a73b4612ce t.c
provides c:@F@add 5336311b8eae8c73
A weak definition's provides line ends in weak (see
embidx).
--emit-empty-object FILE#
Write a valid relocatable ELF object with an empty .text section to
FILE. This option must be the first argument and FILE the only other
one; anything else is refused (embcc: --emit-empty-object needs a FILE,
or unknown argument when other options are present). The object is for
the configured default target, since
--target= cannot be combined with it. A default target whose object
format is not ELF or COFF is refused with
embcc: error: no object writer for Mach-O yet, which is what 'TRIPLE' needs.
Exit status#
embcc exits with status 0 when the requested output was produced and no
error was reported, and 1 otherwise. A warning promoted by --warnings-are-errors or
--error=NAME counts as an error, and the compile then leaves no output
file (see --warnings-are-errors). With --fix, the status is 0
when at least one fix was applied.
The language#
--std=STANDARD#
GCC: -std=STANDARD.
Name the language standard. For C, each standard is compiled as it is:
--std= value |
Standard | __STDC_VERSION__ |
|---|---|---|
c89, c90, iso9899:1990, gnu89, gnu90 |
C89/C90 | not defined |
iso9899:199409 |
C94: C89 with its first amendment | 199409L |
c99, c9x, iso9899:1999, gnu99, gnu9x |
C99 | 199901L |
c11, c1x, iso9899:2011, gnu11, gnu1x |
C11 | 201112L |
c17, c18, iso9899:2017, iso9899:2018, gnu17, gnu18 |
C17 | 201710L |
c23, c2x, iso9899:2024, gnu23, gnu2x |
C23 | 202311L |
c2y, gnu2y |
C2y, the standard after C23, as drafted | 202500L |
The default is gnu17. A cNN name is the standard alone and defines
__STRICT_ANSI__; a gnuNN name adds GNU's extensions
(GNU extensions). What the standard changes:
- Keywords. Before C23,
bool,true,false,nullptr,static_assert,alignas,alignof,thread_local,typeof_unqualandconstexprare ordinary names under acNNstandard.asmis a keyword only undergnuNN, andtypeofundergnuNNor from C23. In strict C89,inlineandrestrictare names too. - EmbCC's GNU dialects before C23 also have C23's keywords,
autotype inference, digit separators (1'000) andu8'c', as EmbCC always has; GCC's have them only fromgnu23.--pedantictakes them away. - C89. Strict
c89has no//comments. In bothc89andgnu89aforloop cannot declare its variable, a declaration with no type is anint(static x;,auto w = 4;,f() { ... }), and a call to a function nothing declares declares itextern int f();. - Before C23,
int f()declares a function with no prototype: its calls are not checked against it and their arguments are promoted. From C23 it meansint f(void). Old-style (K&R) definitions are compiled under every standard. - Trigraphs (
??=for#, and the other eight) are read under a strict standard before C23,c89toc17, as GCC reads them.
C2y's _Countof and 0o17 octal constants, and C23's binary constants,
are compiled under every standard, as GCC compiles them; --pedantic
reports them before the standard that has them.
Every case of tests/std/c-corpus.txt is compiled under each standard,
as it is and with --pedantic-errors, and accepted or refused as GCC 16
decides (tools/std-check.py). Where EmbCC decides otherwise on purpose,
the case is listed with the reason in tests/std/c-expect.txt.differs.
For C++, the accepted names are c++NN and gnu++NN, where NN is one
of 98, 03, 11, 0x, 14, 1y, 17, 1z, 20, 2a, 23, 2b,
26 and 2c. These set the standard macros that libraries choose code
by: __cplusplus (199711L, 201103L, 201402L, 201703L, 202002L,
202302L, or 202400L for C++26), the feature-test macros, and
__STRICT_ANSI__ for the c++NN spellings. And they decide the
program's language, as GCC's do: the keywords (C++11's constexpr and
nullptr are names in C++98, C++20's concept and requires before
C++20), the tokens (digit separators from C++14, u8'c' from C++17,
<=> from C++20, u8"" of type const char8_t[] from C++20), and the
constructs each standard introduced: in C++98 auto is a storage class
and >> does not close two template argument lists, deduced return types
and generic lambdas are C++14's, template<auto> and class template
argument deduction C++17's, using enum C++20's. An older standard
refuses a newer construct by name:
embcc: f.cc:3:1: error: class template argument deduction is C++17's, and the standard here is C++14
A system header is read with all of EmbCC's C++ whatever --std= says:
EmbCC's own C++ library is C++20 throughout. The cases are checked
against GCC 16 in tests/std/cxx-corpus.txt.
The default for C++ is equivalent to --std=gnu++20.
One C standard changes code generation: c89, c90, iso9899:1990,
gnu89 and gnu90 select GNU89 inline semantics, as
-fgnu89-inline does.
--char=signed, --char=unsigned#
GCC: -fsigned-char, -funsigned-char.
Make plain char signed or unsigned, overriding the target's default
(signed on x86-64, macOS on AArch64, and AVR; unsigned on the other
AArch64 targets, ARM and RISC-V; see Targets). Honoured: the
predefined macro __CHAR_UNSIGNED__ is defined or removed to match. The
last of the two on the command line wins.
--wchar=short, --wchar=int#
GCC: -fshort-wchar, -fno-short-wchar.
--wchar=short makes wchar_t an unsigned short on every target, as
GCC's and Clang's option does. UEFI code and ARM code written for UTF-16
strings use it. What changes:
- The type.
wchar_tisunsigned short, in C and C++, and anL""literal has 16-bit elements. A code point above U+FFFF is a surrogate pair, as in au""literal. - The macros, as Clang has them:
__WCHAR_TYPE__isunsigned short,__WCHAR_MAX__65535,__WCHAR_MIN__0,__WCHAR_WIDTH__16,__SIZEOF_WCHAR_T__2,__WCHAR_UNSIGNED__1, and on ARM__ARM_SIZEOF_WCHAR_T2. So<wchar.h>'sWCHAR_MAXis 65535. - ARM objects record a 2-byte
wchar_tin their build attributes (Tag_ABI_PCS_wchar_t).
lib/libc is built with the target's own wchar_t, so its wcs*
functions do not work on 16-bit strings. This is the same as GCC with a
newlib built without the option. The last of the two options on the
command line wins.
--freestanding#
GCC: -ffreestanding.
Accepted. EmbCC makes no hosted assumptions to turn off: it treats no
library function name as a builtin. __STDC_HOSTED__ stays defined as 1
on every target, with or without this option.
--no-builtins, --no-builtin=NAME#
GCC: -fno-builtin, -fno-builtin-NAME.
Accepted. EmbCC recognizes no library function name (memcpy, printf)
as a builtin; only the __builtin_ spellings are builtins. A call to
memcpy is a call, so the per-name form asks for nothing more.
--wrap-overflow#
GCC: -fwrapv.
Accepted. Signed integer overflow wraps in two's complement on every
target, and no optimization assumes it cannot happen. -fno-strict-overflow
says the same and is accepted too. -fno-wrapv, -ftrapv and
-fstrict-overflow are not accepted.
--strict-aliasing, --no-strict-aliasing#
GCC: -fstrict-aliasing, -fno-strict-aliasing.
Accepted. EmbCC's alias analysis is not type-based, so code is compiled
as --no-strict-aliasing would compile it in either case.
--common, --no-common#
GCC: -fcommon, -fno-common.
--no-common is the default: a tentative definition (int x; at file
scope, with no initializer) is a definition in .bss, and two units
that both have one do not link (embld: multiple definition of 'x').
--common is honoured for C on the ELF targets. A tentative definition
is emitted as a COMMON symbol (SHN_COMMON, its value the alignment),
as GCC emits it, and embld merges every unit's COMMON symbols of one
name into one object with the largest size and the strictest alignment,
or drops them for a real definition elsewhere. A static, thread-local
or weak tentative definition, or one with a section attribute, stays a
definition, as in GCC. A linker script must place *(COMMON) (in
.bss, where the startup code zeroes it); embld says so if it does
not. -S writes .comm NAME,SIZE,ALIGN. C++ has no tentative
definitions, so the option changes nothing there. For a Mach-O or COFF
target it is refused: embcc: error: --common is not supported for x86_64-apple-darwin: EmbCC writes COMMON symbols into ELF objects only, and this target's are Mach-O.
--enums=short, --enums=int#
GCC: -fshort-enums, -fno-short-enums.
--enums=short makes every enumeration the smallest integer type that
holds all its values: unsigned char, signed char, unsigned short,
short, or the type the values need, unsigned when no value is
negative. That is how arm-none-eabi-gcc lays enumerations out by
default, and how __attribute__((packed)) lays out one enumeration. It
changes the layout of every structure that holds an enumeration, so it
is an ABI choice: a Cortex-M object says which in Tag_ABI_enum_size (1,
against 2 for int-sized ones), embld refuses to link the two kinds
together, and ACLE's __ARM_SIZEOF_MINIMAL_ENUM is 1. C only: for a C++
input it is refused (supported for C, not C++). --enums=int is
the default: an enumeration is int-sized unless its values need a wider
type.
--enums=neutral#
Also accepted: -fenum-size-neutral (EmbCC's earlier spelling).
EmbCC's own. The object makes no claim about the size of an enumeration
(Tag_ABI_enum_size 0), so it links with objects built either way. For a
library whose interface passes no enumeration type: EmbCC's libc and
compiler runtime are built with it.
--float-constants=single, --float-constants=double#
GCC: -fsingle-precision-constant, -fno-single-precision-constant.
Honoured, for C. A floating constant without a suffix has type float
and the value it has as a float, as with GCC's option:
sizeof(1.0) is 4, x * 0.1 with float x is a float multiply, and
double d = 0.1; holds 0.1f widened. A constant beyond the range of
float becomes an infinity, and one too small becomes zero, with a
warning (floating constant exceeds the range of 'float'). L-suffixed
constants are unchanged. For a C++ input it is refused (supported for C, not C++), because the C++ front end types its own constants.
--float-constants=double is the default.
--char8#
GCC: -fchar8_t.
C++ only. Define __cpp_char8_t when the standard selected by --std= is
earlier than C++20. In C++20 mode char8_t is a keyword already.
--exceptions, --no-exceptions#
GCC: -fexceptions, -fno-exceptions.
C++ exceptions are on by default. --no-exceptions turns them off as
g++'s flag does. --exceptions turns them on and also requests unwind
tables, which makes it useful for C code that C++ exceptions must unwind
through (see unwind tables).
--rtti, --no-rtti#
GCC: -frtti, -fno-rtti.
Run-time type information is on by default for C++. --no-rtti turns it
off.
--access-control, --no-access-control#
GCC: -faccess-control, -fno-access-control.
--no-access-control stops enforcing private and protected in C++,
as GCC's option does. -faccess-control restores the default.
Language options that are not accepted#
-fms-extensions, -fno-asm and
-fvisibility=... are not accepted (unknown argument).
Headers and macros#
The preprocessor itself is described in C language and Extensions.
-D NAME, -D NAME=VALUE#
Define a macro, as #define NAME 1 or #define NAME VALUE. The argument
may be attached (-DNAME) or the next word (-D NAME). With no argument
the driver stops with embcc: -D needs a macro name.
-U NAME#
Remove a predefined or earlier -D definition of NAME. -D and -U
are applied in command-line order: -DFOO -UFOO leaves FOO undefined,
and -UFOO -DFOO leaves it defined.
--include=FILE#
GCC: -include FILE.
Read FILE as if #include "FILE" were the first line of the source
file. FILE is looked for in the working directory first, as GCC does,
then on the include path. Several are read in the order given. A file
that cannot be found is refused:
cannot find "FILE", which --include names to read before the source.
-I DIR#
Add DIR to the list of directories searched for headers. The argument
may be attached (-Iinclude) or the next word. With no argument the
driver stops with embcc: -I needs a directory.
--system-include=DIR#
GCC: -isystem DIR.
Add DIR as a system header directory: searched in command-line order
with the -I directories, but its headers do not produce warnings (unless
--warn-in-system-headers) and are left out of the rules --deps and --deps-only write.
--no-std-include#
GCC: -nostdinc.
Do not search EmbCC's C library, C++ library and freestanding header directories. See the note below on the one directory this does not remove.
Search order#
For #include "file" the directory of the including file is searched
first. Then, for both forms of #include, the directories are searched
in this order:
- every
-Iand--system-includedirectory, in command-line order; - the directory
includebeside the path the compiler was invoked as: for/opt/embcc/bin/embccthat is/opt/embcc/bin/include; for a bareembccfound throughPATHit is./includein the current working directory; - unless
--no-std-includeis given, EmbCC's own directories, all treated as system directories: the C++ library headers, the C library headers, and the freestanding headers (<stddef.h>,<stdarg.h>,<stdint.h>,<limits.h>,<float.h>and the rest).
Directory 2 is searched as a system directory and is added even with
--no-std-include. In a build tree it is EmbCC's freestanding header directory,
so <stddef.h> and <stdint.h> remain reachable under --no-std-include there.
When embcc is run through PATH from a directory that has an include
subdirectory, headers there are found as system headers.
Project headers therefore take precedence over EmbCC's: a project that
ships its own <stdio.h> in an -I directory gets its own.
The search list holds at most 512 directories -- a vendor SDK's build
gives a hundred or so, one per component (the Pico SDK's does). EmbCC's
four are appended only while there is room, so with more than 508
-I/--system-include options some of them are left out without a
message, and a 513th -I is refused with embcc: too many -I directories (embcc: too many include directories for
--system-include).
How the compiler finds its own files#
EmbCC locates its headers and per-target libraries relative to its own
executable, so a build tree and an installation both work without -I:
- If the environment variable
EMBCC_PREFIXis set and$EMBCC_PREFIX/lib/embcc/VERSION/include/stdio.hexists, that installation is used. If the variable is set and the file does not exist, the driver printsembcc: EMBCC_PREFIX=DIR has no lib/embcc/VERSION/includeand goes on to the next step. - If the directory containing the executable has
lib/libc/include/stdio.h, it is a build tree, and itslib/andbuild/directories are used. - Otherwise, if
BINDIR/../lib/embcc/VERSION/include/stdio.hexists, that installation is used.
VERSION is the string --version prints (0.0.1-beta). The
executable's own path is read from the operating system on macOS and
Linux; on other hosts none of the steps after the first can apply, and
every header needs an explicit -I. --print-search-dirs shows the
result. The installed layout is described in
Getting started.
Preprocessor options that are not accepted#
-imacros, -iquote, -idirafter,
-iprefix, -dM, -dD, -C, -CC, -P, -H, -MG,
-trigraphs and -undef are not accepted either. Use --dump-predef
to list predefined macros.
The target#
Targets covers each family in depth, and the target list names every target.
--target=TARGET#
Generate code for TRIPLE. One embcc binary contains every backend, so
any accepted triple can be named on any compiler. The triple selects the
backend, the data model and ABI, the predefined macros, the object
format, and the library directory used for linking.
A triple EmbCC does not know is refused, with the full list:
embcc: error: unknown target 'bogus'
embcc: the targets it emits for are:
embcc: x86_64-elf
...
The accepted triples, with the canonical spelling first. The canonical
name is what --print-target, --version and diagnostics print.
| Canonical | Also accepted | Architecture | Environment | Object |
|---|---|---|---|---|
x86_64-elf |
x86_64, x86_64-none-elf |
x86-64 | bare metal | ELF64 |
aarch64-elf |
aarch64, arm64, aarch64-none-elf |
AArch64 | bare metal | ELF64 |
thumbv7m-none-eabi |
thumbv7m, armv7m-none-eabi, arm-none-eabi |
ARMv7-M (Cortex-M3) | bare metal | ELF32 |
thumbv7em-none-eabi |
thumbv7em, armv7em-none-eabi |
ARMv7E-M (Cortex-M4/M7), soft float | bare metal | ELF32 |
thumbv7em-none-eabihf |
ARMv7E-M with FPv4-SP-D16 (FPv5-D16 with --cpu=cortex-m7), hard float |
bare metal | ELF32 | |
thumbv8m.main-none-eabi |
thumbv8m.main, thumbv8m-none-eabi, armv8m.main-none-eabi |
ARMv8-M Mainline (Cortex-M33), soft float | bare metal | ELF32 |
thumbv8m.main-none-eabihf |
ARMv8-M Mainline with FPv5-SP-D16, hard float | bare metal | ELF32 | |
riscv32-unknown-elf |
riscv32-none-elf, riscv32, riscv32-elf, rv32 |
RV32 | bare metal | ELF32 |
riscv64-unknown-elf |
riscv64-none-elf, riscv64, riscv64-elf, rv64 |
RV64 | bare metal | ELF64 |
avr |
avr-none-elf, avr-elf, avr-unknown-none |
AVR (ATmega328P) | bare metal | ELF32 |
mipsel-none-elf |
mipsel-unknown-elf, mipsel-elf, mipsel |
MIPS32r2, little-endian, o32 soft float | bare metal | ELF32 |
x86_64-emblink |
x86-64 | EmbLinkOS | ELF64 | |
aarch64-emblink |
AArch64 | EmbLinkOS | ELF64 | |
x86_64-linux-gnu |
x86_64-linux |
x86-64 | Linux, static | ELF64 |
aarch64-linux-gnu |
aarch64-linux |
AArch64 | Linux, static | ELF64 |
x86_64-apple-darwin |
x86_64-darwin |
x86-64 | macOS | Mach-O |
aarch64-apple-darwin |
arm64-apple-darwin, aarch64-darwin |
AArch64 | macOS | Mach-O |
x86_64-windows-gnu |
x86_64-w64-mingw32 |
x86-64 | Windows | COFF |
The ARM sub-architecture also follows from --cpu= and the float ABI, so
--target=thumbv7m-none-eabi --cpu=cortex-m4 reports itself as
thumbv7em-none-eabi. Every compile for x86_64-windows-gnu prints the
--warn=windows-abi warning, because the Windows calling convention is not yet
complete. What each target supports is in Targets.
The default target#
With no --target=, the target is chosen in this order:
- the environment variable
EMBCC_DEFAULT_TARGET, if set and not empty; - the
DEFAULT_TARGETthe compiler was built with (make DEFAULT_TARGET=riscv32-unknown-elf; see Getting started); x86_64-elf, or in a compiler built without x86 (make TARGETS=; see Choosing the targets) the first family it has:thumbv7m-none-eabiforTARGETS=arm.
--target= on the command line overrides both. A default that is not an
accepted triple stops every invocation, naming where it came from:
embcc: error: the default target 'bogus' is not one EmbCC knows
embcc: it came from EMBCC_DEFAULT_TARGET in the environment
embcc: the targets it emits for are:
...
embcc --help and embcc --version print the default this compiler
uses, and embcc --print-target prints the target in effect.
Options for every target#
--endian=little, --endian=big#
GCC: -mlittle-endian, -mbig-endian.
A target has one byte order, which its triple names, so either option is
accepted when it says what the target is and refused when it does not:
embcc: error: --endian=big is not supported for x86_64-elf, which is little-endian. The big-endian targets are PowerPC, SPARC, ColdFire, and
MIPS under the mips-none-elf and mips64-none-elf triples; the others
are little-endian (Target list).
x86-64 options#
--no-sse, --general-regs-only#
GCC: -mno-sse, -mno-sse2, -mgeneral-regs-only.
Generate no SSE instructions, for code that runs before the kernel has
enabled SSE. Floating-point arithmetic needs SSE on x86-64, so a unit
that uses it is refused: floating point needs SSE, which --no-sse forbids. On AArch64, --general-regs-only (and --no-sse) similarly
keeps code out of the floating-point registers, and floating point is
refused with floating point used under --general-regs-only (in 'f').
On the other targets these options are accepted and have no effect.
--no-red-zone#
GCC: -mno-red-zone; also -mno-mmx and -mno-80387.
Accepted on every target and without effect: EmbCC never uses MMX or the x87 register stack for ordinary code, and never uses the red zone.
--code-model=MODEL#
Accepted on every target, with any MODEL, and without effect. On
x86-64 the code model EmbCC uses already suits a kernel linked in the
upper 2 GiB of the address space.
ARM options#
These options apply to the Thumb targets. On any other target each one is
refused: --cpu=cortex-m4 is an ARM option, and the target is x86_64-elf
(--abi= is the RISC-V option of that name on RISC-V, below).
--isa=thumb#
Accepted. Thumb is the only instruction set a Cortex-M has.
--isa=arm#
Refused: --isa=arm is not supported: a Cortex-M has no ARM instruction set, only Thumb.
--cpu=CPU#
Select the processor. cortex-m0, cortex-m0plus and cortex-m1
select ARMv6-M (Thumb-1) code on any ARM triple. cortex-m4 and
cortex-m7 select ARMv7E-M (with the DSP extension), cortex-m3
ARMv7-M, and cortex-m33 ARMv8-M Mainline with the DSP extension, on any
Thumb triple, as clang takes them: the part has one architecture, whatever
the triple said. The DSP extension defines __ARM_FEATURE_DSP and lets
inline asm and .s files use its instructions
(Inline assembly).
cortex-m23 selects ARMv8-M Baseline on any ARM triple, as
thumbv8m.base-none-eabi does (Targets).
--isa=ARCH, --tune=CPU#
GCC: -march=ARCH, -mtune=CPU.
--isa= selects the level as --cpu= does:
armv6-mandarmv6s-mgive ARMv6-M;armv7-mgives ARMv7-M, andarmv7e-mARMv7E-M;armv8-m.basegives ARMv8-M Baseline, andarmv8-m.mainARMv8-M Mainline.
GCC's extensions are accepted after +:
+fpnames the architecture's single-precision unit: FPv4-SP-D16, or FPv5-SP-D16 on ARMv8-M;+fp.dpnames FPv5-D16, and+nofpnone;+dspand+nodspapply onarmv8-m.main, which without+dsp(as the barethumbv8m.main-none-eabitriple) has no DSP extension.
The unit is used when --fpu= is not given or is auto, and
--float-abi= decides whether it is used at all, as with GCC. Any other
architecture or extension is refused by name.
--tune= chooses a core to schedule for, which EmbCC does not do. Any
Cortex-M core is accepted and changes nothing; anything else is refused.
On armv7a-none-eabi, --isa=armv7-a and any --tune=cortex-a* are
accepted.
Any other CPU is refused: --cpu=cortex-m55 is not a part EmbCC knows: it emits ARMv6-M (cortex-m0, m0plus, m1), ARMv8-M Baseline (cortex-m23), ARMv7-M and ARMv7E-M (cortex-m3, m4, m7) and ARMv8-M Mainline (cortex-m33).
--cmse#
Compile for the Secure state of an ARMv8-M part (TrustZone-M, ACLE's
CMSE): cmse_nonsecure_entry and cmse_nonsecure_call take effect, and
__ARM_FEATURE_CMSE is 3. Only on thumbv8m.main-none-eabi and
thumbv8m.base-none-eabi, and only with soft float. See
Targets.
--fpu=FPU#
Name the floating-point unit. EmbCC generates code for three units: the
single-precision fpv4-sp-d16 (Cortex-M4F, ARMv7E-M) and fpv5-sp-d16
(Cortex-M33, ARMv8-M Mainline), and the double-precision fpv5-d16
(Cortex-M7, ARMv7E-M). none, soft and auto mean no FPU. The unit
must match the architecture, on ARMv7 the architecture must be ARMv7E-M,
and fpv5-d16 is refused with a --cpu= other than cortex-m7. An
ARMv6-M part has no FPU, so every unit, and --float-abi=softfp or
hard, is refused there:
--fpu=fpv4-sp-d16 is an ARMv7E-M unit, and the part is ARMv7-M (a Cortex-M3 has no FPU); add --cpu=cortex-m4
--fpu=fpv5-d16 is the Cortex-M7's double-precision unit, and --cpu=cortex-m4 does not have it; the Cortex-M4F's is --fpu=fpv4-sp-d16
--fpu=fpv5-d16 is not supported on thumbv8m.main-none-eabi: EmbCC emits VFP for the Cortex-M33's unit (--fpu=fpv5-sp-d16) and nothing else: ...
--fpu=fpv5-d16 on thumbv6m-none-eabi: an ARMv6-M core (Cortex-M0, M0+, M1) has no FPU, so floating point is soft and travels in the core registers
With a single-precision unit, float arithmetic is VFP instructions and
double arithmetic is runtime calls. With fpv5-d16 both are VFP
instructions: double arithmetic, comparisons, fabs, negation,
__builtin_sqrt and the conversions to and from float and 32-bit
integers are .f64 instructions, and only the conversions between a
floating type and a 64-bit integer are still calls. __ARM_FP is
0x4 for the single-precision units and 0xc for fpv5-d16.
--fpu= alone generates no FPU instructions; --float-abi= decides
that, as in GCC.
--float-abi=ABI#
ABI |
Effect |
|---|---|
soft |
No FPU instructions; floating point is done by runtime calls. The default, except for the -eabihf triples. |
softfp |
FPU instructions; floating-point arguments and results passed in core registers. Links with soft objects. |
hard |
FPU instructions; floating-point arguments and results passed in s0-s15/d0-d7 (AAPCS-VFP). Does not link with soft objects. |
softfp and hard need an FPU (--float-abi=hard needs an FPU to use: add --fpu=fpv4-sp-d16 (Cortex-M4F), --fpu=fpv5-d16 (Cortex-M7) or --fpu=fpv5-sp-d16 (Cortex-M33)). Any other value is refused:
--float-abi=ABI is not an ARM float ABI: it is one of soft, softfp and hard.
--fpu= and --float-abi= are resolved together after the whole command
line is read, so their order does not matter. An -eabihf triple implies
the part's FPU and --float-abi=hard -- fpv4-sp-d16 for
thumbv7em-none-eabihf, or fpv5-d16 with --cpu=cortex-m7 -- and an
explicit option overrides it.
The choice is recorded in the object's .ARM.attributes, in the
predefined macros (__ARM_FP, __ARM_PCS_VFP, __SOFTFP__), and in the
triple --print-target prints.
--abi=ABI#
aapcs and aapcs-linux are accepted: EmbCC emits the AAPCS (the
AAPCS-VFP variant under --float-abi=hard), and aapcs-linux differs
from it only in making every enumeration int-sized, which EmbCC's are.
The other values (apcs-gnu, atpcs, iwmmxt) are refused:
--abi=apcs-gnu is not supported: EmbCC emits the AAPCS ....
-mthumb-interwork, -mno-thumb-interwork#
Accepted. Interworking is between ARM and Thumb code, and a Cortex-M
runs only Thumb: every call and return is already one a bx or blx
would make.
--unaligned-access, --no-unaligned-access#
--unaligned-access is accepted: it is the ARMv7-M and ARMv8-M default,
and what EmbCC's code relies on. --no-unaligned-access is refused,
because EmbCC cannot keep it: a packed structure's int member is one
ldr at its odd address, and a structure aligned below 4 (packed, or
struct { char c[5]; }) is copied and passed by value a word at a time
from wherever it lies.
--no-unaligned-access is not supported: EmbCC's ARMv7-M code uses word and halfword loads and stores at unaligned addresses (packed struct members; copies and by-value passing of structs aligned below 4), which the architecture allows and this flag forbids
-mslow-flash-data#
Accepted. It asks the compiler to keep constants out of literal pools
in flash, and EmbCC's compiled Thumb code has none: it builds constants
and addresses with movw/movt. (An assembly file's ldr rd, =VALUE
still gets a pool entry where it needs one.)
RISC-V options#
--isa=ISA#
The instruction set, as GCC and clang spell it: rv32 or rv64 (the
target's width), the base i -- or g, which is imafd with Zicsr and
Zifencei -- then single-letter extensions in any order, then _zicsr
and _zifencei. EmbCC emits I, M, A, F, D and C:
- M is required (the code multiplies and divides with
mulanddiv); - A gives the atomics as
lr/scloops and AMOs. Without it, as on an ESP32-C3 (--isa=rv32imc), every atomic exchange, compare-exchange and fetch-and-op is a call to libatomic's sized routines (__atomic_fetch_add_4and its kin), as GCC and clang make it, and an aligned load or store stays one instruction. The program links the runtime and C library built without A, from<triple>/noa(whose routines mask interrupts: see Embedded), and the A extension's macros are left out (__riscv_atomic,__GCC_HAVE_SYNC_COMPARE_AND_SWAP_N, with every lock-free macro 1); - F lets
floatarithmetic, comparisons, square roots and conversions run on the FPU, and D the same fordouble; D needs F; - without C nothing is compressed and
EF_RISCV_RVCis clear.
Anything else is refused by name: --isa=rv32imafcv: the 'v' extension is not supported, --isa=rv32imadc: the D extension needs F. The
default is rv32imac and rv64imac. The predefined macros follow
(__riscv_f, __riscv_d, __riscv_flen, __riscv_fdiv,
__riscv_fsqrt, __riscv_zicsr, __riscv_zcf, __riscv_zcd, and the
compressed ones only with C), as clang defines them for the same flags.
--abi=ABI#
Where floating point travels across a call: ilp32/lp64 in the
integer registers (__riscv_float_abi_soft), ilp32f/lp64f a float
in fa0-fa7 (__riscv_float_abi_single), ilp32d/lp64d a double
too (__riscv_float_abi_double) -- with the psABI's flattening of small
structs into floating-point registers. Without --abi= it follows
--isa= as clang's does: D gives the double ABI, F alone the single
one. A float ABI wider than the --isa= has FPU is refused
(--abi=ilp32d passes doubles in floating-point registers, and --isa=rv32imafc has no D extension), as are ilp32e and the other
width's ABIs. Objects of two float ABIs do not link (embld refuses them),
and the driver links a hard-float ABI's librt.a and libc.a from
<triple>/<abi> (riscv32-unknown-elf/ilp32f), which make rt-embedded libc-embedded builds.
MIPS options#
These options apply to mipsel-none-elf; on any other target they are
unknown arguments (and --cpu= is an ARM option). EmbCC emits one MIPS
configuration -- MIPS32 Release 2, little-endian, o32, soft float, no
abicalls, no small data -- so each option is accepted when it asks for
that and refused by name when it asks for anything else.
--cpu=CPU, --isa=CPU#
Accepted for a MIPS32 Release 2 core: mips32r2, m4k, m14k,
m14kc, 24kc, 24kf, 24kec, 24kef, 34kc and 74kc. The code is
the same for every one. Any other value, Release 6 and the pre-Release 2
ISAs included, is refused: --cpu=mips32r6 is not a MIPS32 Release 2 core: EmbCC emits MIPS32r2 (mips32r2, m4k, m14k, m14kc, 24kc, 24kf, 24kec, 24kef, 34kc, 74kc).
--abi=32 and the MIPS defaults#
GCC: -mabi=32, -msoft-float, -EL, -mno-abicalls, -G0.
Accepted; each names what EmbCC does anyway. Their opposites are refused:
--abi=n32 is not supported: EmbCC emits the o32 ABI (--abi=32) only
-mhard-float is not supported: EmbCC emits soft-float o32, which passes floating point in the integer registers
-EB is not supported: the MIPS target is little-endian (mipsel) only
-mabicalls is not supported: EmbCC's MIPS code takes addresses absolutely (lui/addiu) and keeps no $gp; it is -mno-abicalls code
-G8 is not supported: EmbCC puts no data in .sdata and addresses nothing through $gp (-G0)
Machine options that are not accepted#
-masm=, -m32 and -m64 are unknown arguments. --mcu= is refused
on every target but AVR, and --cpu=, --isa=, --tune= and --abi=
on a target whose section here and in Targets does not list
them: embcc: error: --isa=x86-64 is not an option for x86_64-elf. The
architecture, ABI and part are selected by the
target triple (and on ARM by --cpu=, --fpu= and
--float-abi=). The RISC-V targets generate the C (compressed) extension
and the integer multiply/divide instructions; the AVR target generates
code for the avr5 architecture of the ATmega328P, and --mcu= names the
part (atmega328p, atmega328, atmega168p or atmega168). See
Targets.
Optimization#
What each level and pass does is described in Optimization. This section lists the options.
--opt=none, -O0#
No optimization. This is the default. The optimizer does not run, so the
per-pass options have no effect at --opt=none. Every
source variable stays in its stack slot; on ARM Cortex-M and RISC-V the
temporaries of expressions get registers (see
Optimization).
--opt=basic, -O1, -O#
Optimize without making the code larger, as GCC's and Clang's -O1 do.
Every backend allocates registers, with tail calls where its conditions
allow them, and the optimizer runs the passes that only remove work:
the local passes, cfg-clean, mem2reg, sroa, sccp, dse,
load-cse, licm, if-convert and div-magic, and inlines a static
function with a single caller and an always_inline function. Static
functions that are unreachable after optimization are not emitted.
--opt=speed, -O2#
Everything in --opt=basic, plus every other pass in the
pass table (the vectorizer on x86-64 only): full
inlining, gcse, pre, unroll, switch-thread, tail-recursion and
idiom.
--opt=max, -O3#
The same as --opt=speed. The output is identical.
--opt=size, -Os, --opt=min-size, -Oz#
Optimize for size: --opt=speed with the vectorize, unroll and
switch-thread passes off, a smaller inlining budget, and size-directed
choices in IR generation and in the backends. --opt=min-size is the same as
--opt=size.
--opt=debug, -Og#
Optimize for debugging: --opt=basic, with every source variable kept in its
stack slot, so a debugger reads each one where the debug information
says it is. See Debugging.
--pass=NAME, --no-pass=NAME#
Also accepted: -fNAME, -fno-NAME (EmbCC's earlier spelling).
Turn one optimizer pass on or off, overriding what the -O level chose.
A pass turned on this way also runs at --opt=basic, which lets one pass be tried
on its own. The pass names are:
| Pass | Default at | What it does |
|---|---|---|
mem2reg |
--opt=basic |
promotes local variables to SSA values |
gcse |
--opt=speed |
dominator-scoped global common-subexpression elimination |
load-cse |
--opt=basic |
global redundant-load elimination |
sccp |
--opt=basic |
resolves constant branches and drops dead blocks |
licm |
--opt=basic |
loop-invariant code motion, loop rotation and strength reduction |
vectorize |
--opt=speed, x86-64 only, not --opt=size |
lane-wise loops |
inline |
--opt=basic (a single caller or always_inline only), --opt=speed |
inlines small callees into their callers |
dse |
--opt=basic |
removes a store a later store overwrites |
div-magic |
--opt=basic |
divides by a constant without a divide instruction |
if-convert |
--opt=basic |
replaces a two-way branch with a select |
cfg-clean |
--opt=basic |
threads jumps and removes unreachable code |
tail-recursion |
--opt=speed |
turns a self tail call into a loop |
idiom |
--opt=speed |
turns a copy or clear loop into memcpy/memory clear |
sroa |
--opt=basic |
splits a private aggregate into scalars |
unroll |
--opt=speed, not --opt=size |
unrolls counted loops |
pre |
--opt=speed |
partial-redundancy elimination |
switch-thread |
--opt=speed, not --opt=size |
jumps from a known state straight to its switch case |
licm-mem |
--opt=basic |
moves loads out of loops and keeps a loop's variable in a register |
A --pass=, --no-pass=, -f or -fno- option that is not a pass name and not listed elsewhere
on this page is an unknown argument. GCC's pass options are accepted only
where this page lists them (below); -ftree-vectorize, for example, is
not.
--inline=declared, --inline=auto#
GCC: -fno-inline-functions, -finline-functions.
--inline=declared is honoured: the inliner takes only functions
declared inline in some declaration, or marked always_inline, at
every level. A function its author did not mark stays a call (and a
symbol in the image) at --opt=speed and --opt=size as at --opt=basic, including a static
function with a single caller, which GCC's option would still inline
(-finline-functions-called-once). embcc why not-inlined reports
not-declared-inline for such a call. C++ member functions defined in
their class are not marked inline in the C the C++ front end lowers to,
so under this option they are not inlined. --inline=auto, the
--opt=speed default, restores the default.
--unroll, --no-unroll#
GCC: -funroll-loops, -fno-unroll-loops, -funroll-all-loops.
The unroll pass, as --pass=unroll and --no-pass=unroll name it. The pass
is on by default at --opt=speed. --unroll turns it on at --opt=basic and
--opt=size too.
--fp-contract=off, --fp-contract=on, --fp-contract=fast#
GCC: -ffp-contract=off, -ffp-contract=on, -ffp-contract=fast.
EmbCC never fuses a multiply and an add into one instruction; an fma
is only ever __builtin_fma's. So off is kept, and on and fast,
which only permit fusing, change nothing.
--fast-math, --no-math-errno#
GCC: -ffast-math, -fno-math-errno.
Accepted and without effect. Both give the compiler permission to relax
IEEE arithmetic or to assume the math library sets no errno; EmbCC
uses neither permission. So are the permissions --fast-math is made
of: -funsafe-math-optimizations, -fno-signed-zeros,
-fno-trapping-math, -ffinite-math-only, -fassociative-math and
-freciprocal-math. __FAST_MATH__ is not defined, so a header
that tests it takes its careful path, as it should for code that is not
compiled fast-math.
--remarks, --remarks=json#
Also accepted: -fremarks, -fremarks=json (EmbCC's earlier spelling).
Report what the optimization passes decided and why. The report is written to standard error after the compile, one line per decision:
w.c:3: remark: inlined 'sq': 4 instructions into f, budget 24 [inline/small-enough]
w.c:1: remark: promoted-to-register 's' [mem2reg/scalar-and-never-addressed]
w.c:1: remark: unrolled 'big': 8 copies of a 5-instruction body, the original kept for the remainder [unroll/counted-loop]
--remarks=json writes the same records as a JSON array of objects with
the keys pass, decision, subject, reason, detail and
location. Most decisions are made only at --opt=speed. Clang's -Rpass=,
-fsave-optimization-record and GCC's -fopt-info are not accepted. See
also embcc why.
Code generation#
--sections=functions, --sections=data#
GCC: -ffunction-sections, -fdata-sections.
Put each function in a section of its own, .text.NAME, and each object
in .data.NAME, .rodata.NAME or .bss.NAME, as GCC names them, so that
the linker's --gc-sections can drop whatever the program never reaches:
embcc --target=thumbv7em-none-eabi --opt=speed --sections=functions --sections=data -c main.c
embcc --target=thumbv7em-none-eabi --link-script=board.ld -Wl,--gc-sections startup.o main.o -o fw.elf
A function or object that names its own section keeps it, and so does a
thread-local object (.tdata/.tbss). String literals stay in the
unit's .rodata. A call from one function to another in the same unit
becomes a relocation, as it is between units; on RISC-V that is the
eight-byte call instead of a four-byte jal, because embld does not
relax. -g works with either option: the unit's code is described by
DW_AT_ranges. ELF output only; for Mach-O and COFF the options change
nothing. They are off by default; GCC's -fno-function-sections and
-fno-data-sections are accepted.
What the linker keeps is in embld.
--unwind-tables#
GCC: -funwind-tables, -fasynchronous-unwind-tables.
Emit unwind tables (.eh_frame) so that a C++ exception can unwind
through the unit's functions. --exceptions has the same effect for C.
The default depends on the language and target. Unwind tables are always
emitted for C++ units (unless both --no-exceptions and
--no-unwind-tables are given). For C they are emitted by default on the
hosted ELF targets (-linux-gnu), where an exception may unwind through
C library frames, and not on the freestanding targets.
--no-unwind-tables#
GCC: -fno-unwind-tables, -fno-asynchronous-unwind-tables.
Do not emit unwind tables for a C unit. For a C++ unit with exceptions enabled, tables are emitted regardless.
--no-frame-pointer, --frame-pointer#
GCC: -fomit-frame-pointer, -fno-omit-frame-pointer.
Accepted and without effect: each backend decides where a function keeps a frame pointer (Frame pointer).
--no-jump-tables, --jump-tables#
GCC: -fno-jump-tables, -fjump-tables.
--no-jump-tables is honoured on every target: no switch is lowered
through a table of addresses, which code that runs before it is
relocated, or from an address other than the one it was linked at,
cannot use. A switch dense enough for a table (n cases, at least
four, whose values span at most 4n + 4; at --opt=size at least six,
spanning at most 2n) becomes the balanced compare tree that sparse
switches always get. The tables this removes are x86-64's indirect jmp,
AArch64's br, ARM's tbb/tbh and RISC-V's jr through a table;
the AVR target never uses one. --jump-tables, the default, turns them
back on.
Warnings and diagnostics#
The diagnostic format, the warnings themselves, --explain and fix-its
are described in Diagnostics. This section lists the
options.
--warn=all, --warn=extra#
GCC: -Wall, -Wextra, -W.
Turn on the warnings in the all or extra set. As in GCC, --warn=extra
does not include --warn=all. -W is GCC's old spelling of -Wextra.
--warn=NAME#
GCC: -WNAME.
Turn on the warning NAME. A name EmbCC does not have is not an error:
embcc: warning: -Wcast-align is not a warning EmbCC has, so it turns nothing on (--help-warnings lists them)
Any --warn=NAME, and any argument beginning with -W that matches
nothing else, is handled this way, including --warn=format=2. -Wa,..., -Wl,... and -Wp,... are
options of their own (see
Assembling and linking and
-Wp,ARGS).
-Wimplicit-function-declaration, --error=implicit-function-declaration
and GCC's older -Werror-implicit-function-declaration are accepted
without a word: in EmbCC a call to an undeclared function is always an
error ('g' is not declared in 'f', E0001), which is what they ask for.
--no-error=implicit-function-declaration cannot make it a warning, and
says so.
--no-warn=NAME#
GCC: -Wno-NAME.
Turn off the warning NAME. An unknown NAME is accepted silently.
--no-error=NAME is a different option; see above.
--no-warnings#
GCC: -w.
Suppress all warnings. Takes precedence over every --warn= option, including
--warnings-are-errors.
--warnings-are-errors, --no-warnings-are-errors#
GCC: -Werror, -Wno-error.
--warnings-are-errors reports every warning as an error, and the compile fails.
--no-warnings-are-errors turns that off again; the last one wins.
A compile that fails because of a warning made an error writes no output:
the object, assembly file or executable it was producing is removed, so
that make does not take it as up to date. A file of that name left by
an earlier compile is removed as well. The dependency file of --deps or
--deps-all and the .su file of --stack-usage are still written.
--error=NAME, --no-error=NAME#
GCC: -Werror=NAME, -Wno-error=NAME.
--error=NAME reports the warning NAME as an error, with or without
--warnings-are-errors, and turns the warning on. --no-error=NAME keeps NAME a
warning under --warnings-are-errors; it does not turn the warning on. Either form
decides for its one warning whatever --warnings-are-errors and --no-warnings-are-errors say, and
wherever it appears on the command line. --no-warnings still suppresses the
warning. A NAME that is not a warning EmbCC has is reported and
ignored:
embcc: warning: -Werror=cast-align names no warning EmbCC has (--help-warnings lists them)
The warnings that no option controls (see
Diagnostics) follow
--warnings-are-errors alone.
--warn-in-system-headers#
GCC: -Wsystem-headers.
Report warnings in system headers. By default a warning whose location is
in a header found through --system-include or through EmbCC's own include
directories is not reported.
--pedantic, --pedantic-errors#
GCC: -pedantic, -pedantic-errors. --warn=pedantic (-Wpedantic) is
--pedantic.
Report what the C standard named by --std= does not
have: a later standard's feature, or a GNU extension. --pedantic
reports each as a warning, and --pedantic-errors as an error:
embcc: f.c:1:9: warning: a binary constant is new in C23, and the standard here is C17 [--warn=pedantic]
embcc: f.c:4:12: warning: a statement expression is a GNU extension, not ISO C [--warn=pedantic]
embcc: f.c:7: warning: an old-style (K&R) parameter list is not C23: that standard removed it [--warn=pedantic]
Either also takes C23's keywords and syntax away from a GNU dialect
before C23, so that --std=gnu17 --pedantic reads what GCC's gnu17
reads.
Reported, before the standard that has it: from C99, long long,
_Bool, _Complex, designated initializers, compound literals, flexible
array members, variable-length arrays, a declaration after a statement,
and // comments in gnu89; from C11, _Static_assert, _Noreturn,
_Thread_local, _Generic, _Alignas, _Alignof, _Atomic and
anonymous struct and union members; from C23, binary constants, [[...]]
attributes, #elifdef, #elifndef, #warning, an empty initializer
{}, an enum's fixed underlying type, a parameter with no name in a
definition, a static assertion with no message, and a label before a
declaration or at the end of a block; from C2y, _Countof, 0o
constants and case ranges. In every standard: statement expressions,
?: with no middle operand, arrays of size 0, a struct or union with no
members, designator ranges, computed goto, a label's address,
__int128 and #include_next; and, from C23, an old-style (K&R)
parameter list.
Nothing is reported in a system header, or after __extension__, up to
the end of the declaration or expression it begins, as in GCC.
For C++ the same options report what the --std= C++ standard does not
have, from C++11's lambdas, long long, [[...]] attributes and
= default in C++98 to C++23's if consteval and explicit object
parameters and C++26's = delete("why"); register from C++17, which
removed it; and GNU's statement expressions, case ranges, ?: with no
middle operand, variable-length and zero-length arrays, flexible array
members and __int128. As for C, not in a system header.
The warnings#
Every warning, what it diagnoses, and the set that turns it on, is in the
warning reference, which the compiler prints from its own
table (embcc --reference=warnings); embcc --help-warnings prints the
same list in short.
For a C++ input, unused-variable, unused-parameter,
unused-function, shadow and sign-compare are always off, even
when named on the command line, because they would be reported against
code the C++ lowering generated.
--diagnostics=FORMAT#
GCC: -fdiagnostics-format=FORMAT.
text (the default) prints caret diagnostics. json prints the
diagnostics as one JSON array in GCC's format, on standard error. Any
other value is refused with embcc: unknown diagnostic format 'FORMAT' (text, json).
--color=WHEN#
GCC: -fdiagnostics-color[=WHEN], -fno-diagnostics-color.
Control color in text diagnostics. --color=always forces it on, and
--color=never turns it off. Any other value, including auto,
selects the default: color when standard error is a terminal. Clang's
-fcolor-diagnostics spelling is not accepted.
--max-errors=N#
GCC: -fmax-errors=N.
Stop after N errors with embcc: compilation terminated due to --max-errors=N. 0, the default, means no limit. Clang's
-ferror-limit= is not accepted.
--explain=ID#
Print the long explanation of diagnostic ID (E0001 and up) and exit.
Every diagnostic that has an explanation prints its ID in brackets. With
no ID, list the IDs that have explanations. An unknown ID ends with
embcc: no explanation for 'ID'; embcc --explain lists what there is
and status 1. See Diagnostics.
--fix#
Apply the fix-its the diagnostics propose, editing the source file in
place, and write no object. Implies --syntax-only. When it changed
something the driver reports embcc: 1 fix applied; compile again (or
N fixes) and exits with status 0; otherwise it reports
embcc: nothing to fix automatically and exits with the compile's
status.
Debugging and reports#
-g, --debug#
Emit DWARF version 4 debug information (.debug_info, .debug_abbrev,
.debug_line) on every ELF target. See Debugging.
-g1, -g2, -g3, -ggdb, -gdwarf, -gdwarf-2, -gdwarf-3 and
-gdwarf-4 are all accepted and mean -g: EmbCC emits one kind of debug
information, so a level or version that asks for no more than DWARF 4
gets exactly that.
-g can change the code generated. On some targets it keeps a frame and
a stack home for each variable, and it disables tail calls on ARMv7-M, so
that the debugger can find every variable. Build with the same -g
setting you ship with if you compare code size.
-g is refused in three situations, each with an error naming the cause:
- for the
-apple-darwintargets (-g is not supported for a Darwin target yet: its DWARF goes in a __DWARF segment this does not write ...); - for
x86_64-windows-gnu(-g is not supported for a Windows target yet: its debug information goes in CodeView records this does not write ...); - for a unit that places a function in a named section with
__attribute__((section))(-g with a function in a section of its own ('NAME') is not supported yet ...).
Refused debugging options#
A DWARF version EmbCC does not emit, split DWARF and compressed debug sections are refused rather than downgraded:
embcc: error: -gdwarf-5 is not supported; EmbCC emits DWARF 4, uncompressed and in one piece
The same message is given for -gsplit-dwarf and -gz. -g0 and
-gline-tables-only are not accepted (unknown argument); to compile
without debug information, omit -g.
--stack-usage#
GCC: -fstack-usage.
Write a stack-usage file beside the output: the output name with its
suffix replaced by .su (foo.o gives foo.su). It has one line per
function that was emitted, in GCC's format:
w.c:3:f 8 static
The fields are FILE:LINE:FUNCTION, the frame size in bytes, and the
qualifier: static, or dynamic for a function whose frame grows at run
time (a variable-length array or alloca), whose number is then only the
fixed part. The frame includes everything the prologue reserves, a
variadic function's register save area among it. Functions that inlining
absorbed or that were dropped as unreachable are not listed.
The .su name is derived from the output file, or, with no -o, from the
source's name in the current directory, as GCC does.
--call-graph, --call-graph=su#
GCC: -fcallgraph-info, -fcallgraph-info=su.
Write the call graph beside the output as FILE.ci, in GCC's format: a
VCG graph with a node per emitted function (with =su, its frame and
qualifier as in --stack-usage), an elliptic node per external callee,
and an edge per call site left after optimisation, labelled with its
location. A call through a pointer is an edge to the
__indirect_call placeholder. A function the inliner absorbed is not a
node: its calls appear under its callers.
graph: { title: "w.c"
node: { title: "main" label: "main\nw.c:5:5\n16 bytes (static)" }
node: { title: "g" label: "g" shape : ellipse }
edge: { sourcename: "main" targetname: "g" label: "w.c:5:18" }
}
A backend's calls to run-time helpers (a soft-float operation, a 64-bit
divide) are not in the graph: they are in the object's relocations.
embrt reads all three -- .su, .ci and the
object -- to bound each entry point's stack.
--time-report#
GCC: -ftime-report.
Print where the compile's time went, phase by phase, on standard error at
exit, as GCC and clang do. The times are processor time (ISO C clock()),
so they mean the same on every host. A phase not listed (writing the
object, linking) is counted in "object and the rest". The output is
otherwise unchanged: the object is the same with or without it.
Execution times (seconds, processor time)
startup : 0.001 ( 0%)
preprocess : 0.039 ( 4%)
parse : 0.006 ( 1%)
semantic analysis : 0.004 ( 0%)
IR generation : 0.002 ( 0%)
optimization : 0.958 ( 91%)
code generation : 0.039 ( 4%)
object and the rest : 0.000 ( 0%)
TOTAL : 1.050
A C++ unit also lists "C++ front end". With several inputs, each phase is the sum over them.
--instrument-functions, --no-instrument-functions#
GCC: -finstrument-functions, -fno-instrument-functions.
GCC's: every function calls
void __cyg_profile_func_enter(void *this_fn, void *call_site);
void __cyg_profile_func_exit(void *this_fn, void *call_site);
on entry and before each return. this_fn is the function's address and
call_site its return address (__builtin_return_address(0)). A return
calls the exit hook after its value is computed. An inlined function
keeps its calls.
Left alone:
- a function marked
no_instrument_functionon any of its declarations; nakedfunctions and interrupt handlers;- the two hooks themselves.
lib/rt defines both hooks, weak, as EmbTrace's recorder: a ring of
events that embtrace_dump() writes out for embtrace on the host. See
embtrace. A program that defines the hooks itself
replaces them.
--instrument-exclude-functions=LIST, --instrument-exclude-files=LIST#
GCC: -finstrument-functions-exclude-function-list=LIST, -finstrument-functions-exclude-file-list=LIST.
LIST is comma-separated.
- Function list: a function whose name is on it, exactly, is not instrumented.
- File list: a function is not instrumented when any item occurs in
the path of the file that defines it. The match is a substring, as
GCC's is, so
lib2matchessrc/lib2.c.
--sanitize=LIST, --no-sanitize=LIST#
GCC: -fsanitize=LIST, -fno-sanitize=LIST.
Insert run-time checks for undefined behavior. LIST is comma-separated;
the checks EmbCC has are:
| Name | Checks |
|---|---|
signed-integer-overflow |
signed +, -, * that overflow |
integer-divide-by-zero |
integer division and remainder by zero |
shift, shift-exponent |
a shift count out of range |
undefined |
all three of the above |
A failed check executes the target's trap instruction: under a debugger
that stops at the faulting operation, and without one the program stops.
There is no diagnosing runtime, so trap mode is the only mode. At --opt=speed
the optimizer removes checks whose operands it can prove safe.
--no-sanitize=LIST removes checks from the set. Options accumulate in
command-line order.
A check EmbCC does not have is refused by name:
embcc: error: -fsanitize=address is not supported: EmbCC's sanitizer inserts checks that TRAP, and this one needs a runtime library to report through. The ones it has are undefined, signed-integer-overflow, integer-divide-by-zero and shift
Dependencies#
A build system that tracks headers asks EmbCC for a make rule naming the headers each source includes.
--deps=FILE, --deps-all=FILE#
GCC: -MMD -MF FILE, -MD -MF FILE.
Compile normally and also write the rule (all headers, or non-system
headers only) to a file named after the object with its suffix replaced
by .d, or to FILE. The rule is written once the unit has
passed semantic analysis.
--deps-only#
GCC: -MM.
Write the make rule for the unit instead of compiling it, leaving out
headers found in system directories (those added with
--system-include and EmbCC's own). GCC's -M keeps them.
--deps-target=TARGET#
GCC: -MT TARGET, -MQ TARGET.
Use TARGET as the rule's target. GCC's two spellings are identical in
EmbCC: -MQ does not quote characters special to make. When given more than
once, the last one is used (GCC would list them all).
--deps-phony#
GCC: -MP.
Also write an empty rule for each header, so that make does not fail
when a header is deleted.
Assembling and linking#
Assembly input#
A .s or .S file is assembled by EmbCC's built-in GNU-syntax assembler
for the selected target: AArch64, ARM (Thumb), RISC-V, MIPS32 or AVR. A .S file
is preprocessed first; -D and -U apply, but the -I and --system-include
directories are not searched (a #include "file" beside the source is
found). The inline-assembly vocabulary of each target is listed in
Inline assembly.
The assembler follows GNU as for each target:
- Comments.
/* ... */anywhere;#starts a comment, except on ARM and AArch64, where it is an immediate's prefix (mov r0, #1) and starts a comment only as a line's first character.@is ARM's comment character and;AVR's (elsewhere;separates statements);//works everywhere. - Macros and expressions.
.macro,.rept,.irp, the.iffamily,.include,.equ/.set/.thumb_set, named sections with flags, and GNU as's expressions; the full list is in embas. - Alignment.
.align Nand.p2align Nalign to 2^N bytes, and.balign Nto N bytes. Padding in code is the target's no-op instruction, and zero bytes in data. - Operand words. A special register, interrupt mask, IT condition, barrier option or floating-point register on ARM, and a CSR name or a fence set on RISC-V, is an operand and never a symbol.
- Symbols. On ARM, a symbol is reached by
bl sym,b sym,ldr rd, =sym(a load from the literal pool, as GNU as makes it),movw rd, #:lower16:sym,movt rd, #:upper16:symand.word sym.ldr rd, labelloads a word from a label defined in the same file..thumb_func, or.type sym, %function, makes a label a Thumb function: its symbol carries the interworking bit, which a vector table entry needs. A symbol the file uses but does not define is an external reference. On MIPS32,jal symandj sym,lui rt, %hi(sym),addiu rt, rs, %lo(sym), a load or store at%lo(sym)(rs),la rt, symand.word sym; a branch reaches only labels in the same section. - MIPS32's modes. A file starts in
.set reorder, GNU as's default: the assembler puts anopin every delay slot..set noreordermakes the instruction after a transfer its slot;.set push/.set popsave and restore the mode..ent,.end NAME,.frame,.mask,.fmaskand.insnare accepted as markers (.endwith a name does not end the file there, as it does elsewhere)..abicalls,.cpload,.cprestore,.gpword,.option pic2and the%got/%call16/%gp_reloperators are refused: EmbCC builds neither PIC nor small-data code. - Sizes.
.size sym, .-symrecords a function's size, which a debugger uses to attribute addresses to it.
For x86-64 targets, GNU-syntax assembly files are refused:
embcc: error: no assembly-file support for x86_64-elf yet; its instruction encoder exists (inline __asm__ works) but this driver has not been wired to it
A .asm file is assembled as NASM/Intel-syntax x86-64 into an x86-64
ELF64 object. This is the same assembler as the standalone
embas. For any target other than x86-64 with ELF
objects, a .asm input is refused:
embcc: error: 'boot.asm' is NASM-syntax x86-64 assembly, which EmbCC assembles to x86-64 ELF only, and the target is thumbv7m-none-eabi
An assembly input always produces an object: -c and -S make no
difference, and no link follows. The one other mode is -E, which
preprocesses a .S file to standard output. Without -o the object
is the input's file name with its suffix replaced by .o, in the
current directory (boot/start.S gives start.o).
--assembler-arg=ARG#
GCC: -Wa,ARGS.
Options for the assembler, separated by commas. The integrated assembler takes no options. Accepted:
--noexecstack,-g, any option beginning with--gdwarf, and-mrelax, which change nothing it writes;- GNU as's listing options
-a[cdghlmns][=FILE], as an STM32CubeMX Makefile passes them (-Wa,-a,-ad,-alms=build/main.lst). The listing is the-Stext of the object's own bytes, written beside the object toFILE, or to standard output when no option names a file. Xtensa and RX, which have no-Stext, warn and write none.
Any other option is refused, and nothing is compiled:
embcc: error: assembler option '-z' is not one the integrated assembler has
The options are checked on every invocation, whatever the input file.
Linking#
Without -c, -S, -E or --syntax-only, embcc FILE -o OUT compiles
the file and links it in the same process with EmbCC's linker,
embld. This is available only for x86-64 ELF targets
(x86_64-elf, x86_64-emblink, x86_64-linux-gnu and their aliases).
For the firmware targets (ARMv7-M, ARMv8-M, RV32, RV64, MIPS32 and AVR)
the driver links too, and the build supplies the memory map: a linker
script with --link-script=FILE.ld (ARM, RISC-V and AVR), or
--linker-arg=-Ttext=FLASH and --linker-arg=-Tdata=RAM (on MIPS32 the message offers only these). There is no default map, because an image linked to a
guessed one runs, wrongly:
embcc: error: linking a thumbv7em-none-eabi image needs its memory map: a linker script (-T FILE.ld), or -Wl,-Ttext=FLASH and -Wl,-Tdata=RAM
embcc --target=thumbv7em-none-eabi --opt=size -c main.c -o main.o
embcc --target=thumbv7em-none-eabi --link-script=STM32F407VGTx_FLASH.ld \
startup.o main.o -o fw.elf
embld does not read AArch64, Mach-O or COFF objects, and for those
targets the driver stops with embcc: error: cannot link for TRIPLE; link
them with the platform's linker.
The link line, in order:
crt1.ofrom the target's library directory, for a hosted target (not with--no-stdlibor--no-start-files; a firmware target has none, because its startup is the program's own);- the object just compiled (written to
OUT.embcc-tmp.obeside the output and removed afterwards; whenemblditself refuses the link, for an undefined symbol or an image over--rom-limit, the temporary object is left in place); - the objects, archives and
-llibraries of the command line, in their order; libcxx.a, for a C++ input;libc.a, if the target has one (not with--no-stdlibor--no-default-libs);librt.a, the compiler runtime, if the target has one (likewise).
-lNAME is libNAME.a in the first -L directory that has it. -lc and
-lm name EmbCC's own libc.a (its math is in it) and -lgcc its
librt.a, so a link line written for arm-none-eabi-gcc that says
--no-stdlib ... -lc -lgcc gets them. A library found nowhere is refused:
embcc: error: cannot find libnosys.a for -lnosys in any -L directory.
For x86_64-linux-gnu the result is a static executable with no
dependence on another C library. A missing crt1.o or libc.a for that
target is reported by name, with what builds it: in a build tree the
make target (embcc: error: no crt1.o for x86_64-linux-gnu: its C library is not built; `make libc-linux-x86_64` in TREE builds it), and
for an installed compiler make install. A missing libcxx.a for a C++
program is reported the same way. For the freestanding x86-64
targets nothing supplies the entry point: the program must define
_start, or be linked with embld and a start file.
The image is written without execute permission; run chmod +x on it
before running it directly.
--linker-arg=ARG#
GCC: -Wl,ARGS, -Xlinker ARG.
Pass ARG to the link as one word, as -Xlinker does. GCC's -Wl,
splits ARGS at its commas, so -Wl,-Ttext,0x200000 is two words.
-Xlinker passes the next argument as one word, and with none the driver stops with embcc: -Xlinker needs an option. The words are kept in command-line order and read when the
driver links. A compile that does not link (-c, -S, -E,
--syntax-only) ignores them, as GCC does.
These are applied, with the meaning of the embld
option of the same name:
| Linker option | Effect |
|---|---|
-Ttext ADDR, -Ttext=ADDR, -TtextADDR, -Ttext-segment ADDR, -Ttext-segment=ADDR |
the address the image starts at (default 0x400000) |
-Tdata ADDR, -Tdata=ADDR, -TdataADDR |
the address of the writable data |
--rom-limit N, --rom-limit=N |
refuse an image whose stored bytes exceed N |
--lma-offset N, --lma-offset=N |
load each segment at its address minus N |
-e SYM, --entry SYM, --entry=SYM |
the entry symbol (default _start) |
-T FILE, -TFILE, --script=FILE |
the GNU ld linker script that lays the image out (ARM and RISC-V) |
-L DIR |
a directory for -l and for the script's INPUT/GROUP/INCLUDE |
-u SYM, --undefined=SYM |
treat SYM as referenced, so that an archive supplies it |
--orphan-handling=place|warn|error |
what to do with a section the script places nowhere |
--gc-sections, --no-gc-sections |
drop the sections nothing kept refers to (see --sections=functions) |
--print-gc-sections |
name each section dropped, on standard error |
-Map FILE, -Map=FILE, --Map=FILE |
write a map of where every input went, in GNU ld's format |
--print-memory-usage |
print how full each MEMORY region of the script is, as ld does |
-Tstack ADDR, -Tstack=ADDR |
passed on: embld emits an entry stub that sets sp (RISC-V and MIPS), and refuses it elsewhere: embld: -Tstack is a RISC-V and MIPS option: ... |
These are accepted and change nothing, because nothing in an image
embld makes depends on them: --as-needed, --no-as-needed, -O0, -O1, -O2, --build-id,
--build-id=STYLE, --no-undefined, -s, --strip-all, -S,
--strip-debug, and -z followed by noexecstack, relro, norelro,
now or lazy as a separate word (-Wl,-z,now). The image keeps its
symbol table under -s and --strip-all.
Any other linker option is refused, and nothing is linked:
embcc: error: linker option '--section-start=.text=0' is not one EmbLD has (it takes -T, -L, -u, -e, -Ttext, -Tdata, -Tstack, --rom-limit, --lma-offset, --orphan-handling, --gc-sections, --print-gc-sections, -Map and --print-memory-usage); dropping it could build a different image from the one asked for
This covers --section-start, -zKEYWORD written as one word, and -z
with any other keyword.
Linker options#
Besides --linker-arg, the driver takes these link
options:
| Option | What happens |
|---|---|
-lNAME, -l NAME |
link libNAME.a from the -L directories, at its place on the line |
-LDIR, -L DIR |
search DIR for -l libraries |
--link-script=FILE, -T FILE |
link with the GNU ld linker script FILE (ARM and RISC-V) |
--entry=SYM, --undefined=SYM (-e, -u) |
the entry symbol; a symbol to treat as referenced |
--no-stdlib |
no crt1.o, libc.a or librt.a |
--no-default-libs |
no libc.a or librt.a |
--no-start-files |
no crt1.o |
--static |
accepted: every image EmbLD writes is static; with --libc=system, glibc's libc.a |
--libc=embcc|system |
the C library: EmbCC's own (the default), or for a Linux target the system's glibc, its headers and shared objects linked by the system's linker (The C library) |
--sysroot=DIR |
the system's root, where --libc=system finds glibc (default /) |
--linker=PATH, --ld-path=PATH, -fuse-ld=NAME |
the linker --libc=system runs |
FILE.so, FILE.so.N |
a shared object, linked with --libc=system; otherwise refused: embcc: error: x.so is a shared object, which EmbLD does not link; --libc=system links a Linux program against the system's |
-specs=FILE, --specs=FILE |
accepted: nano.specs and nosys.specs choose GCC's newlib and its system-call stubs, and EmbCC links its own C library and runtime; a link notes once embcc: note: -specs=nano.specs is ignored: EmbCC links its own C library and runtime, not newlib, and a compile says nothing |
-pthread |
with --libc=system, _REENTRANT and -lpthread; otherwise nothing (EmbCC's C library has no threads, macOS's libSystem has them) |
-rdynamic, -no-pie |
unknown argument |
-shared, -static-pie |
refused: embcc: error: -shared needs position-independent code, which EmbCC does not emit |
Asking EmbCC#
--help, --help=gcc#
Print the usage line and a summary of the options, then exit with status
0. Recognized anywhere on the command line. The summary names the target
this compiler emits for when no --target= is given.
--help-warnings#
Print every warning option EmbCC has and the set (--warn=all or
--warn=extra) each belongs to, then exit with status 0. The
warning reference says what each one diagnoses.
--reference=PAGE#
Print a reference page made from the compiler's own tables, then exit.
options, warnings and targets print the Markdown pages this manual
includes: the option reference, the
warning reference and the target list.
make reference writes them into docs/manual/. man prints embcc(1)
in roff, which make install installs in share/man/man1; it is dated
by SOURCE_DATE_EPOCH when that is set, as reproducible builds ask.
--version#
Print the version, the target in effect, the default target, the
target families this compiler was built with, the languages supported,
which images embld links, and what is not supported yet, then exit with
status 0. Recognized anywhere on the command line; a --target= on the
same command line changes the target it reports:
EmbCC 0.0.1-beta (the Emb toolchain's C and C++ compiler, for embedded systems and operating systems)
Target: x86_64-elf
Default target: x86_64-elf
Targets (every family; make TARGETS= builds fewer):
x86 x86-64: bare metal, EmbLinkOS, Linux, Darwin and Windows
aarch64 AArch64: bare metal, EmbLinkOS, Linux and Darwin
arm Cortex-M (ARMv6-M, ARMv7-M, ARMv7E-M, ARMv8-M Baseline and Mainline) and ARMv7-A
...
A compiler built with make TARGETS= (see
Choosing the targets) lists
only its own families, then the others under Not built:. A --target=
from a family it lacks is refused rather than reported.
--print-target#
GCC: -dumpmachine.
Print the canonical name of the target in effect and exit. The name
reflects --target=, the configured default, and the ARM options that
change the sub-architecture or the float ABI:
embcc --target=arm-none-eabi --print-target # thumbv7m-none-eabi
embcc --target=thumbv7m-none-eabi --cpu=cortex-m4 \
-mfpu=fpv4-sp-d16 -mfloat-abi=hard -dumpmachine # thumbv7em-none-eabihf
All other options on the command line are still checked first, so an invalid ARM option combination is reported instead of a name.
--dump-predef#
Print the target's table of predefined macros as #define lines and
exit. Like --print-target, it is answered after every option has been
read, so --target=, --cpu=, --fpu= and --float-abi= select the
table printed.
The table is the per-target set (architecture, data model, ABI). It does
not include the macros the preprocessor defines for every target
(__STDC__, __STDC_VERSION__, __STDC_HOSTED__, __FILE__ and the
like), command-line -D definitions, the C++ macros, or
__CHAR_UNSIGNED__ as changed by --char=signed/--char=unsigned. To
see exactly what a compile sees, preprocess a file that uses the macro.
The complete lists are in Targets.
--print-search-dirs#
Print where the driver found its own headers and libraries, then exit:
layout: build tree
self: /home/me/EmbCC/embcc
lib: /home/me/EmbCC
include: /home/me/EmbCC/lib/libcxx/include
include: /home/me/EmbCC/lib/libc/include
include: /home/me/EmbCC/include
layout is installed, build tree or none found. With none found
the line include: (none -- every header needs an explicit -I) follows.
See Headers and macros.
embcc inspect STAGE FILE [OPTION...]#
Run the pipeline up to STAGE and print what it built, instead of
producing an object. inspect must be the first argument and STAGE the
second; the rest is an ordinary command line, so -I, -D, --target=
and -O apply as they would to a compile. The output goes to standard
output.
STAGE |
Prints |
|---|---|
tokens |
the tokens of the preprocessed source, with positions |
pp |
the preprocessed source (the same as -E) |
ast |
the syntax tree, with resolved types |
symbols |
every declaration in the unit, with its type and location |
types |
structure layout: member offsets, bit-fields and padding |
ir |
EmbIR as it stands at the selected -O level |
cfg |
the control-flow graph of each function: blocks, edges, dominators, loops |
callgraph |
which function calls which, from the IR |
embcc inspect ir FILE.ir reads EmbIR text instead of C and prints it
back, which is the IR's round-trip test. With --opt=basic, --opt=speed or --opt=size, the
optimizer runs on the parsed IR before it is printed, so a pass can be
tried on IR written by hand. embcc inspect mir explains that
EmbCC has no machine-IR level. An unknown stage is refused with
embcc: inspect: unknown stage 'STAGE'. With fewer than two arguments
after inspect, the stage list is printed and the status is 1.
Comparing inspect ir at --opt=none and at --opt=speed shows what the optimizer did.
embcc why DECISION [SUBJECT] FILE [OPTION...]#
Compile FILE with remarks enabled and print the recorded decisions of
kind DECISION, optionally only those about SUBJECT (a function or
variable name). why must be the first argument. SUBJECT is recognized
when it does not start with - and contains no .; otherwise the word
is taken as the file.
$ embcc why inlined w.c --opt=speed
sq (w.c:3): inlined
because small-enough — 4 instructions into f, budget 24
decided by the inline pass
DECISION is any decision name that appears in --remarks
output, such as inlined, not-inlined, promoted-to-register,
hoisted, unrolled or spilled-to-stack. When nothing matches, why
says so and reminds you that optimization decisions need --opt=speed; the
status is still 0.
Machine-readable output#
The driver's machine-readable forms are
--diagnostics=json,
-fdiagnostics-parseable-fixits,
--remarks=json,
--emit-interfaces and
--stack-usage. The language server
embls obtains its diagnostics by running embcc.
GCC flags EmbCC accepts#
GCC has flags EmbCC has no name for. Each one here is accepted, because it changes nothing EmbCC does, or read so that it can be refused by name. A build written for GCC passes them; a new one has no reason to.
-pipe#
Accepted. EmbCC uses no temporary files between its stages.
-Ofast#
The same as -O3 (and so -O2). GCC's -Ofast adds -ffast-math,
which EmbCC does not do (see --fast-math),
so no result differs from --opt=max's.
Any other -O spelling (-O4, -Ox) is refused with
embcc: unknown optimization flag '-O4'.
-fgnu89-inline, -fno-gnu89-inline#
Select the meaning of inline on a function with external linkage.
The default, -fno-gnu89-inline, is C99's: a definition whose
declarations all say inline and none extern is an inline definition,
which is never emitted. -fgnu89-inline selects GNU89's: inline alone
is an external definition, and extern inline is never emitted. See
Inline functions.
With --std=c89, c90, iso9899:1990, gnu89 or gnu90, GNU89
semantics apply and -fno-gnu89-inline does not change them, as with
Clang.
Any other name is refused: embcc: error: unknown standard '--std=NAME',
or embcc: error: unknown C++ standard '--std=NAME' for a malformed
c++/gnu++ name.
-ansi is not accepted (unknown argument). --pedantic and
--pedantic-errors are accepted with a warning and change nothing (see
--pedantic). -Wpedantic is accepted as a
warning name EmbCC does not have (see --warn=NAME).
-Wp,ARGS#
Pass ARGS, split at the commas, to the preprocessor. Each must be -D,
-U or -I with its value joined to it, and is applied where the
-Wp option stands among the others, so -DY -Wp,-DX=1,-UY defines X
and leaves Y undefined. Any other preprocessor option is refused:
embcc: error: preprocessor option '-MD' in -Wp,-MD,dep.d is not supported; pass -D, -U or -I with its value, or give the option directly
-M#
Instead of compiling, write a make rule naming the input and every
header it includes, to standard output (or to the -MF file). Use it with
-c or -E: without one of them the driver goes on to link and fails.
$ embcc -M -c m.c
m.o: m.c \
/opt/embcc/lib/embcc/0.0.1-beta/include/stdio.h \
/opt/embcc/lib/embcc/0.0.1-beta/freestanding/stddef.h \
/opt/embcc/lib/embcc/0.0.1-beta/freestanding/stdarg.h \
loc.h
$ embcc --deps-only -c m.c
m.o: m.c loc.h
The rule's target is the --deps-target value if one is given. Otherwise, with
-c, it is the object's name (the -o name or the default object name),
and with -E it is the input's file name with its suffix replaced by
.o. Long rules are wrapped at about 72 columns with a backslash.
-MF FILE#
Write the rule to FILE. FILE is the next argument; with none the
driver stops with embcc: -MF needs a file name.
-fno-reorder-functions, -freorder-functions, -fno-ident, -fident#
What EmbCC does anyway:
- functions stay in source order, with no hot or cold subsections;
- no
.commentsection identifies the compiler.
-finline-small-functions, -fno-inline-small-functions, -finline-limit=N#
Accepted and without effect. They are hints to GCC's inliner, whose budgets EmbCC's does not share (see Optimization).
-fno-delete-null-pointer-checks#
Accepted. EmbCC already keeps the promise: no pass infers that a pointer
is non-null -- not from a dereference before the check, not from the
nonnull and returns_nonnull attributes (which are ignored), not from
its being an object's address -- so no comparison with a null pointer is
removed, and a load from address 0 stays a load.
-fno-tree-loop-distribute-patterns#
Accepted. No loop ever becomes a call. The idiom pass turns a copy or
clear loop with a constant trip count into a block copy or clear, and
every backend expands those inline, as it does structure copies, so a
hand-written memset or memcpy cannot become a call to itself. No
EmbCC object refers to memset, memcpy or memmove unless the source
calls them.
-fmerge-constants#
Accepted and without effect: it permits merging identical constants across units, and EmbCC does not.
-fno-isolate-erroneous-paths-dereference, -fno-move-loop-invariants, -fno-ipa-sra, -fno-lto#
Accepted and without effect. EmbCC turns no null dereference into a
trap, and has no passes under the other three names; -fno-lto is the
default (-flto is refused). EmbCC's loop-invariant
motion is the licm pass, turned off with --no-pass=licm.
-fsanitize-trap, -fsanitize-trap=LIST, -fsanitize-undefined-trap-on-error#
Accepted, since trapping is the only mode. Note one difference from GCC
and Clang: -fsanitize-trap=LIST with a list also enables the checks it
names, as --sanitize=LIST would. An unknown name in the list is reported
with the --sanitize=NAME message above.
Every other option beginning with -fsanitize (for example
-fsanitize-recover=...) is refused.
-fno-stack-protector#
Accepted: EmbCC emits no stack protection. Every -fstack-protector
form is refused:
embcc: '-fstack-protector-strong' is not supported (EmbCC emits no stack protection); -fno-stack-protector is
-fno-plt#
Accepted. EmbCC does not generate code that calls through a procedure linkage table.
-fno-pic, -fno-pie#
Accepted, with -fno-PIC and -fno-PIE. EmbCC's code is not position
independent (-fPIC is refused): an x86-64 jump
table holds absolute addresses, a Cortex-M address is a movw/movt
pair, and embld links no position-independent executable. The Darwin
targets' code is PC-relative because Mach-O requires it, and links the
same way either way.
-fno-zero-initialized-in-bss#
Accepted. EmbCC already places every object with an initializer in
.data, zeros included: int x = 0; and static char buf[64] = {0};
are in .data, so a bootloader that does not clear .bss still finds
them zero. Only an object with no initializer at all (int y;) is in
.bss, as with GCC's option. -fzero-initialized-in-bss, which permits
the other placement, is accepted and changes nothing.
-fstrict-volatile-bitfields, -fno-strict-volatile-bitfields#
Accepted. EmbCC already reads and writes a volatile bit-field with one
access of its declared type's width, as the AAPCS requires and
-fstrict-volatile-bitfields asks: a volatile uint32_t f : 8 is an
ldr (and an ldr/str pair to write it), never an ldrb, even when
the field lies in one byte. The access is to the field's container at
the type's alignment, so it can include neighbouring members. The
negative form permits narrower accesses, and EmbCC does not use the
permission.
-fverbose-asm#
Accepted and without effect on what -S writes.
-fgnuc-version=MAJOR[.MINOR[.PATCH]]#
Define __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ and
__GNUC_STDC_INLINE__ in a C unit, as that version of GCC, the way clang
does by default. EmbCC does not by default, because a header that sees
__GNUC__ may take paths that need GCC itself. Vendor headers that pick
their compiler support by it need it: CMSIS's cmsis_compiler.h stops at
#error Unknown compiler without it, and compiles with
-fgnuc-version=4.2.1 (see Embedded programming).
0 leaves the macros undefined. A C++ unit always presents itself as
g++ (see C++).
-fmessage-length=N#
Accepted. EmbCC does not wrap diagnostics, which is what
-fmessage-length=0 asks for; other values change nothing.
-fanalyzer#
Accepted with a warning: embcc: warning: -fanalyzer: EmbCC has no static analyzer, so this checks nothing.
-fdiagnostics-parseable-fixits#
After each fix-it hint, also print it in the machine-readable form Clang uses:
fix-it:"bad.c":{2:36-2:36}:";"
Refused options#
These options are refused because each is a promise about the generated code that EmbCC would not keep. The driver stops with status 1 and a message that names the option as it was typed; the messages below are what EmbCC's spellings get.
| Option | Message |
|---|---|
-fPIC, -fpic, -fPIE, -fpie |
embcc: error: -fPIC is not supported; EmbCC would emit ordinary code and the flag's promise would not hold |
-flto |
(same form) |
-fprofile-*, --coverage, -fcoverage-mapping, -pg |
(same form) |
-fstack-clash-protection, -fcf-protection, -fcf-protection=... |
(same form) |
-fsanitize* other than the forms in --sanitize |
(same form) |
--sanitize=NAME for a check EmbCC does not have |
--sanitize=NAME is not supported: EmbCC's sanitizer inserts checks that TRAP ... |
-fstack-protector, -fstack-protector-all, -fstack-protector-strong, -fstack-protector-explicit |
embcc: '-fstack-protector...' is not supported (EmbCC emits no stack protection); -fno-stack-protector is |
-shared, -static-pie |
embcc: error: -shared needs position-independent code, which EmbCC does not emit |
-gdwarf-5 (any version but 2 to 4), -gsplit-dwarf, -gz |
embcc: error: -gdwarf-5 is not supported; EmbCC emits DWARF 4, uncompressed and in one piece |
--isa=arm |
--isa=arm is not supported: a Cortex-M has no ARM instruction set, only Thumb |
--no-unaligned-access (ARM) |
--no-unaligned-access is not supported: EmbCC's ARMv7-M code uses word and halfword loads and stores at unaligned addresses ... |
--abi=apcs-gnu, --abi=atpcs, --abi=iwmmxt (ARM) |
--abi=apcs-gnu is not supported: EmbCC emits the AAPCS ... |
--abi=ilp32e, lp64e (RISC-V), or a float ABI the --isa= has no FPU for |
embcc: error: --abi=ilp32e is not supported for riscv32-unknown-elf: EmbCC emits ilp32, ilp32f and ilp32d (not the E base's) |
--isa= with an extension other than I, M, A, F, D, C, Zicsr, Zifencei, or without M or A (RISC-V) |
embcc: error: --isa=rv32imafcv: the 'v' extension is not supported: EmbCC emits I, M, A, F, D and C |
--endian=big or --endian=little against the target's byte order |
embcc: error: --endian=big is not supported for x86_64-elf, which is little-endian |
-fdump-rtl-*, -fdump-tree-* and every other -fdump-; -fcallgraph-info= with anything but su |
embcc: error: -fdump-rtl-expand is not supported: it dumps GCC's internal representation, which EmbCC does not have; ... |
--common for a Mach-O or COFF target |
embcc: error: --common is not supported for x86_64-apple-darwin: ... |
--float-constants=single for C++ |
embcc: error: --float-constants=single is supported for C, not C++: ... |
-O4 and other -O forms not listed |
embcc: unknown optimization flag '-O4' |
-Wl,OPTION or -Xlinker OPTION, for a linker option not listed under --linker-arg |
embcc: error: linker option 'OPTION' is not one EmbLD has (it takes -e, -Ttext, -Tdata, -Tstack, --rom-limit and --lma-offset); dropping it could build a different image from the one asked for |
-Wa,OPTION, for an option not listed under --assembler-arg |
embcc: error: assembler option 'OPTION' is not one the integrated assembler has |
Environment variables#
Variables the driver reads#
EMBCC_DEFAULT_TARGET
: The target used when the command line has no --target=. Overrides
the compiled-in default. See The default target.
EMBCC_PREFIX
: An installation prefix to take headers and libraries from, ahead of
the build tree or installation found beside the executable. See
How the compiler finds its own files.
The driver reads no other variables: CPATH, C_INCLUDE_PATH,
LIBRARY_PATH, GCC_EXEC_PREFIX, TMPDIR and NO_COLOR have no effect.
Variables for developing EmbCC#
The compiler also reads the following variables. They exist for testing and bisecting EmbCC itself; they change generated code or print internal traces, and they are not a stable interface. See Testing and Register allocation.
| Variable | Effect |
|---|---|
EMBCC_VERIFY |
Run the IR verifier after IR generation and after optimization, and make an optimizer that fails to converge a fatal error. The test suite sets it. |
EMBCC_RA_MAXPOOL=N |
Limit the integer register pool to its first N registers, to exercise spill paths. |
EMBCC_RA_WHY |
Print a line to standard error for each function in which values were spilled. |
EMBCC_RA_TRACE |
Print the register allocator's pool and each value's assignment to standard error. |
EMBCC_RA_DEGREE_SPILL |
Choose spill candidates by interference degree instead of by cost. |
EMBCC_MIPS_RA_MAX=N |
On MIPS, leave only the first N vregs of each function in registers (a bisection handle; always correct). |
EMBCC_MIPS_PAIRS=0 |
On MIPS, do not give 64-bit values register pairs. |
EMBCC_MIPS_NO_FILL |
On MIPS, leave every branch's and call's delay slot a nop (a return's still releases the frame). |
EMBCC_NO_TAILCALL |
Disable tail calls in the AArch64, ARM, RISC-V, MIPS and AVR backends. |
EMBCC_NO_JUMP_TABLES |
Compile as with --no-jump-tables, whatever the command line says, so a test suite's own command lines can be run without tables. |
EMBCC_NO_DIVMOD_CONST, EMBCC_NO_MLAKEEP |
On Thumb, stop sharing one divide between a quotient and a remainder by the same constant, or stop keeping a multiply's constant in a register for mla/mls. |
EMBCC_NO_SPLITLOOPS |
Disable the optimizer's loop-splitting step. |
EMBCC_NO_LKCONST |
Stop deciding compares and branches from a constant the same block has just written. |
EMBCC_VECDEBUG |
Trace the vectorizer's decisions. |
EMBCC_NO_FULLUNROLL |
Unroll a loop with a constant trip count like any other, instead of copying it whole. |
EMBCC_NO_RMW |
Disable x86-64 read-modify-write instruction fusion. |
EMBCC_NO_SINKUPD |
Leave a loop counter's update where it was instead of moving it next to the copy that ends it. |
EMBCC_NO_MEMOFF |
Disable folding constant offsets into loads and stores (ARM, RISC-V, AVR). |
EMBCC_NO_IDXOFF |
On RISC-V, stop moving a constant out of an array index into the access's displacement. |
EMBCC_RV_RA_MAX, EMBCC_RV_PAIRS, EMBCC_RV_PAIRS_ONLY, EMBCC_RV_JAL_RANGE, EMBCC_RV_LONG_CALLS |
RISC-V allocator, register-pair and call-range knobs. |
EMBCC_T_RA_MAX, EMBCC_T_PAIRS, EMBCC_T_PAIRS_ONLY, EMBCC_T_NOLO, EMBCC_T_FPU, EMBCC_T_EXT |
ARM allocator, register-pair and FPU knobs; EMBCC_T_EXT=0 keeps r9-r11 out of the allocator's pool. |
EMBCC_T_NOWIDEIMM, EMBCC_T_NOCMP64IMM, EMBCC_NO_SIGNTEST |
Build a 64-bit constant whole on Thumb instead of using it half by half (a compare's constant too); keep x >> 63 as a shift before a branch. |
EMBCC_AVR_RA, EMBCC_AVR_RA_MODE, EMBCC_AVR_RA_ONLY, EMBCC_AVR_RA_LIMIT, EMBCC_AVR_RA_CAP, EMBCC_AVR_REMAT_MAX, EMBCC_AVR_NO_VOL, EMBCC_AVR_NO_OCT, EMBCC_AVR_NO_XHOME |
AVR register-allocation mode and bisection knobs. |
The build and test scripts read further variables (EMBCC_TARGET,
EMBCC_QEMU_TIMEOUT, EMBLD, EMBCC_AR and others); those are
described in Getting started and
Testing.