09 - EmbCC Toolchain Profile

Status: Compiler capability review refreshed against EmbLinkOs/EmbCC at a3c3ef4c81f9de977b8741ed16072cadca921788, 2026-10-14, superseding the 387eb23 snapshot. The ten-item review names current manuals, implementation/regression evidence and scoped local checks, and separates them from upstream-reported RTOS experiments. The consequence lines remain RTOS design rules unless explicitly amended; a compiler improvement does not silently relax them. Current EmbCC RTOS integration is incomplete and is not a configured CI leg.


1. What EmbCC is

EmbCC is a C and C++ compiler written in C99 with its own preprocessor, per-target assemblers, linker (embld), archiver (embar), SVD tool (embsvd), debugger (embdbg) and additional firmware tools. It targets embedded systems and hosted/bare-metal operating-system programs; one compiler handles each supported target. Runtime, C-library and supported C++ runtime builds exist upstream, including a separate AVR library. They must be built/installed for the selected target; their availability does not introduce a libc dependency into the RTOS kernel. (README.md, docs/manual/targets.md, docs/manual/getting-started.md)

2. Targets that matter to EmbLinkRTOS

Family

Triples

Notes for the RTOS

Cortex-M, Armv6-M

thumbv6m-none-eabi (Cortex-M0, M0+, M1)

Thumb-1 lowering; divide, 64-bit multiply, and atomics are librt.a calls; the atomic helpers mask with PRIMASK, which does nothing in unprivileged Thread mode, so an isolated profile on Armv6-M would need its own atomics

Cortex-M, Armv7-M

thumbv7m-none-eabi (M3)

Reference port target

Cortex-M, Armv7E-M

thumbv7em-none-eabi, thumbv7em-none-eabihf (M4, M4F, M7 with -mcpu=cortex-m7 for FPv5-D16)

Hard-float objects do not link with soft-float ones; one librt.a per float ABI

Cortex-M, Armv8-M Mainline/Baseline

thumbv8m.main-none-eabi[hf] (M33), thumbv8m.base-none-eabi (M23)

Stack-limit assembly and M23 selection are implemented. Secure-side -mcmse supports both profiles with soft float; the FPU combination is refused. Compiler support does not establish an RTOS secure port or stack-overflow hardware evidence

RISC-V

riscv32-unknown-elf (default RV32IMAC, ilp32), riscv64-unknown-elf (default RV64IMAC, lp64)

ISA/ABI options include F/D and hard-float runtimes upstream; the current RTOS port uses its own RV32IMAC scope. Consult docs/manual/targets.md for accepted -march/-mabi combinations

AVR

avr, with -mmcu=atmega328p, atmega328, atmega168p, atmega168 upstream

double is 32-bit. Generic const still costs SRAM; __flash and avr-libc-compatible progmem are implemented. Current RTOS EmbCC macros still use generic fallback reads, so flash placement needs explicit integration

MIPS32

mipsel-none-elf (MIPS32r2, PIC32 class)

A candidate fifth architecture port; not planned

Hosts

x86_64-linux-gnu, x86_64-apple-darwin, aarch64-*, x86_64-windows-gnu (incomplete)

C++ supported on x86-64 and AArch64 only

The RTOS reference targets above are little-endian; EmbCC also has other, including big-endian, targets outside this scope. C++ is implemented on Cortex-M/32-bit ARM and RV32 with -fno-exceptions; AVR C++ remains refused. Use current docs/manual/targets.md and docs/manual/cxx.md, plus regression fixtures, rather than older contradictory C-only rows in docs/internals/status.md.

Consequence. EmbCC supplies compiler targets for the reference architecture families. Each RTOS compiler/port combination still needs its own storage, linking, ABI and executed conformance evidence. GCC/Clang remain the active matrix; a target’s existence does not establish an EmbCC CI job or qualify a port.

3. Language level

  • One dialect: C17 plus GNU extensions plus part of C23; -std= is accepted and ignored, __STDC_VERSION__ is 201710L. _Static_assert, _Generic, _Noreturn supported; _Alignas partial; _Atomic for integer and pointer types only; <stdatomic.h> and __atomic_*/__sync_* builtins provided. (docs/manual/c-language.md)

  • _Thread_local and __thread compile to one shared instance on every embedded target, without an error. (docs/internals/status.md §Thread-local storage)

  • __STDC_HOSTED__ is 1 even with -ffreestanding. Empty parameter list () always means no parameters. A non-void function that can reach its closing brace is an error. Block-scope tags and typedefs stay visible to the end of the translation unit. (docs/manual/c-language.md)

