Skip to content

CLI flag reference

Every flag the xcc driver accepts, grouped by purpose. For the flat listing the compiler itself prints, run xcc -h.

Terminal window
xcc -o prog prog.xc

This is a complete invocation. With no -A, xcc builds a native executable for the machine it is running on, finds the standard library relative to its own binary, and optimises at -O3. A simple program needs nothing else.

Terminal window
xcc [options] <input.xc>

xcc compiles one source file at a time. A second .xc on the command line is an error:

xcc: error: multi-file inputs not supported on the new-IR path yet

To build a program from several source files, compile each one to an object with -c, then link the objects in a separate invocation:

Terminal window
xcc -c -o main.o main.xc
xcc -c -o util.o util.xc
xcc -o prog main.o util.o

Objects (.o) and archives (.a) may be named together on the link line, but not alongside a source file. -c is available on arm64 (including ios and ios-sim), x86_64, win64 and arm9. On 6502, m68k and wasm32 a program is compiled from one file; use #include to pull in the rest of its source.

FlagEffect
-o <path>Output file. On a native target this is a runnable executable unless the path ends in .s (assembly) or .o (object). On 6502 and m68k the extension picks the container (see below). The long form is --output.
-cCompile and assemble to a relocatable object (.o), but do not link.
-SStop after code generation and write assembly to the -o path, as cc -S does.
-E <path>, --preprocessed <path>Write the preprocessed source to <path> and continue. Shows what the lexer sees.
-I <path>Add an include-search path. Repeatable. The long form is --include.
-D <name>[=<value>]Define a preprocessor symbol. -D DEBUG is #define DEBUG 1; -D LEVEL=3 defines it as 3. The name may follow as a separate argument or be joined to the flag: -D DEBUG and -DDEBUG are the same.
-q, --quietSuppress informational output. Errors and warnings still print.
-V, --verbosePrint the resolved support root and every include path at startup. First stop when Cannot find include file fires.
-v, --versionPrint the version and exit.
-h, --helpPrint the full flag listing and exit.

Output containers on the non-native targets:

ExtensionFormat
.asmassembly source (stops before the assembler)
.xex .exe .bin .combanked 6502 executable (.xex)
.tos .prgGEMDOS executable (m68k)
FlagEffect
-A <arch>Target architecture. With no -A, xcc builds for the machine it is running on. The long form is --arch.
-ATargetOutput
(none)the host you are onnative executable
arm64macOS / Linux on 64-bit ARMMach-O / ELF; run it
ios / ios-simiOS device / simulator (arm64)Mach-O; sign with xcc-sign, install on device/simulator
androidAndroid (arm64)with --emit-apk, a signed .apk
x86_64Linuxdynamically linked glibc ELF that can load GTK 4, libGL and other system libraries; run it. With -static, a static ELF over musl that runs on any distribution. (From 0.72; up to 0.71 the static ELF is the default and -dynamic, from 0.66, gives the glibc one.)
win64WindowsPE/COFF .exe, or a DLL (see --emit-lib)
arm9AArch32 / XTOSELF, or a .so (see --emit-lib)
m68kMotorola 68000GEMDOS .prg/.tos; run under xcc-sim-68k. -A 68000 is the same target, and -A 68030 builds for the 68030 (run with xcc-sim-68k --cpu 68030).
wasm32WebAssembly.wasm / WAT
6502banked xt6502banked 6502 executable (.xex); run under xcc-sim-6502 -m xt

-A and -m are orthogonal: -A picks the instruction set, -m picks the memory layout within it. Only the 6502 path has layouts to choose.

These apply when xcc produces a native executable or library. By default it assembles, links and (on macOS) signs in-house, with no system assembler, linker or clang.

