Testing#
This page describes how EmbCC is tested: the make targets, the test runner and the directives it reads, the golden tests grouped by area, the QEMU harnesses that run compiled code on each target, the reference tools the tests compare against, and the environment variables that steer the suite or stress the compiler. It is for anyone changing EmbCC who needs to know which tests a change must pass and how to add one.
Principles#
- The tests that count run the program. A test that only checks that the compiler exits 0, or that an object looks well-formed, proves the object writer, not the compiler. Most of the suite compiles a program, runs it on the architecture it was compiled for, and asserts its exit status or its output.
- The expected answer comes from a reference. Where another implementation exists, the test compares against it: the target's gcc or clang, g++, nasm, llvm-mc, a disassembler, gdb, a Linux kernel, or the host's own arithmetic. See Referees.
- Agreeing with yourself proves nothing at a boundary. Calling conventions, layouts and unwind tables are checked by linking an EmbCC half with a half built by another compiler and calling in both directions.
- A skipped test is not a pass. A test that cannot run on the current host says so and is counted as SKIP, with the reason.
- A test is evidence only after it has failed. See A new test must fail first.
Make targets#
| Target | Builds first | Runs | When |
|---|---|---|---|
make check |
embcc, libc-x86_64, libcxx-x86_64 (without the libraries when built without x86, make TARGETS=) |
tests/run.sh --exec-only: the compiled-and-run corpus (tests/exec/*.c, tests/exec/x86_64/*.c, tests/cxx/*.cc) for x86_64-elf, or a skip when the compiler has no x86 |
while working |
make test |
embcc, embread, embld, embdbg, embls, embas, libc-x86_64, libcxx-x86_64, libc-linux-x86_64, libcxx-linux-x86_64 |
tests/run.sh: every shell test in tests/exec, tests/compile, tests/golden and tests/golden/x86_64, then the corpus, for x86_64-elf |
before a commit |
make test-arm64 |
embcc, libc-aarch64, libcxx-aarch64, libc-linux-aarch64, libcxx-linux-aarch64 |
tests/run.sh --target=aarch64-elf: tests/golden/*.sh, tests/golden/aarch64/*.sh, and the corpus compiled for aarch64-elf |
before a commit that can affect aarch64 or shared code |
make test-libstdcxx |
embcc |
tests/golden/cxx-libstdcxx-embcc.sh for x86_64-elf and aarch64-elf, with EMBCC_LIBSTDCXX=1 |
opt-in; builds libstdc++ from GCC's sources and takes minutes |
make check is not a unit-test suite. There is no separate unit-test
layer: the fast loop is the program corpus, which catches most front-end
and code-generation regressions because they show up as a program
returning the wrong answer. What make check does not check is what the
golden tests exist for: agreement with gcc, -S against -c, object
formats, interoperability, and every target other than x86_64-elf.
The embedded targets (ARMv7-M, ARMv8-M, RV32, RV64, AVR) have no make
target of their own. Their suites are golden tests that pick their own
--target= and run under make test; each one skips, with a reason, when
its QEMU or its reference compiler is missing.
make test does not build embidx, so tests/golden/index.sh reports
SKIP unless it exists; make all builds it. (Plain make with no target
builds only build/embdbg_core.o, the first rule in the Makefile, so
always name the target.)
Two further checks are run by hand:
EMBCC_KM1=1 sh tests/golden/x86_64/embbuild-kernel.shbuilds the EmbLinkOS kernel from its EmbBuild manifest with EmbCC, EmbAS and EmbLD alone, and boots it. It needs an EmbLinkOS tree (EMBCC_MYOS) and takes over a minute.tools/x86-identity.sh [REV]buildsembccat a baseline revision (defaultHEAD) and requires every x86-64 object it produces from the exec corpus (at-O0,-O1,-O2and with-g), and from the EmbLinkOS kernel when one is found, to be byte-identical to the working tree's. On a host that cannot run x86-64 code natively, this is how a change to shared code shows that it left the x86-64 backend alone. A change meant to alter x86-64 output fails it by design; inspect the difference, then move the baseline.
The runner#
tests/run.sh [--target=x86_64-elf|aarch64-elf] [--exec-only] [--embcc=PATH]
tests/run.sh must be run from a tree where ./embcc is built; it exits
with an error otherwise. The default target is x86_64-elf.
--exec-only skips every shell test and runs only the corpus.
--embcc=PATH tests another compiler than ./embcc, such as one built
into a different BUILD directory.
A compiler built for some target families only (make TARGETS=; see
Getting started)
runs what it can. When it has no backend for the suite's --target=, the
runner prints run.sh: skipped: with the compiler's refusal and exits 0;
that is what make check does in a tree built without x86. A shell test
that fails with that refusal (this embcc was built without the NAME backend) is reported as SKIP rather than FAIL. A compiler with every
family never prints it, so on a full build nothing is skipped this way.
The runner exports these variables to every test:
| Variable | Value |
|---|---|
EMBCC |
the absolute path of ./embcc |
EMBCC_TARGET |
the --target= the suite runs for |
EMBCC_VERIFY |
1: every optimizing compile runs the IR verifier (see Compiler knobs) |
EMBCC_QEMU_TIMEOUT |
seconds a QEMU guest may run; 20 unless already set |
EMBCC_X86_RUNNER |
host on a Linux x86-64 host, qemu anywhere else, unless already set |
Shell tests#
Every *.sh under tests/exec, tests/compile and tests/golden, and
tests/golden/<arch> for the selected architecture, is a shell test. With
--target=aarch64-elf only tests/golden/*.sh and
tests/golden/aarch64/*.sh run; the refusal tests and the x86-64
toolchain tests drive the default target.
A shell test passes when it exits 0 and its output contains the line
TEST-MARKER NAME, where NAME is the script's file name without .sh.
Exit 0 without the marker is a failure ("did it run?"): the marker catches
a test that silently never ran.
A test that cannot run on this host prints a line beginning skipped:,
followed by the reason, and exits 0. The runner reports it as SKIP and
does not count it as a pass. Any other spelling (SKIP:, skipping) is
not recognized, and a test that uses one and exits 0 is counted as a
pass.
Shell tests run concurrently, EMBCC_JOBS at a time (default: the number
of online processors; 1 runs them serially, which is the setting to use
when debugging one test's output). Results are reported in list order, so
two runs can be compared line by line, and a failing test's whole output is
printed under its FAIL line.
The corpus#
tests/exec/*.c and tests/exec/<arch>/*.c are compiled by EmbCC at
-O0, linked, and run; tests/cxx/*.cc are compiled by EmbCC and linked
against the reference libstdc++ built by tools/build-ref-gxx.sh (SKIP
when it is absent). Each object is rebuilt from scratch on every run, so a
stale artifact cannot be measured.
Where the program runs depends on the target and the host:
x86_64-elfwithEMBCC_X86_RUNNER=host: linked with the hostcc -no-pieand executed directly.x86_64-elfwithEMBCC_X86_RUNNER=qemu, andaarch64-elf: linked bytests/harness/<arch>/link.shinto a bare-metal image and run bytests/harness/<arch>/run.shunder QEMU. C++ programs always use the harness.
The program's exit status must equal its // expect-exit: value. A run
that ends in status 124 is reported as "timed out after N s under QEMU";
status 125 as "the guest crashed or reset before exiting".
Directives the runner and the golden tests read from a corpus file:
| Directive | Meaning |
|---|---|
// expect-exit: N |
Required. The exit status the program must return. A file without it fails: it cannot assert anything. |
// target: TRIPLE |
Run the program only for this target; elsewhere it is reported as SKIP. Used for programs that contain one machine's inline assembly. |
// needs-libc: REASON |
The program calls what only EmbCC's C library has. tests/run.sh, which links the QEMU harnesses' newlib or the host's C library, reports it as SKIP, and so do the gcc-differential tests; tests/golden/libc.sh and the boards' exec goldens, which link lib/libc, run it. |
// no-gcc-reference: REASON |
The gcc-differential tests (agrees-with-gcc, optimizer, regalloc-O2, exec-Os) skip the file and print the reason. Used where the cross gcc cannot build a reference, for example because it would call libatomic. |
Exec programs must be valid strict C11: agrees-with-gcc compiles each
one with the target's gcc at -std=c11 and fails a program gcc rejects.
The final line is P/T passed, with the skip count when there were skips.
The runner exits non-zero on any failure, and also when it found no tests
at all.
Writing a test#
An exec test#
Add tests/exec/NAME.c with a // expect-exit: N line. Prefer a program
that computes its answer and returns a value that is only reached when
every check passed (42 is the convention), and that prints what it
computed: agrees-with-gcc and regalloc-O2 compare stdout with gcc's
build as well as the exit status. Put a program that only one architecture
can compile in tests/exec/<arch>/ or mark it // target:.
A refusal test#
THE RULE (see Contributing) has its
own test: tests/compile/reject-unimplemented.sh. Each check case gives
a source, a pattern the diagnostic must contain, and optional flags. A
case passes only if the compile fails, the diagnostic matches, and the
diagnostic carries a file:line position. Cases are compiled with
-Werror, so a construct that is warned about rather than refused also
fails to compile. When a feature is implemented, its case graduates out of
this file and into an exec test. Target-specific refusals live in the
target's golden test (avr-refuse.sh, thumb-codegen.sh, ...).
A golden test#
A golden test is a POSIX sh script in tests/golden/ (every target) or
tests/golden/<arch>/ (one architecture). It must:
Print
TEST-MARKER NAMEfirst, then sourcetests/lib.sh:#!/bin/sh # What property this checks, and against what reference. set -u echo "TEST-MARKER NAME" . "$(dirname "$0")/../lib.sh"Write only inside
tests/golden/out/NAME/. Golden tests run in parallel, and the harness link scripts take a directory variable (EMBCC_THUMB_HARNESS,EMBCC_RISCV_HARNESS,EMBCC_M33_HARNESS,EMBCC_AVR_HARNESS,EMBCC_HARNESS_WORK) so that harness objects are built in the test's own directory rather than shared.Say
skipped: REASONand exit 0 when a prerequisite (a QEMU binary, a reference compiler, a tree) is missing.Exit non-zero with a one-line explanation on the first disagreement.
tests/lib.sh provides the host's tool paths (from tools/hostpaths.sh)
and target-generic helpers for tests that run under any --target=:
| Helper | Does |
|---|---|
t_gcc_c SRC -o OBJ [FLAGS] |
compile with the target's reference gcc |
t_link EXE OBJ... |
link a hosted test program for the target (host cc or the harness) |
t_run EXE |
run it; prints its stdout and returns its exit status |
x86_gcc_c, x86_link, x86_run, x86_gcc_exe |
the same, for x86-64 only |
pinned_elsewhere FILE |
true if FILE says // target: for another target |
no_gcc_reference FILE |
true, and prints why, if FILE says // no-gcc-reference: |
It also sets TARGET and ARCH from EMBCC_TARGET, and EMBCC_ROOT to
the repository root.
To run one golden test by hand, run it from the repository root with the variables the runner would set:
EMBCC=$PWD/embcc EMBCC_VERIFY=1 sh tests/golden/riscv-exec.sh
EMBCC=$PWD/embcc EMBCC_TARGET=aarch64-elf EMBCC_VERIFY=1 sh tests/golden/regalloc-O2.sh
The library tests link the archives under build/, so rebuild them
(make libc-x86_64 libcxx-x86_64, and the -linux- and aarch64
variants) before trusting a hand-run result; make test does that itself.
A fresh checkout also needs embld, embidx and the other tools built
before a FAIL from a test that uses them means anything.
A new test must fail first#
A new test is evidence only after it has been seen to fail against the code it is meant to catch. Revert the fix (or build the previous revision in a separate worktree), run the test, confirm that it fails for the reason it was written for, then restore the fix. A test that passes either way is worse than none, because it is reported as coverage. The usual ways a test turns out vacuous:
- the optimizer folds the case away, so the code path is never reached
(a
staticfunction inlined into a constant); - the check reads back nothing and compares nothing (a pipeline that silently produced empty output, decoded as zeros on both sides);
- the property the test prints does not depend on the code under test.
When a change removes a refusal, search tests/ for the refusal's text:
a test that pinned the refusal now fails, as it should, and has to be
changed into a test of the new behaviour.
Golden tests#
One line per test, grouped by area. The header comment of each script says in full what it checks and why.
The exec corpus, differentially#
| Test | Checks |
|---|---|
agrees-with-gcc |
every exec program built by EmbCC (-O0) and by the target's gcc (-std=c11); both run; exit status and stdout must match |
optimizer |
-O0 output is byte-identical to the no-flag output; every exec program keeps its expect-exit value at -O1; dead-code cases |
regalloc-O2 |
every exec program at -O2 against gcc: exit status and stdout |
exec-Os |
every exec program at -Os against gcc |
libc |
the exec corpus linked against EmbCC's own C library with no newlib at all |
cxx-agrees-with-gxx |
every tests/cxx program built by EmbCC and by the reference g++, run on the harness; exit status and output must match |
Interoperability and calling conventions#
| Test | Checks |
|---|---|
sysv-abi |
struct passing under System V: half the program from gcc, half from EmbCC, calling each other |
struct-abi-edges |
struct and float arguments at the edges of the convention (too few registers left, over-aligned, packed, padding eightbytes), both directions, -O0 and -O2 |
stack-args |
the seventh parameter onward, arriving on the stack, at -O2 |
cross-varargs |
varargs and floating aggregates across the EmbCC/gcc boundary, including va_list handed to newlib's vsnprintf |
complex-abi |
_Complex float, double and long double across the boundary |
int128-abi |
__int128 across the boundary, including AAPCS64 even-register pairs and stack alignment |
ldouble-abi |
long double across the boundary (x87 extended, IEEE binary128) |
ldouble-same |
long double where it is a double (ARM) or a float (AVR) |
struct-layout |
struct, union and bit-field layout on every target against the reference compiler of that target's data model |
win-abi |
the Microsoft x64 convention, argument placement checked against clang by disassembly |
win-abi-run |
Win64 code called from gcc-built code through ms_abi pointers in an x86-64 test image, -O0 to -O2 |
darwin-abi |
Apple's arm64 convention and data model, clang half and EmbCC half, run natively (macOS on Apple silicon only) |
cxx-abi |
EmbCC's C++ linked with g++'s: mangling, layouts, vtables, VTTs, classes by value, in both directions |
eh-regions |
EmbCC's exception regions against exceptions thrown by g++-built code |
unwind-through |
a C++ exception unwinding through EmbCC-compiled C frames using only their .eh_frame, at -O0 and -O2 |
The embedded ABI pairing (tests/golden/embedded-abi-caller.c,
embedded-abi-callee.c, embedded-abi.h) has no script of its own: it
runs inside thumb-exec, riscv-exec, mips-abi and avr-wide, which build every
pairing of EmbCC and the reference compiler for caller and callee. AVR is
paired against the documented avr-gcc rules instead (avr-abi), because
clang's AVR struct convention is not avr-gcc's.
ARMv7-M and ARMv8-M#
| Test | Checks |
|---|---|
thumb-target |
the ARMv7-M triples and data model |
thumbv8m-target |
ARMv8-M Mainline (Cortex-M33) as a level of the Thumb target: triples, macros, object attributes, an image run on the M33 board |
thumb-codegen |
objects are ELF32 ARM, every .text instruction decodes, relocations are ARM ones, refusals fire by name |
thumb-encoding |
every Thumb-2 encoder, disassembled by llvm-objdump and compared with what it was meant to be |
thumb-vfp |
the VFP instruction vocabulary against llvm-mc, for the Cortex-M4F's unit and the Cortex-M7's |
thumb-v6m-encoding |
the ARMv6-M (Thumb-1) encoders across every operand combination, byte for byte against llvm-mc -triple=thumbv6m-none-eabi; out-of-field operands refused |
thumbv6m-target |
ARMv6-M as a level: triples, -mcpu=cortex-m0/m0plus/m1, the refused M23 and FPU, the data model, the macro table and the object's tags against clang's, and a literal pool right after a switch table marked as one run of data |
thumbv6m-asm-values |
ARMv6-M inline asm with its operands in registers: PRIMASK intrinsics inline into callers that touch no stack, and value, continuation, "+", through-address and r12 operands, values across asm and templates that call, run on the Cortex-M0 board at -O0 to -Os and with two and no pool registers |
thumb-v6m-exec |
the tests/exec corpus on the Cortex-M0 board (QEMU micro:bit) at -O0, -O1, -O2, -Os, each wrong answer re-run as an ARMv7-M build on the M3 to tell a backend bug from a program that assumes LP64; embedded-stress against clang, embedded-int64/-float against the host; every instruction of every object built scanned for ARMv6-M, and every object for two mapping symbols at one address |
thumb-asm |
the inline-assembly vocabulary against llvm-mc, operand handling, refusals |
thumb-exec |
programs compiled by EmbCC and by clang, linked by embld, run on the Cortex-M3 board at -O0, -O1, -O2, -Os; 64-bit, float, aggregate, varargs and ABI programs against the host |
thumb-relax |
16-bit branch forms at both sides of every reach limit |
thumb-far |
conditional branches beyond the 1 MB reach of B<c>.W |
thumb-fpu |
single-precision arithmetic on the Cortex-M4F FPU, run on an M4 board |
thumb-hardfp |
-mfloat-abi=hard against clang in both directions on the M4F and M33 boards |
thumb-m7-dp |
-mfpu=fpv5-d16: the flags, macros and attributes against clang's, the .f64 code shape, double programs against the host at four levels on the Cortex-M7 board, AAPCS-VFP against clang both ways, softfp against soft-float objects |
thumb-m7-exec |
tests/exec at -O2 on the Cortex-M7 board with doubles in hardware and lib/libc built for it, against the same corpus with doubles in software |
thumb-atomic |
C11 atomics and __sync builtins under a SysTick interrupt updating the same variables |
thumb-calleesave |
every function preserves r4-r11 and sp, checked by an assembly probe |
arm-abi-tags |
.ARM.attributes contents and embld's refusal to link mismatched float ABIs |
RISC-V#
| Test | Checks |
|---|---|
riscv-target |
the RV32 and RV64 triples and data models |
riscv-encoding |
every RISC-V encoder round-tripped through llvm-mc and a disassembler |
riscv-compressed |
the C extension against llvm-mc over tens of thousands of instructions |
riscv-asm |
the inline-assembly vocabulary against llvm-mc |
riscv-exec |
programs compiled by EmbCC and by clang, run on QEMU virt at both widths, plus the ABI pairing |
riscv-relax |
branch and jump relaxation at the reach limits |
riscv-atomics |
the A extension at both widths |
Renesas RX#
| Test | Checks |
|---|---|
rx-encoding |
every encoder form decoded by QEMU's RX disassembler (its monitor's x/Ni) as the instruction meant and from the length emitted; byte for byte against GNU as when an rx-elf binutils is installed; every range check provoked |
rx-exec |
tests/exec/*.c on QEMU gdbsim-r5f562n8 at -O0, -O1, -O2, -Os, linked with lib/libc and lib/rt built for RX; the programs whose expected value assumes LP64 or a binary64 double against the status rx-elf-gcc's code exits with (recorded) |
rx-abi |
EmbCC and rx-elf-gcc calling each other: the shared embedded ABI pairing and the RX-specific one (rx-abi-*.c); skipped without an rx-elf-gcc |
rx-refuse |
the object's header, the data model and Microsoft bit-field layout, the options and constructs refused by name |
MIPS32#
| Test | Checks |
|---|---|
mips-encoding |
every encoder form against llvm-mc -show-encoding, word by word; mips_li executed; every range check provoked |
mips-exec |
tests/exec/*.c on QEMU malta at -O0, -O1, -O2, -Os, linked with lib/libc and lib/rt built for mipsel; the programs whose expected value assumes LP64 against clang's result on the same board |
mips-abi |
EmbCC and clang (mipsel-unknown-elf -msoft-float) calling each other: the shared embedded ABI pairing and the o32-specific one (mips-abi-*.c) |
mips-link |
EmbLD's REL relocations: the HI16/LO16 AHL rule against absolute symbols over every carry case, EmbCC's own far addends, the link refusals (gp-relative, hard float, a lone HI16, a linker script) |
mips-asm |
the inline-assembly vocabulary against llvm-mc, asm programs on the board (in .set reorder and noreorder), refusals, and -S reassembled by llvm-mc and linked to the same image as -c's object, asm blocks included |
mips-gas |
a .S file of every form (modes, labels, jal/%hi/%lo/la, expressions, data) assembled by EmbCC and by clang and linked to the same image; the .S refusals |
mips-slots |
delay-slot filling on malta at -O1..-Os: instructions that write a branch's operand, touch $ra under a jal, write jalr's target, or sit behind a label (the epilogue's included) stay put; argument setup moves; some call's slot must be filled |
mips-switch |
jump tables in a leaf (its $ra kept across the bal) and a calling function, values below, inside and above the range, -O0..-Os on malta; the dispatch must be in the object |
mips-exc |
a general exception handler in a .S file on malta: syscalls from a naked function return the handler's values, the CP0 timer interrupts a register-heavy loop under -icount without changing its result, a file-scope asm function calls C; at -O0 to -Os |
mips-refuse |
the object header and flags, the MIPS options accepted and refused, the constructs refused by name (atomics, frame builtins, __int128, interrupt, unwind tables, C++) |
mips-access |
atomic, volatile, op=, ++ and va_arg accesses are single lw/sw/lhu/sh (disassembled at -O0 and -O2); packed members are still split, and run right at a misaligned address on the board, in an image whose .data ends mid-word |
libc-embedded and debug-embedded include mipsel-none-elf too.
LoongArch64#
| Test | Checks |
|---|---|
loongarch-encoding |
every encoder form against llvm-mc -show-encoding, word by word, every register in every field; 6000 la_li constants against llvm-mc's own li.d expansion and executed; every range check provoked |
loongarch-exec |
tests/exec/*.c on QEMU virt at -O0, -O1, -O2, -Os, linked with lib/libc and lib/rt built for loongarch64 |
loongarch-abi |
EmbCC and clang (loongarch64-unknown-elf -msoft-float, its default medium code model and GOT accesses) calling each other: the shared embedded pairing, the 128-bit one and the LP64S one (loongarch-abi-*.c) |
loongarch-asm |
the assembler's vocabulary against llvm-mc, pseudos included; a C program of inline asm, a file-scope block and a naked function linked with a .S file (assembled by EmbCC and by clang) on the board; -S reassembled; the refusals |
loongarch-atomics |
one- and two-byte atomics at every place in their word on the board, the neighbours untouched; the ll.w/sc.w loop in the object |
mips-atomics |
one- and two-byte atomics at every place in their word on MIPS32 and MIPS64, both byte orders and every level: the little-endian boards against the host, the big-endian ones against clang's build on the same board; the compare-and-swap macros |
ppc-atomics |
one- and two-byte atomics at every place in their word on the ppce500 board at every level, against clang's build on the same board; the lwarx/stwcx. loop in the object; the compare-and-swap macros |
sparc-atomics |
one- and two-byte atomics at every place in their word on the leon3_generic board at every level, against clang's build (a casa loop too) on the same board; the casa loop in the object |
sparc-slots |
the SPARC delay slots read back with llvm-objdump: no cc-setter in a conditional branch's slot, no %o7 in a call's, no jump target written in its own; lib/libc's nops at most two thirds of what the filling off leaves; a leaf's retl, a restore moving the return value, an annulled if/else arm (tests/golden/sparc-exec/slots.c, run on the board by sparc-exec, computes through each hazard) |
tricore-atomics |
one- and two-byte atomics at every place in their word on the tricore_testboard at every level, against the host; the compare-and-swap macros |
xtensa-atomics |
one- and two-byte atomics at every place in their word on the de212 sim board at every level, against the host; the compare-and-swap macros |
loongarch-refuse |
the triples, the object header and flags, -S reassembled, the options accepted and refused, the constructs refused by name, the objects embld refuses |
libc-embedded, debug-embedded, embedded-runtime and predef include
loongarch64-unknown-elf too.
AVR#
| Test | Checks |
|---|---|
avr-target |
the AVR data model (2-byte int and pointers, 4-byte double) and target refusals |
avr-encoding |
the instruction vocabulary against llvm-mc, including decoding EmbCC's bytes back for branch forms |
avr-asm |
EmbCC's AVR assembler against llvm-mc |
avr-exec |
programs run on the ATmega328P board against the same source run on the host |
avr-alloca |
variable-length arrays, alloca and aligned locals on the ATmega328P against the host, at every level and every forced allocation mode: stack arguments after each (direct, indirect, variadic), 300 VLA scopes, a 400-call loop and a frameless alloca function, any of which overruns the 2 KiB if sp is not given back |
avr-abi |
the calling convention against avr-libc's documented rules, with hand-written assembly on the other side |
avr-calleesave |
every function preserves r2-r17 and Y |
avr-cgoto |
the computed-goto programs of tests/exec on the part at every level and allocation mode, and their label addresses as word (gs, pm) relocations |
avr-float |
software binary32 on the part, bit for bit against the host |
avr-softfp-host |
the same float routines built for the host, against native float |
avr-inline-asm |
operand constraint classes, byte modifiers, refusals |
avr-io16 |
16-bit I/O registers written high byte first, read low byte first |
avr-isr |
signal and interrupt handlers and the vector table |
avr-narrow |
computing only the bytes a use reads |
avr-narrow-cmp |
narrow comparisons at every level against the host |
avr-refuse |
what the backend refuses, by name |
avr-shared |
the shared embedded programs (64-bit, aggregates, varargs) on the part |
avr-wide |
64-bit integers, varargs and aggregates by value, plus the ABI pairing |
Every embedded target#
| Test | Checks |
|---|---|
embedded-runtime |
every runtime routine each embedded backend can call is in that target's librt.a, exactly once |
bitops-width |
ctz, clz, popcount, ffs, parity, clrsb at each target's type widths |
cvt-fold |
constant conversions folded by the optimizer agree with run-time conversion and the host |
double-pairs |
64-bit values in register pairs on RV32 and Cortex-M3, with the pair allocation forced on and off |
vla-exec |
variable-length arrays on Cortex-M3, RV32 and RV64 |
ptr-compound |
pointer += and -= at the target's address width |
debug-embedded |
-g on the embedded targets: DWARF checked by llvm-dwarfdump and gdb |
embdbg-remote |
EmbDBG's remote-protocol client stopping a QEMU guest at a function and reading its arguments |
Language and front end#
| Test | Checks |
|---|---|
c23 |
the C23 features, each checked for its effect |
gnu-c |
GNU C extensions used in kernel macros, against the host compiler |
has-feature |
the __has_* operators in C; __has_builtin agrees with the builtin dispatch |
typeof-attrs |
typeof on parameters and expressions; attributes on typedefs |
attrs |
attributes whose effect is in the object (used, visibility) |
alignment |
object alignment carried into section and symbol alignment |
overflow-builtins |
__builtin_{add,sub,mul}_overflow at the boundaries, against the host |
float-bits |
fabs, copysign, signbit and the classification builtins on every target |
float-types |
_Float32, _Float64, _Float128 and friends, per target |
int-constants |
integer literal types and constant expressions on every target |
stdint |
<stdint.h> widths and limits on every target against the reference compiler |
rodata-const |
const objects in .rodata, non-const in .data |
static-init |
constant initialization versus .init_array, and that a null dereference still faults in the harnesses |
include-next |
#include_next |
predef |
the predefined-macro table of each target against its reference compiler |
atomic-operators |
operators on _Atomic objects are atomic read-modify-writes on every target |
volatile-access |
every volatile access happens, on every target, at every level |
Optimizer#
| Test | Checks |
|---|---|
algebra |
algebraic identities and constant reassociation, checked on the IR and by running against gcc |
alias |
alias analysis and the passes that rely on it, weighted toward references that do alias |
loopopt |
LICM, loop rotation and the value numbering rotation depends on |
pre |
partial redundancy elimination |
sroa |
splitting private aggregates into scalars |
unroll |
loop unrolling |
vectorize |
auto-vectorization fires and computes the same checksums as -O0, -O1 and gcc |
tailcall |
sibling calls run in constant stack |
frameless |
leaves with no frame, and the conditions that make it safe |
time-report |
-ftime-report reports every phase and a total on stderr, and the object is the same with or without it |
branches |
short branch encodings where they reach |
parallel-move |
ra_parallel_move simulated over every small shape (tools/pmovecheck) |
remarks |
optimization remarks and embcc why: different causes give different reasons |
provenance |
every IR instruction keeps a source location through every pass |
ir-roundtrip |
EmbIR's textual form: print, parse, print reproduces the text |
inspect |
embcc inspect shows the optimizer's work between two levels |
Driver, output and object formats#
| Test | Checks |
|---|---|
asm-S |
-S output assembled by the GNU assembler reproduces the -c object (x86-64 only; skips elsewhere) |
asmout-roundtrip |
the same property on every target |
gas-file |
.s and .S input files on the embedded targets |
triple |
target triples parse to architecture, OS and object format; old spellings keep their meaning |
default-target |
DEFAULT_TARGET, EMBCC_DEFAULT_TARGET and --target= precedence, and the separation from EMBCC_TARGET |
target-select |
make TARGETS=arm builds a compiler without the other backends (none of their symbols in it) whose objects are byte-identical to the full compiler's; it refuses another family's target by --target=, the default, -arch and its name, and the linker refuses that family's objects; --version lists the families; the runtimes and the install are the family's triples only; an unknown family stops make and ./configure; tests/run.sh skips a suite the compiler cannot build. EMBCC_TARGETS_EVERY=1 also builds each of the 13 one-family compilers |
install |
the compiler finds its headers and libraries relative to its own binary |
version |
--version names the target and the language and admits what is missing |
driver-deps |
-M, -MM, -MD, -MMD, -MF, -MT, -MP and -fsyntax-only |
gcc-flags |
the flags an arm-none-eabi-gcc build passes: each accepted one compiles silently on every target it applies to, each refused one names itself, and each promise holds in the object -- no jump table under -fno-jump-tables on any backend (and the same answers on QEMU's Cortex-M3 and RV32), -fcommon's COMMON symbols merged by embld and run, -fno-inline-functions, -fsingle-precision-constant, -save-temps, = 0 in .data, volatile bit-fields at their declared width |
stack-usage |
-fstack-usage output in gcc's format |
sanitize |
-fsanitize=undefined in trap mode on every target |
tools-build |
every tool in make all links |
host-agnostic |
EmbCC's output does not depend on which host compiler built EmbCC |
macho |
the Mach-O writer, judged by the macOS SDK's structures and the system linker (macOS only) |
windows |
the COFF writer, judged by independent readers and a real linker |
linux |
the Linux target: a static image with no glibc, run as PID 1 on a real Linux kernel |
interfaces |
stable symbol identity and interface hashes (--emit-interfaces) |
index |
embidx: a header edit that changes no interface rebuilds nothing |
Diagnostics and tools#
| Test | Checks |
|---|---|
diagnostics |
located headings, source line and caret, across includes |
diagnostics-explain |
--explain entries exist for every diagnostic id the compiler can print |
diagnostics-fix |
-fdiagnostics-parseable-fixits and --fix: the fixed file compiles |
diagnostics-json |
-fdiagnostics-format=json; applying its fix-its makes the file compile |
diagnostics-recovery |
several independent errors reported in one run, and no invented ones |
warnings |
each warning's name, group and switch; EmbCC warns where gcc warns |
warnings-uninit |
-Wuninitialized and -Wmaybe-uninitialized, mostly cases that must not warn |
format-check |
-Wformat: every line EmbCC warns about, gcc warns about too |
embls |
the language server over a real LSP session |
embld-doctor |
embld --doctor names the cause of a failed link |
link-dwarf |
EmbLD carries DWARF into the image; gdb on a QEMU guest |
debug-line |
DWARF line tables read by gdb; -g off leaves the object unchanged |
debug-locals |
subprogram, parameter and local DIEs; frame offsets checked against codegen's slots |
debug-live |
gdb stops a running program on the harness and reads its actual state |
Libraries and runtime#
| Test | Checks |
|---|---|
libc-emblinkos |
the same portable library builds for EmbLinkOS with one different file |
malloc |
the allocator: disjoint blocks, content kept, time linear in the work |
threadsafe |
malloc, stdio and function-local statics under threads |
rt |
lib/rt, the compiler runtime, against the host's runtime and libgcc |
unwind |
EmbCC's unwinder against libgcc's, by a program whose output records what ran |
libcxx |
EmbCC's C++ runtime (lib/libcxx): new/delete, guards, RTTI, the personality routine |
libcxx-std |
<type_traits>, <utility>, <limits> and the <c*> headers |
C++#
| Test | Checks |
|---|---|
cxx-access |
private and protected enforced, and every valid use still accepted |
cxx-format-check |
std::format strings checked at compile time, agreeing with g++ |
cxx-libstdcxx |
tests/libstdcxx programs against the reference libstdc++ |
cxx-libstdcxx-embcc |
libstdc++ and libsupc++ built by EmbCC, then every C++ program linked with them (opt-in) |
cxx-libsupcxx |
libsupc++ compiled by EmbCC from GCC's sources |
cxx-no-rtti |
-fno-rtti as g++ means it |
cxx-noexcept |
an exception leaving a noexcept function calls std::terminate |
cxx-reject |
ill-formed C++ refused with a located diagnostic |
cxx-std |
__cplusplus and feature macros per -std=, against g++ |
x86-64 only (tests/golden/x86_64/)#
| Test | Checks |
|---|---|
assembler |
EmbAS output byte-identical to nasm -f elf64 |
data-symbols |
data symbols, sections and their relocations, with readelf |
relocations |
external calls as R_X86_64_PLT32 against undefined globals |
symtab |
symbol binding, sizes and intra-unit calls |
weak-undef |
weak references emitted as weak undefined symbols |
empty-object |
the ELF writer's minimal object accepted by readelf and objdump |
inline-asm |
extended inline asm with fixed-register constraints |
inline-asm-kernel |
the kernel's privileged inline-asm instructions, decoded by objdump |
newlib-headers |
real newlib headers through the preprocessor and predefined macros |
embld-link |
EmbLD links freestanding programs that run on the host |
x86-disasm |
the x86-64 decoder behind -S and EmbDBG splits every function of tests/exec exactly as llvm-objdump does, and names x87, SSE2, cmov and bswap instead of printing .byte |
embld-bss-align |
__data_end and __bss_start on word boundaries for ARMv6-M, ARMv7-M and RV32, with every length of .data from 1 to 8: a startup's word loop from an odd address faults on a core without unaligned access |
embld-sections |
orphan sections gathered contiguously, with bracket symbols in both spellings |
embld-b1 |
EmbLD links a program against the EmbLinkOS runtime and newlib |
embld-embdbg |
EmbLD writes the .embdbg sidecar at link time |
embld-embdbg-multi |
several -g objects merged into one .embdbg |
embdbg-symbolize |
addresses to function and file:line |
embdbg-inspect |
parameters and locals in scope with their types |
embdbg-disasm |
instruction boundaries against objdump |
embdbg-crash |
a kernel fault dump turned into a diagnosis |
embdbg-embdbg |
the native .embdbg format round-tripped |
embdbg-tui |
the TUI's non-terminal fallback |
embx-embread |
embread against real EMBX images, intact and corrupted |
emblinkos-sdk |
the EmbLinkOS sval SDK compiled by EmbCC, run against a gcc-built driver |
emblinkos-cxx |
the OS's C++ demo compiled by EmbCC, run by the OS |
emblinkos-cxx-onos |
C++ compiled on EmbLinkOS by the EmbCC that runs there |
self-host |
EmbCC compiles all of its own sources and EmbLD links them; codegen is deterministic |
embbuild |
EmbCC built from its EmbBuild manifest |
embbuild-kernel |
the EmbLinkOS kernel built from its manifest and booted (opt-in, EMBCC_KM1=1) |
The OS-dependent tests in this directory skip when no EmbLinkOS tree or cross runtime is present.
aarch64 only (tests/golden/aarch64/)#
| Test | Checks |
|---|---|
arm64-encoding |
every aarch64 encoder, disassembled by aarch64-elf-objdump |
arm64-asm |
the inline-assembly vocabulary and the ARM kernel's templates against aarch64-elf-as |
Harnesses#
tests/harness/<arch>/ holds what it takes to run one program on one
machine: a link.sh that turns an EmbCC object into a bootable image, a
run.sh that boots it under QEMU, and the start-up code between reset and
main. Only the object under test comes from the test; the harness is
scaffolding.
| Harness | Machine | Program output | How the result gets out | Harness built with |
|---|---|---|---|---|
x86_64 |
qemu-system-x86_64 -cpu max -m 128M, a Multiboot image loaded with -kernel |
the debug console (-debugcon stdio) |
sys.c prints @@EMBCC-EXIT n@@; run.sh strips it and exits with n |
x86_64-elf-gcc and newlib |
aarch64 |
qemu-system-aarch64 -M virt -cpu cortex-a72 -semihosting |
semihosting | semihosting carries the exit status | aarch64-elf-gcc and newlib |
thumb |
qemu-system-arm -M lm3s6965evb -cpu cortex-m3 |
UART | output sentinel | EmbCC and embld |
thumb-m4f |
qemu-system-arm -M mps2-an386 -cpu cortex-m4 (with FPU) |
UART | output sentinel | EmbCC and embld |
thumb-m33 |
qemu-system-arm -M mps2-an505 -cpu cortex-m33 |
UART | output sentinel | EmbCC and embld |
thumb-m7 |
qemu-system-arm -M mps2-an500 -cpu cortex-m7 (double-precision FPU) |
UART | output sentinel; main's value printed as ==EXIT n ==, through exit() when exec.c links lib/libc |
EmbCC and embld |
riscv |
qemu-system-riscv32 or -riscv64 -M virt -bios none -m 8 |
UART | output sentinel; boot.c stops QEMU through the SiFive test device |
EmbCC and embld |
avr |
qemu-system-avr -M uno (ATmega328P), image loaded with -bios |
USART0 | output sentinel | EmbCC (including boot.S) and embld |
linux |
a real Linux kernel under QEMU, the program as /init |
the console | the kernel's panic message for PID 1 carries the exit status | the program is a static *-linux-gnu image |
Exit statuses from run.sh:
x86_64andaarch64return the guest's exit status,124when the run timed out, and125when the guest crashed or reset. Address zero is unmapped in both, so a null dereference faults; on aarch64 the vector table printsESR,FARandELRbefore exiting 125.thumb,thumb-m4f,thumb-m7,thumb-m33,riscvandavralways exit 0. A bare-metal image on these boards has nowhere to return to, so the run is judged by its output: each test program prints a sentinel whenmainfinishes (==END==in the shared embedded programs), and the test fails when the sentinel is missing.linuxexits 126 when no kernel is available, so the caller skips. Supply one astests/harness/linux/vmlinuz-x86_64or-aarch64, or name it withEMBCC_LINUX_KERNEL_X86_64orEMBCC_LINUX_KERNEL_AARCH64.
tests/harness/mips boots on QEMU malta (-cpu 24Kc, no FPU): the
image is linked at 0x80100000 in KSEG0 and loaded with -kernel, writes
the FPGA UART at 0xbf000900 (the third serial port), reports an exception
as ==FAULT cause N epc ... badvaddr ...==, and ends with ==EXIT n==
and a write to the FPGA's SOFTRES register, which -no-reboot turns into
QEMU's exit.
tests/harness/loongarch boots on QEMU virt (-kernel, direct address
mode): the image is linked at 0x1000000, above the boot information and
device tree QEMU keeps in the first 2 MiB, writes the UART at 0x1fe001e0
(the first serial port), reports an exception as ==FAULT ecode N subcode N era ... badv ...== through a b written into a 4 KiB-aligned page that
EENTRY points at, and ends with ==EXIT n == and the ACPI GED's
power-off (0x34 to 0x100e001c).
tests/harness/qrun.sh SECONDS [--until TEXT] CMD... runs QEMU under a
hard timeout. With --until TEXT it polls the guest's output and kills
QEMU as soon as TEXT appears, which turns a run that would cost the full
timeout into one that ends when the program does. The thumb-m33 and
avr run scripts pass EMBCC_QEMU_UNTIL through as --until; the
thumb-m7 one does too, and stops at its own ==EXIT line without it.
tests/harness/crt.c is the start-up shared by the x86_64 and aarch64
harnesses: it registers the unwind tables, runs .ctors and
.init_array, calls main, and leaves through exit so that atexit
handlers and stdio flushing run.
Referees#
| Reference | Used for | Tests |
|---|---|---|
the target's gcc (x86_64-elf-gcc or host cc, aarch64-elf-gcc) |
what a C program computes; half of each SysV/AAPCS64 ABI pairing; warnings that must agree | agrees-with-gcc, optimizer, regalloc-O2, exec-Os, sysv-abi, struct-abi-edges, cross-varargs, complex-abi, int128-abi, ldouble-abi, format-check, warnings |
the reference g++ and libstdc++ (tools/build-ref-gxx.sh) |
what a C++ program computes; mangling, layout and EH interoperability | cxx-agrees-with-gxx, cxx-abi, cxx-libstdcxx, cxx-std, cxx-format-check, eh-regions, unwind-through |
| clang | the embedded targets' reference compiler; Win64 and Apple arm64 conventions; predefined macros and data models for the targets gcc is not installed for | thumb-exec, riscv-exec, mips-abi, mips-exec, thumb-hardfp, thumb-m7-dp, win-abi, darwin-abi, predef, stdint, struct-layout |
llvm-mc |
the bytes of every instruction form the assemblers and encoders produce | thumb-asm, thumb-vfp, thumb-v6m-encoding, riscv-asm, riscv-compressed, riscv-encoding, mips-encoding, mips-asm, mips-link, avr-encoding, avr-asm |
llvm-objdump, aarch64-elf-objdump, objdump |
what the emitted bytes decode to | thumb-encoding, thumb-codegen, arm64-encoding, inline-asm-kernel, embdbg-disasm |
aarch64-elf-as, the GNU assembler |
assembling the same text EmbCC assembles; reassembling -S output |
arm64-asm, asm-S |
nasm |
EmbAS | assembler |
readelf, nm, file, the platform linkers |
object and image structure | relocations, symtab, data-symbols, macho, windows, linux |
| gdb | DWARF, read by a real debugger, on a running program | debug-line, debug-locals, debug-live, link-dwarf, debug-embedded |
| the host | arithmetic that has one answer at any register width: 64-bit integers, IEEE results bit for bit | avr-exec, avr-float, avr-softfp-host, the 64-bit and float halves of thumb-exec and riscv-exec, cvt-fold, double-pairs |
| a Linux kernel | syscall numbers, flags and structure layouts in lib/libc/os/linux |
linux |
| written rules | where no compatible compiler exists: avr-libc's calling-convention rules | avr-abi |
| simulation | ra_parallel_move over every small shape |
parallel-move |
A referee grades only what it is shown. A vocabulary generated from the
encoder's own tables (tools/thumbcheck, riscvcheck, avrcheck,
vfpcheck, a64check, tasmcheck, rvasmcheck, avrasmcheck,
mipscheck, mipsasmcheck) cannot
miss a form that was added to the encoder; an encoder function that is not
in the vocabulary is unchecked however many forms the vocabulary reports.
Where a reference assembler emits a placeholder (a relocated branch), the
comparison runs the other way: the reference disassembles EmbCC's bytes.
Beyond the suite#
Two tests are too long for make test and run on their own, before a
batch is marked ready and every night in CI:
- The fuzzer,
verify/fuzz/run.sh: random C programs with no undefined behavior, each checked on every QEMU board at every level against the host compiler's answer (verify/fuzz/README.md). - GCC's torture suite,
verify/torture/run.sh: the 3,900 files ofgcc/testsuite/gcc.c-torture, all code GCC accepts.verify/torture/fetch.shfetches them at a pinned commit (50 MB) intobuild/gcc-torture. Every file is compiled for x86-64, Cortex-M4, RV32 and AVR at-O0and-O2, and everyexecute/program is built for the host and run. The baselines inverify/torture/expected/say which files compile and which programs pass; one that did and no longer does fails the test. So does a crash -- a signal, an internal error or no answer in 60 s -- except those listed incrash-known.txt, each of which is a bug to fix and take off the list.--updaterecords the results after a change has been reviewed. The whole run takes about eight minutes on an M-series Mac;EMBCC_TORTURE_TARGETSandEMBCC_TORTURE_OPTSnarrow it.
Environment variables#
Suite and harness#
| Variable | Effect |
|---|---|
EMBCC |
the compiler under test; set by the runner, required by most golden tests when run by hand |
EMBCC_TARGET |
the target the suite runs for; read by tests/lib.sh. Not read by the compiler (see EMBCC_DEFAULT_TARGET) |
EMBCC_JOBS |
how many shell tests run at once |
EMBCC_QEMU_TIMEOUT |
seconds a guest may run (runner default 20; the embedded run.sh scripts default to 10 when run alone) |
EMBCC_QEMU_UNTIL |
a sentinel that ends a thumb-m33, thumb-m7 or avr run as soon as the guest prints it |
EMBCC_X86_RUNNER |
host or qemu: where x86-64 programs run |
EMBCC_KM1 |
1 enables embbuild-kernel |
EMBCC_LIBSTDCXX |
1 enables cxx-libstdcxx-embcc; EMBCC_LIBSTDCXX_LIB names a library already built |
EMBCC_QEMU_X86, EMBCC_QEMU_AARCH64, EMBCC_QEMU_ARM, EMBCC_QEMU_RISCV, EMBCC_QEMU_AVR |
the QEMU binary for each harness |
EMBCC_X86_NEWLIB, EMBCC_AARCH64_NEWLIB, EMBCC_X86_GCC, EMBCC_AARCH64_GCC, EMBCC_X86_LD, EMBCC_AARCH64_LD, EMBCC_REF_GXX |
the cross toolchains and newlib the x86-64 and aarch64 harnesses link with |
EMBCC_MYOS, EMBCC_MYOS_BUILD |
the EmbLinkOS tree and its build directory |
EMBCC_LINUX_KERNEL_X86_64, EMBCC_LINUX_KERNEL_AARCH64 |
the kernels the linux harness boots |
EMBCC_REF_GCC_THUMB, EMBCC_REF_GCC_RISCV, EMBCC_LLVM_MC, EMBCC_LLVM_OBJDUMP |
the reference compiler and LLVM tools the embedded tests use |
tools/hostpaths.sh is the one place the host layout is known: each path
tries the current machine's layout first and can be set outright from the
environment.
Compiler knobs#
These are read by the compiler itself. They exist for testing and for finding a miscompile; none of them is needed to use EmbCC.
| Variable | Effect |
|---|---|
EMBCC_VERIFY |
when set, the IR verifier runs after IR generation and after the passes of every optimizing compile, and stops the compile with an internal: error if a pass dropped a live value, left a stale scope index, or built an instruction without a source location; an optimizer that fails to converge is an error instead of a warning. The runner always sets it. |
EMBCC_RA_MAXPOOL=N |
the shared register allocator hands out only the first N registers of each pool. Run the exec goldens with small N (3, 1, 0) to reach the spilled-operand paths that a full pool reaches only in rare functions. |
EMBCC_NO_IDXOFF |
turn off moving a constant out of an array index into the access's displacement on RISC-V, for bisecting |
EMBCC_RA_WHY=1 |
one line per function on stderr accounting for every value that did not get a register |
EMBCC_RA_TRACE=1 |
every allocated or eligible value, with its live range, hint and location |
EMBCC_NO_SINKUPD |
leave a loop counter's update where it was instead of moving it next to the copy that ends it, for bisecting |
EMBCC_RA_DEGREE_SPILL=1 |
the previous spill choice (highest degree first), for bisecting a difference to it |
EMBCC_RA_POOL_K=1 |
the previous colourability test for a value that crosses a call (the whole pool, not the callee-saved registers), for bisecting a difference to it |
EMBCC_T_PAIRS, EMBCC_RV_PAIRS |
1 forces the 64-bit register-pair allocation on, 0 forces it off, on Thumb and on RISC-V; unset, the backend generates each function both ways and keeps the shorter. EMBCC_T_PAIRS_ONLY=FN and EMBCC_RV_PAIRS_ONLY=FN turn pairs on in function FN only. |
EMBCC_T_RA_MAX=N, EMBCC_RV_RA_MAX=N |
keep only the first N values in registers on Thumb or RISC-V; bisecting N names the one value whose register breaks a program |
EMBCC_NO_DIVMOD_CONST, EMBCC_NO_MLAKEEP |
on Thumb, stop pairing a quotient and remainder by one constant, or stop keeping a multiply's constant in a register for mla/mls, for bisecting |
EMBCC_AVR_RA_MODE=n |
force one AVR allocation mode; each mode is otherwise generated and the shortest kept, so a mode that never wins is still tested |
EMBCC_AVR_RA=1, EMBCC_AVR_RA_ONLY=FN, EMBCC_AVR_RA_LIMIT=K |
AVR allocation report, one function only, first K homes only |
EMBCC_NO_MEMOFF, EMBCC_NO_RMW, EMBCC_NO_MLA, EMBCC_NO_SPLITLOOPS, EMBCC_NO_TAILCALL, EMBCC_NO_LKCONST |
turn off one transformation (constant offsets folded into loads and stores on Thumb and RISC-V; x86-64 read-modify-write fusion; multiply-accumulate fusion on aarch64 and Thumb; loop live-range splitting; tail calls on aarch64, Thumb and RISC-V; compares decided from a constant the block has just written) for bisecting |
EMBCC_RV_JAL_RANGE=BYTES, EMBCC_RV_LONG_CALLS |
shrink the reach the RISC-V backend assumes for jal, or (when set) call functions in the same unit with auipc+jalr instead of jal, to test the long-call path without a megabyte of code |
EMBCC_T_FPU |
1 or 0 overrides whether the Thumb backend uses the FPU |
EMBCC_T_EXT |
1 keeps a Thumb attempt with r9-r11 in the pool whenever its scratch roles found registers, 0 never tries one; unset, it competes on size with the others. tests/golden/thumb-ext-pool.sh runs the exec programs with 1. |
EMBCC_T_NOWIDEIMM, EMBCC_T_NOCMP64IMM, EMBCC_RV_NOWIDEIMM, EMBCC_NO_SIGNTEST, EMBCC_RV_NOCMPIMM |
build a 64-bit constant whole on Thumb or RV32; keep x >> 63 a shift, and x & K eight bytes wide, before a branch; load a RISC-V value compare's constant into a register; for bisecting |
EMBCC_NO_JUMP_TABLES |
compile as with -fno-jump-tables whatever the command line says: run an exec golden with it set to take every dense switch through the compare tree |
EMBCC_VECDEBUG |
the vectorizer prints, on stderr, each loop it considers and why it rejected it |
EMBCC_NO_FULLUNROLL |
a loop with a constant trip count is unrolled like any other instead of being copied whole, for bisecting |
EMBCC_DEFAULT_TARGET and EMBCC_PREFIX are user-facing and documented in
Invoking EmbCC.
Which tests a change needs#
| Change | Run |
|---|---|
| anything, while working | make check |
one backend (src/arch/<arch>/ and its tests) |
make check, then that target's golden tests (for Thumb: thumb-*, arm-abi-tags; for RISC-V: riscv-*; for AVR: avr-*), plus regalloc-O2 and the embedded shared tests it can affect |
shared code (src/opt, src/ir, src/sema, src/parse, src/arch/regalloc.c, src/arch/target.c) |
make test and make test-arm64, and tools/x86-identity.sh when x86-64 output is not meant to change |
| register-allocator, scratch-register or spill changes | the above with EMBCC_RA_MAXPOOL at 3, 1 and 0 |
C++ front end or lib/libcxx |
make test, make test-arm64; make test-libstdcxx at milestones |
Capture a suite's full output to a file and read the FAIL lines from it;
do not pipe a suite through tail or head, which cuts the failures (they
come before the summary) and can kill the run early. Do not rebuild
embcc or edit the tree while a suite is running: make test rebuilds
the compiler, and the tests read the sources.