Consequences.

  • The C11 minimum of ADR-023 is enforced by the GCC and Clang legs of the matrix with -std=c11 -pedantic; EmbCC cannot enforce it.

  • The kernel and every port never use _Thread_local or __thread. Per-thread data is kernel TLS slots only (KRN-THR-013, KRN-THR-014). A static-analysis rule bans the keywords tree-wide.

  • The kernel does not test __STDC_HOSTED__.

4. Attributes and builtins

Supported where the RTOS needs them: naked (Cortex-M, RISC-V, MIPS32, AVR), interrupt (Cortex-M: accepted, no code change; AVR: implemented; RISC-V: refused), signal (AVR), section (static storage), used, weak, alias (functions), aligned (including typedefs and supported over-aligned locals), packed, constructor (including priorities), noreturn, always_inline, pcs. Provided builtins include __builtin_memcpy/memset/memmove/memcmp, __builtin_trap, __builtin_expect, the atomics, __builtin_sqrt, __builtin_fabs. (docs/manual/extensions.md)

Some extensions remain refused, including cleanup, weakref and ifunc; consult current docs/manual/extensions.md for exact attribute/builtin diagnostics. Automatic storage cannot have a linker section. The old refusal of constructor priorities and typedef alignment is no longer a compiler limitation.

Consequences.

  • Device and subsystem initialization order remains a generated table (DRV-001), never constructor priorities, although the compiler now implements them.

  • No scope-guard macros built on cleanup.

  • Alignment remains on object declarations and struct definitions, never typedefs, as the RTOS portability policy. EmbCC now supports typedef alignment; the storage-member alignment correction of M1 remains necessary independently of that earlier compiler restriction.

  • Register-file-wide bit operations use the emb_arch_ layer (clz, ctz, popcount) with compiler-portability fallbacks; the exact __builtin_clz family availability is verified by a probe at configure time.

5. Inline assembly and naked functions

  • Inline asm is assembled by EmbCC’s own assembler per target with a fixed instruction vocabulary (documented per target). Labels are not accepted inside inline asm; branch targets are .+N. Only r/m/i-style constraints; callee-saved registers may not be named or clobbered in a template (r4-r11 on Cortex-M, s0-s11 on RISC-V, r2-r17 and Y on AVR). An asm output wider than a register is refused.

  • Cortex-M vocabulary includes mrs/msr on the kernel registers and Armv8-M stack limits; barriers, masking, sleep, context-save and FPU sequences; and now ldrd/strd, clrex and byte/halfword exclusives on applicable cores. Core-specific restrictions still apply; use docs/manual/inline-asm.md and the encoding/RTOS goldens before relying on another instruction.

  • RISC-V vocabulary covers CSR access by name or number, mret/sret/wfi/ecall/fence, loads, stores, ALU, branches. Missing in inline asm: lr/sc/amo* (the compiler emits them for C atomics), la/call/tail pseudo-instructions, floating point. .S files use the same assembler and do accept call.

  • AVR vocabulary is the whole AVR5 set, including in/out, cli/sei, push/pop, reti, sleep, wdr, lpm.

  • A naked function body is assembled as a file-scope block: it may hold labels, literal pools, and zero-argument calls into C; it may not hold other statements or asm outputs. FreeRTOS’s Cortex-M3, M4F, M7, M33 non-secure, and RISC-V ports compile unmodified and run on QEMU. (docs/manual/inline-asm.md, docs/manual/embedded.md §An RTOS kernel)

Consequences.

  • The architecture-port contract (02 §2) is implementable on all three EmbCC embedded families in C plus inline asm, with the context switch and first-thread launch as naked functions and the RISC-V trap entry as a .S file.

  • Every critical-section primitive is static inline __attribute__((always_inline)) with a "memory" clobber (the inliner otherwise declines functions containing inline asm).

  • Kernel atomics are C11 atomics through the portability layer, never hand-written lr/sc or ldrex/strex templates, so one source serves EmbCC, GCC, and Clang.

  • No labels in inline asm: any multi-block assembly sequence lives in a .S file or a naked function.

6. Atomics and width limits

Target

Atomic load/store

Atomic read-modify-write

Cortex-M Armv7-M, Armv8-M

1 to 4 bytes

1 to 4 bytes inline (ldrex/strex)

Cortex-M Armv6-M

1 to 4 bytes

1 to 4 bytes via librt.a (PRIMASK masking)

RV32

1 to 4 bytes

1, 2 and 4 bytes; subword RMW operates on the aligned containing word

RV64

1 to 8 bytes

1, 2, 4 and 8 bytes

AVR

1 to 8 bytes, masking as needed