FlagEffect
-l<name>Link a system library, for example -lobjc. On -A x86_64 the dynamic link takes lib<name>.so (or .a) from the -L path and the standard system library directories; a -static link takes lib<name>.a from the -L path. -lc, -lm, -lpthread, -ldl and -lrt name the C library itself and need no file.
-staticFrom 0.72. On -A x86_64, link the executable statically over musl instead of dynamically against glibc: one file with no dependencies, which runs on any x86-64 Linux, musl-based distributions included. It cannot load shared system libraries such as GTK 4. From 0.73, with --emit-lib it builds the musl library earlier releases made, for programs linked -static.
-dynamicFrom 0.66. On -A x86_64, link the executable dynamically against glibc: what a program needs to load GTK 4, libGL or any other shared system library. From 0.72 this is the default and -dynamic only names it; up to 0.71 the default is the static musl link. Still linked in-house: xcc knows glibc’s exports from a table in its support tree, so a Mac can link for Linux, and an -l library is read for its exports. A symbol that neither glibc nor an -l library defines is a link error. Cross-linking names a copy of the libraries with -L. From 0.73 a library built with --emit-lib follows the same rule (see --emit-lib); up to 0.72, executables only.
-framework <F>Link a macOS framework, for example -framework AppKit.
-Xlinker <file>Link a library or object file named by path.
-Wl,<arg>[,<arg>…]The same, in the form clang users write. xcc links in-house: a file is linked, -rpath <dir> adds a run-path entry on arm64 and iOS, and any other linker flag is ignored with a note. -Xlinker takes the same arguments.
--self-hostIn-house assemble + link + sign. This is the default; the flag is accepted but has no effect.
--no-self-hostHand the link to the platform toolchain instead: clang for arm64 macOS, the Android NDK’s clang for -A android (executables and --emit-apk), and arm-none-eabi-gcc for arm9. Those tools must be installed. x86_64, win64, iOS and arm64 libraries link in-house only; m68k, 6502 and wasm32 images are always written in-house.
-fpic, -fPIC, -mpicPosition-independent code. On m68k it selects the GOT/a5 model, which lifts the 32 KB limit on a 68000 program. arm64, android and arm9 code is always position-independent, and --emit-lib implies it.
FlagEffect
--emit-libEmit a shared library instead of an executable, together with a sibling .xtc.iface describing the classes, protocols, structs and enums it exports. Implies -fpic. From 0.73, on -A x86_64 the library links against glibc, as an executable does: each -l library it names becomes one of its dependencies, so a program that imports it loads them too, and it exports only its own API. A program that imports it links against glibc.
-L <path>Add a search path for #import <Lib>, which resolves to lib<Lib>.so and reads its interface (or, for a C library, its DWARF). Repeatable. The long form is --library-path.
Terminal window
xcc --emit-lib -o libXtg.so xtg.xc # build the library
xcc -L . -o app app.xc # build a client against it

#import <Lib> type-checks the client against the actual binary, so there is no header to fall out of sync. It also works on a plain C .so, whose DWARF supplies its functions, types and enum constants. See Modules & shared libraries.

FlagEffect
-H <path>Root holding the support tree. Rarely needed, because xcc finds it relative to its own binary. See Install.
-m <layout>Select a memory layout. -m xt is the banked 6502 map and implies -A 6502. There is no default: with neither -m nor -A, xcc targets the host. The argument is a built-in layout name, or the path of a .lnk file (.lnk is appended if missing). The long form is --memory-model. See Linker scripts.
-ll, --list-layoutsList every built-in layout, grouped by platform, and exit.
-dl, --dump-layoutPrint the active layout’s memory-map diagram and exit. Use with -m.
-dp, --dump-placementxt6502: after code generation, print where each function went (main, bank <n>, irq or vbi) and its size, then the bytes of generated code in main RAM and in each code bank, to stderr. The sizes are the compiler’s estimates, an upper bound.
-du, --dump-usagext6502: after assembly, print the bytes the program uses in every region and code bank of the layout, to stderr: zero page, the software stack, the system, screen and main regions, each code bank in use, the unused code banks as one range, and the data window. With -S the assembly is written and also assembled to measure it.

See Memory models.

FlagEffect
-O0No optimisation. A debug aid; the production level is -O3.
-O1, -ORemoves unreachable functions.
-O2The full optimiser: inlining, constant folding, dead-code elimination, if-conversion, loop unrolling, vectorisation on arm64, x86_64, win64, arm9 and wasm32, strength reduction, loop-invariant code motion and block layout.
-O3The default. Currently the same pipeline as -O2.
-Flu <n>, --fn-loop-unroll <n>Fully unroll counted loops whose constant trip count is at most n. The default depends on the target; see Optimisation.
-Fli <n>, --fn-leaf-inline <n>Max leaf-function size (instructions) eligible for inlining. Default 100; needs -O2+.
-Fmb <n>, --fn-min-banked <n>xt6502: keep a function of fewer than n instructions in main RAM instead of a code bank, so a call to it needs no bank switch. The default, 0, banks every function except the entry point and the interrupt handlers. See Optimisation.

Full discussion on Optimisation.

FlagEffect
-falloc=heapCoalescing free-list allocator; supports delete. Every supported target has a heap region, so this is the allocator they all use.
-falloc=bumpAccepted for older build scripts. No supported target uses the bump allocator, so xcc warns and builds with the heap.
-farc[=on|off]Retired. ARC is always on. xcc accepts the flag and warns that it does nothing.
-fthread-safe-arcForce atomic ARC refcounts, so two threads can share an object.
-fno-thread-safe-arcForce plain, non-atomic refcounts.

