arm-none-eabi-gcc -c INPUT -o OUTPUT -O2 -Wall -Wextra -Wno-unused -ffreestanding \
-mthumb -mthumb-interwork -mcpu=arm7tdmi -mabi=apcs-gnu -mfloat-abi=softfp -mlong-calls
Add -g if your build pipeline allows for debug info to be propagated (lyn and EA don't).
Add any -I PATH you need (header include paths).
Hide this in your build script/Makefile.
Read the GCC docs for more.
This invokes gcc (the C compiler) and tells it to compile into an object (relocatable) file. If one omits -c
, gcc will (attempt to) produce an executable instead. INPUT
is the input .c file. OUTPUT
is the output .o file.
you can use -S instead of -c to produce an asm file instead of an object file.
This is the optimization level. -O2 is a good baseline. -O3 is probably fine as well.
I used to suggest -Os, but it caused problems as it leads gcc to emit calls to libgcc functions that we don't have by default (it does so to support packed switch jump tables that are presumably only emitted with -Os). Don't use -Os.
-O0 should never be used. If for some reason your code compiles/works with -O0 but not -O2 there's a problem with your code and you should fix that instead.
If your build pipeline allows debug information to be propagated, consider perhaps -Og as well.
You can use the optimize(...)
function attribute to override this (see GCC docs), if you need to.
The enabled warnings. -Wall
enables most basic warnings, -Wextra
enables some extras. (see GCC docs for details)
-Wno-unused
disables warnings that deal with unused varaibles, parameters, static functions, etc. I like to disable them as I usually end up having many things temporarily unused during dev and don't want actually important warnings to be lost among instances of these. This one is more of a suggestion than a recommendation.
This tells gcc to assume we are in a freestanding environment (as opposed to a hosted environment). It basically means there's no standard library. Note that despite that, gcc still emits calls to memcpy, memset and friends.
Default to compiling functions in thumb mode.
You can use the target(arm)
function attribute to override this for individual functions, if you need to.
Enables ARM/Thumb interworking, which basically makes the compiler emit bx
s at the end of functions instead of pop {pc}
. This is not necessary most of the time, but doesn't hurt to have on.
Identifies the target CPU. This both tells the compiler which instructions are available and may also enable (or disable) some CPU-specific optimizations.
-mcpu
implies both an adequate -march
option and a -mtune
option, which are thus not necessary to pass explicitly.
Use the old (GNU-flavored) APCS ABI instead of the newer AAPCS. For our purposes, both ABIs are mostly compatible (to my knowledge). However, AAPCS requires the stack pointer to be aligned to 8 byte boundaries which results in unnecessary extra pushes/pops in functions.
Small performance deficits are usually not a big deal imo, but in this case there's no real reason not to have this on.
Use software floating point. This is only necessary if your compiler was not configured to have this the default. For example: this is required if you use Compiler Explorer to test codegen using GCC targetting Linux ARM. It doesn't hurt to have.
Emit calls to outside functions using a function pointer (absolute address) rather than a bl instruction (relative offset). This is necessary if you are going to put code far away from other code (most common "free space" ranges are far enough away for this to matter).
Using long calls does lead to some overhead in function calls. On the other hand, if you do need a long call, having the compiler handle it before the linker leads to better codegen overall.
You can use the the no_long_calls
pragma or the short_call
function attribute on declarations to override this (see GCC docs), if you need to.