1 to 8 bytes inline with saved/restored SREG masking; not lock-free

Eight-byte atomics on 32-bit targets now call sized __atomic_*_8 runtime helpers. The default weak helpers mask privileged local interrupts: they are not a multicore/DMA or unprivileged atomicity solution. AVR’s wide operations are inline masking sequences. Computed goto is now implemented upstream; the kernel prohibition remains policy. __int128 still does not exist on 32-bit targets. (docs/manual/targets.md, docs/manual/embedded.md, docs/manual/extensions.md)

Consequences.

  • 64-bit kernel time remains read/written under a critical section or a sequence lock, never as an atomic 64-bit object (KRN-TIM-016); helper availability does not change this protocol.

  • Kernel atomic state is word-sized. Flags packed into bytes are manipulated as words or under a critical section.

  • The tiny profile’s atomics are interrupt masking by definition; the portability layer makes that explicit rather than relying on compiler fallbacks.

  • No computed-goto dispatch anywhere in the kernel.

7. Linking, sections, and images

  • embld links ARM, RV32, RV64, MIPS32, AVR, and x86-64 objects; AArch64 needs an external linker. Static links only, no PLT, ET_EXEC, W^X segments.

  • Without a script: -Ttext, -Tdata, -Tstack (RISC-V entry stub), --rom-limit, -e, --gc-sections, -Map, --print-memory-usage; .vectors/.isr_vector placed first; orphan sections bracketed by __start_NAME/__stop_NAME or __NAME_start/__NAME_end.

  • GNU ld linker scripts (-T) now include AVR support. The supported subset includes MEMORY, SECTIONS, KEEP, PROVIDE, ALIGN, AT>, (NOLOAD), /DISCARD/, ASSERT, INCLUDE, REGION_ALIAS, EXTERN, sorting, fill and expression functions. AVR scripts must respect the separate program/data address spaces. PHDRS, OVERLAY and INSERT remain refused; check the current linker manual for the exact subset.

  • -ffunction-sections, -fdata-sections, --gc-sections, -fstack-usage (GCC-format .su files) and embcc inspect callgraph are available. LTO, PIC and stack-protector assumptions remain excluded from RTOS builds. -fshort-enums is now available for C but remains prohibited by the RTOS ABI policy. -fsanitize=undefined traps in place. -Os exists; -O0 code may not fit the ATmega328P. DWARF 4 now includes AVR functions/globals/lines, but no AVR .debug_frame; optimized debug builds can grow substantially. (docs/manual/tools/embld.md, docs/manual/targets.md, docs/manual/invoking.md)

Consequences.

  • HW-006 still targets a GNU ld script plus an AVR compatibility embld option set (-Ttext/-Tdata/--rom-limit) from the same region model. Current EmbCC can consume the AVR script, so the option set is a retained design/compatibility target, not a missing-linker-script workaround. The generator remains planned; removing the second output requires a separate reviewed amendment.

  • Kernel registries (device tables, trace points, log sites, partition tables) are orphan or named sections bracketed by linker symbols; this works identically under embld and GNU ld.

  • Deferred-format logging (ADR-009) targets strings as symbols whose address is the identifier, placed in a NOLOAD dummy region. AVR linker scripts are now available; the tiny plain-text backend remains a footprint/design choice, not proof that deferred logging has been implemented or qualified on AVR.

  • Stack sizing methodology (KRN-MEM-009) uses -fstack-usage plus the call graph on all three compilers.

  • Footprint claims are made at -Os and -O2; -O0 is a debug configuration only.

  • Reproducible builds (BLD-004) are helped by EmbCC: -g records no directory, and output is deterministic by design.

8. Startup, vectors, and interrupts per target

Target

Startup

Vector table

Handlers

Cortex-M

C Reset_Handler; hardware loads SP from the table

const array in .vectors/.isr_vector with used

Ordinary C functions (hardware saves the caller-saved set); interrupt attribute accepted as a no-op; context switch and first-thread start are naked

RISC-V

C _start after an embld -Tstack stub or a .S entry

mtvec set by startup

Trap entry in .S (save caller-saved registers, call C, mret); interrupt attribute refused

AVR

.S boot: set SPH/SPL, clear r1, copy .data, zero .bss

26 jmp entries at address 0, weak __vector_n

signal (interrupts stay off) or interrupt (re-enabled at entry)

(docs/manual/embedded.md)

Consequence. The architecture-port contract documents the startup form per target as above; SoC startup on Cortex-M stays in C.

9. Tools around the compiler

Tool

What it is

Use in EmbLinkRTOS

embld

The linker (see §7)