Atomic refcounts are decided per module and switch on when the module spawns a thread. These flags override that choice. See Allocator & ARC and Threading.

FlagEffect
-mhard-float, -mfpuUse the 68881/68882 FPU for float and double.
-msoft-floatFloating point in software. The default.

arm9 code always uses VFP, so -mhard-float is its default and -msoft-float is reported and ignored.

-S is not a stack flag; it keeps the assembly (see Inputs and outputs).

FlagEffect
--xtc-stackxt6502: every function keeps its return address and saved registers in a frame on the xcc software stack instead of on the hardware stack. A function marked :hwStack keeps the hardware-stack convention; :xtcStack selects the software stack for one function without the flag. See Functions.
-ss <n>, --stack-size <n>Cap the xcc stack at n bytes (decimal, $hex or 0xhex; 1..65535). No effect on banked-heap or non-heap targets, which is all of the current ones.
FlagEffect
-Q <rts|loop>, --quit-style <rts|loop>xt6502: what the program does when main returns. rts, the default, returns to the loader (DOS) with main’s value in A, and xcc-sim-6502 exits with that value as its status. loop makes the program jump to itself forever; the simulator stops there, also with main’s value as its status.
FlagEffect
--emit-irDump the IR after lowering, to stderr. Does not change the generated code.
--emit-ir-optDump the IR after the optimiser, to stderr.
FlagEffect
-fltoLink-time optimisation: recompile the whole program from its IR as one module.
-fbounds-checkBuild with subscript bounds checking; see Checked builds. arm64 only so far.
--sign <identity.pem>Sign the output with a developer identity (iOS/macOS); pair with --sign-entitlements <plist>. See also the standalone xcc-sign.
--emit-apkOn -A android, package a signed .apk. --sign-key <path> names the signing key.
--needed <soname>On -A android, add a DT_NEEDED entry naming <soname>. Repeatable. A payload that calls into a companion .so must name it.
--with-lib <path>With --emit-apk, store a prebuilt lib<name>.so in lib/arm64-v8a/ beside the payload. From 0.73 it may be given more than once, one library each time; up to 0.72 only the last one is stored.
--lib-name <name>With --emit-apk, the library the system loads first (android.app.lib_name). Default: the payload.
--with-dex <path>With --emit-apk, package this classes.dex and mark the manifest hasCode="true".
--manifest-attr <name>=<value>(from 0.7) With --emit-apk, set an attribute on the manifest’s <application>; repeatable. label (which also names the activity the launcher shows), and the booleans debuggable, allowBackup, hardwareAccelerated, largeHeap, usesCleartextTraffic, resizeableActivity, requestLegacyExternalStorage and enableOnBackInvokedCallback.
--emit-ifaceWrite the module interface (the .xtc.iface description) to the -o path, or to standard output with no -o, and stop. -c and --emit-lib produce the interface as part of their output without this flag.
-mavx2, -msimd=avx2From 0.66. On -A x86_64 and -A win64, vectorise with 256-bit AVX2 instead of the 128-bit SSE2 baseline. The program then needs an AVX2 CPU (Intel Haswell, AMD Zen and later).
-mavx512, -mavx512f, -msimd=avx512From 0.71. On -A x86_64 and -A win64, vectorise with 512-bit AVX-512: F, DQ, BW and VL, which every AVX-512 CPU since Intel Skylake-SP and AMD Zen 4 has. The program then needs such a CPU.
-msimd=autoFrom 0.66, the default on -A x86_64 and -A win64. Each function the vectoriser widens is built for SSE2 and for AVX2, and from 0.71 for AVX-512 too, and the program picks one at load from what the CPU and OS support, so one binary runs the widest unit the machine has and still runs everywhere. Setting XC_SIMD=base, XC_SIMD=avx2 or XC_SIMD=avx512 in the environment forces a level for one run; forcing one the machine lacks falls back to the best it has, with a note on stderr.
-msimd=baseSSE2 only, one version of every function: the smallest binary.
-mnativeThe vector level of the machine running xcc: avx512 (from 0.71) or avx2 where the CPU and OS support it, otherwise the baseline. Refused when xcc is not running on x86-64, where there is no host level to read.
-fno-matmulFrom 0.71. At -O2 and above, a dense float or double matrix multiply (C[i][j] = Σ A[i][k] · B[k][j] written as three loops) runs as a matrix kernel: on Apple Silicon macOS on the SME matrix unit when the CPU has one (Apple M4 and later), on x86-64 Linux with SSE2, AVX2 or AVX-512 vectors, picked like the rest of the program’s vector code. The results are the loops’, bit for bit. When the matrices overlap or hold a NaN, or on a Mac without SME, the loops run as written. This turns it off.
-fmalloc=system|mimallocChoose the C heap behind the runtime. mimalloc is -A x86_64 only, and from 0.72 needs -static: the mimalloc object is linked ahead of libc, so its malloc family replaces musl’s.
-gWrite DWARF debug information (line table, functions, call frames) into the executable, for lldb and gdb (from 0.74; arm64, x86-64 and win64). See Debugging.

xcc --help prints the complete flag list.

-fbounds-check compiles a program that range-checks its subscripts. It is a debug-time build: the checks cost code and time, and they are not meant to be left on in what you ship.

What each kind of subscript is checked against:

The baseChecked against
an array with a declared length (u16 a[8]), local or globalthat length
an array sized by its own initialiser (u16 a[] = { 1, 2, 3 })the inferred length
a heap allocation (new T[n])the count in its own allocation header
a bare pointerthe allocation header, so the check means something only if the pointer really points at one

A failing check prints the site, the real bound, and a symbolised stack, then aborts:

=== xcc: out-of-bounds access ===
at grid.xc:9:8
array: index 9, but it holds 5 elements
stack:
#0 _xt_check_bounds_n +176
#1 main +88
#2 xtc_start +16

The flag is implemented for arm64 so far. On a target that does not have it xcc stops with an error rather than quietly building an unchecked program.

Suppress a category with -Wno-<category>. All are on by default.

CategoryTriggered by
asm-clobbersan asm{} block’s clobbers annotation disagrees with the registers the compiler thinks it touched
class-inita bad initialiser on a stack-allocated class
escapea stack address stored into a longer-lived slot (global, heap field, outer scope), which is likely to dangle
printf-formata printf-family format string whose arguments are the wrong kind (a double for %d) or the wrong count
unguarded-actionan action used before it was tested since assignment
packed-aligna packed struct field whose access may be misaligned on the target
unknown-annotationan unrecognised function annotation, e.g. :foo
unknown-pragmaan unrecognised # directive
toolchain-fallbackthe build fell back from the in-house assembler/linker to an external tool
return-typea non-void function that can reach its closing brace without returning — its body traps at run time, so the missing return is named at compile time
par-scatter(from 0.7) a par block writes a buffer at an index that is not k*i + c in the item’s index, so two items might write the same element
par-gpu(from 0.7) compiling for a target with a GPU, a par block that cannot run there, and why: it runs on the CPU’s threads instead
range-init-counta range initialiser (u8 a[10] = 0..9;) that supplies fewer or more values than the array holds

xcc --help prints the full category list, including any checks added after this page.

-Wanalyze turns on a further set of checks that are off by default:

  • a condition that is always true or always false
  • a value that is overwritten before anything reads it
  • a local that is never used (prefix its name with _ to say that is intended)
  • code that can never run
FlagEffect
--migrate=<base>:<to>Compile as if the standard library were still <base>: methods annotated since("V") with V newer than <base> are removed from lookup, so a call whose meaning changed between the versions is an error instead of resolving to the new method. Use it when a library you depend on has renamed or repurposed a method between its versions.
VariableEffect
XCC_HOMEOverride the support-tree search. -H beats it.
XTC_HOMEThe older spelling of XCC_HOME, still read.
XTC_LDFLAGSExtra arguments appended to the native link.
Terminal window
# Native build for this machine
xcc -o app app.xc
# Cross-compile the same source three ways
xcc -A win64 -o app.exe app.xc
xcc -A m68k -o app.tos app.xc
xcc -A 6502 -o app.xex app.xc
# Link against a system library and a framework (macOS)
xcc -o app app.xc -lobjc -framework AppKit
# Build a shared library, then a client against it
xcc --emit-lib -o libgfx.so gfx.xc
xcc -L . -o app app.xc
# Inspect the generated assembly rather than linking
xcc -o app.s app.xc
# Build from two source files: compile each, then link
xcc -c -o main.o main.xc
xcc -c -o util.o util.xc
xcc -o app main.o util.o
# Debug build, one symbol defined, one warning silenced
xcc -O0 -D DEBUG -Wno-escape -o app app.xc
# The 6502 memory map, and where functions ended up
xcc -dl -m xt
xcc -A 6502 -dp -o app.xex app.xc