Reference linker for EmbCC builds

embdbg

DWARF 4 reader, symbolizer, crash analyzer, TUI and GDB remote client, now including AVR register layout

AVR tool support is available; RTOS-awareness via the planned debug descriptor and full target sessions still need their own evidence

embsvd

CMSIS-SVD reader with nested clusters/arrays/derivedFrom; device header, startup/linker outputs and structured --json

HW-004 can build on the implemented JSON export; mapping it to curated RTOS SoC YAML/schema remains an unimplemented adapter

embflash

Flash client through GDB servers (OpenOCD, pyOCD, J-Link, Black Magic Probe, QEMU)

Available upstream; RTOS board/programmer integration remains to be tested

embas, embar, embread, embls, embidx

NASM-syntax x86-64 assembler; archiver; ELF and EMBX reader; listing and index tools

Build support

EmbBuild

A typed-manifest build (.ebm: name, kind, inputs, args, output per target) walked by EmbLinkOS’s EmbBuild; manifests are generated, with header closures derived by the compiler; a host reference walker exists (tools/embbuild-run.sh)

Answers Q20: EmbBuild exists as a format and walker, not an IDE build system. The CMake reference build (BLD-001) gains a target that emits an .ebm manifest, so EmbLinkRTOS builds on EmbLinkOS itself

EMBX

EmbLinkOS’s native, capability-carrying executable format, written by embld --embx --cap NAME from the same linked layout as the ELF

Design input for partitions: the per-partition manifest of granted capabilities (03 §8) should use EmbLinkOS’s capability vocabulary where the concepts coincide, so tooling can be shared; MCU boot images stay MCUboot-compatible (ADR-008)

QEMU harnesses

lm3s6965evb, mps2-an386, mps2-an500, mps2-an505, micro:bit (Armv6-M), RISC-V virt, AVR, MIPS malta, with semihosting exit

Added to the CI emulation layer alongside Renode (SIM-002)

EmbFlash is now developed upstream; the 2026-10-07 absence is obsolete. Existing OpenOCD/pyOCD, picotool/UF2 and avrdude workflows remain useful. The RTOS emb facade (BLD-005), board-specific flashing and HIL integration remain planned and must be verified separately.

10. Gaps to track in EmbCC

The original ten-item compiler backlog is implemented upstream within its stated scope: (1) Armv8-M stack limits, (2) AVR scripts, (3) AVR __flash/program-memory support, (4) AVR DWARF, (5) AVR debugger layout, (6) small RV32/AVR atomics, (7) soft-float CMSE, (8) M23/Baseline, (9) SVD clusters/arrays/JSON, and (10) Cortex-M/RV32 C++ without exceptions. The closure matrix and local evidence preserve each item’s identity and remaining limits; do not erase the historical snapshot or infer complete RTOS qualification.

The layout-marker correction supplies EMB_LAYOUT_MARKER and verifies storage/assertion/kernel-archive compilation on eight configurations, with byte-identical native headers against GCC. The subsequent Linux native integration supplies explicit experimental EmbCC-kernel/GCC-host presets, a layout guard and local executed conformance/model evidence. All-EmbCC POSIX host compilation remains blocked by glibc header compatibility; AVR flash/registration macros, RTOS YAML adaptation, C++ header/runtime checks and pinned compiler/port conformance jobs remain separate work. Compiler TLS remains a shared embedded instance; default wide-atomic helpers are privileged local-mask operations. CMSE FPU/stack-crossing restrictions, AVR C++ refusal, debug unwinding/size limits and unrun physical measurements remain explicit.

11. Toolchain matrix for 1.0

Architecture

EmbCC

GCC

Clang

native (host)

Experimental Linux x86-64 EmbCC kernel / GCC POSIX host passes local layout, conformance and model checks; all-EmbCC host compatibility and pinned compiler CI remain open. Upstream macOS experiments remain scoped reports

yes

yes

avr

Compiler/script/flash/debug capabilities available; full RTOS run unqualified

avr-gcc

Clang AVR (experimental)

cortex_m

Compiler M0+/M3/M4/M4F/M7/M23/M33 targets; upstream M3/M4 RTOS experiments are scoped reports, not current CI qualification

arm-none-eabi-gcc

Clang with --target=arm-none-eabi

riscv

RV32/RV64 compiler targets; upstream RV32 RTOS experiment reported, current RTOS compiler leg unqualified

riscv-none-elf-gcc

Clang

Every leg builds the same sources. Compiler-specific attribute and builtin spellings live only in include/emb/compiler/ (BLD-002). Differences recorded here are the first contents of that directory’s EmbCC header.