GCC(1)

๐Ÿ“ฆ version 15.2.0, in ubuntu questing

๐Ÿ’– gcc(1) ๐Ÿ’–

Manual page for gcc, section 1, from the GNU project โœจ

๐ŸŽ€ NAME ๐ŸŽ€

gcc - the GNU project C and C++ compiler ๐Ÿ’…

โœจ SYNOPSIS โœจ

gcc [-c|-S|-E] [-std=standard]
    [-g] [-pg] [-Olevel]
    [-Wwarn...] [-Wpedantic]
    [-Idir...] [-Ldir...]
    [-Dmacro[=defn]...] [-Umacro]
    [-foption...] [-mmachine-option...]
    [-o outfile] [@file] infile...

okay so the synopsis only shows the highlights ๐Ÿ™ˆ for everything else, scroll on down. and g++ takes pretty much the same options as gcc, so you're not learning two whole languages here, bestie ๐Ÿซถ

๐Ÿ“œ DESCRIPTION ๐Ÿ“œ

okay so when you invoke GCC, it normally does preprocessing, compilation, assembly and linking, all in one go ๐Ÿ”ฅ the "overall options" let you stop the process at an intermediate stage. for example, -c says not to run the linker, so the output is just the object files that the assembler spat out ๐Ÿงš

other options get passed along to one or more of those stages. some control the preprocessor, some control the compiler itself, and yet other options control the assembler and linker. most of those aren't documented here because you rarely need them, and honestly that's a mercy ๐Ÿ˜ญ

most of the command-line options you can use with GCC are for C programs. when an option is only useful with another language (usually C++), the explanation will say so explicitly. if the description doesn't mention a source language, you can use that option with all supported languages โœจ

the usual way to run GCC is to run the executable called gcc, or machine-gcc when you're cross-compiling, or machine-gcc-version if you want one specific version of GCC ๐Ÿ’œ and when you compile C++ programs, you should invoke GCC as g++ instead, no cap.

the gcc program takes options and file names as operands. a lot of options have multi-letter names, so multiple single-letter options can't be grouped together. -dv is very different from -d -v, so don't try to be clever ๐Ÿ™ˆ

you can mix options and other arguments. for the most part, the order doesn't matter. order does matter when you use several options of the same kind: for example, if you specify -L more than once, the directories get searched in the order you gave them. also, where you put the -l option is significant ๐Ÿ‘‘

a bunch of options have long names starting with -f or with -W---for example, -fmove-loop-invariants, -Wformat and so on ๐Ÿฆ‹ most of these have both positive and negative forms, and the negative form of -ffoo is -fno-foo. this manual only documents one of those two forms, whichever one is not the default. main character energy for the default ๐Ÿ’…

some options take one or more arguments, usually separated from the option name by either a space or an equals sign (=). unless it's documented otherwise, an argument can be either numeric or a string. numeric arguments should typically be small unsigned decimal or hexadecimal integers, and hexadecimal arguments must begin with the 0x prefix ๐ŸŒธ

arguments to options that set a size threshold of some sort can be arbitrarily large decimal or hexadecimal integers followed by a byte size suffix. that suffix designates a multiple of bytes, like kB and KiB for kilobyte and kibibyte, MB and MiB for megabyte and mebibyte, GB and GiB for gigabyte and gigibyte, and so on. these arguments get called byte-size in the rest of the text. if you want the full listing and explanation of the binary and decimal byte size prefixes, check the NIST, IEC, and other relevant national and international standards ๐Ÿค“

๐ŸŒˆ OPTIONS ๐ŸŒˆ

โœจ Option Summary โœจ

okay here's the big list of every option, grouped by type ๐Ÿซถ the explanations live in the following sections, so this is just your cheat sheet to vibe-check first.

๐Ÿ”ฅ Overall Options ๐Ÿ”ฅ

-c  -S  -E  -o file -dumpbase dumpbase  -dumpbase-ext auxdropsuf
-dumpdir dumppfx  -x language -v  -###  --help[=class[,...]]
--target-help  --version -pass-exit-codes  -pipe  -specs=file
-wrapper @file  -ffile-prefix-map=old=new  -fcanon-prefix-map
-fplugin=file  -fplugin-arg-name=arg -fdump-ada-spec[-slim]
-fada-spec-parent=unit  -fdump-go-spec=file

๐ŸŽ€ C Language Options ๐ŸŽ€

-ansi  -std=standard  -aux-info filename -fno-asm -fno-builtin
-fno-builtin-function  -fcond-mismatch -ffreestanding  -fgimple
-fgnu-tm  -fgnu89-inline  -fhosted -flax-vector-conversions
-fms-extensions -fpermitted-flt-eval-methods=standard
-fplan9-extensions  -fsigned-bitfields  -funsigned-bitfields
-fsigned-char  -funsigned-char  -fstrict-flex-arrays[=n]
-fsso-struct=endianness

๐Ÿงš C++ Language Options ๐Ÿงš

-fabi-version=n  -fno-access-control -faligned-new=n
-fno-assume-sane-operators-new-delete -fchar8_t  -fcheck-new
-fconcepts  -fconstexpr-depth=n  -fconstexpr-cache-depth=n
-fconstexpr-loop-limit=n  -fconstexpr-ops-limit=n
-fno-elide-constructors -fno-enforce-eh-specs -fno-gnu-keywords
-fno-immediate-escalation -fno-implicit-templates
-fno-implicit-inline-templates -fno-implement-inlines
-fmodule-header[=kind] -fmodule-only -fmodules
-fmodule-implicit-inline -fno-module-lazy
-fmodule-mapper=specification -fmodule-version-ignore
-fms-extensions -fnew-inheriting-ctors -fnew-ttp-matching
-fno-nonansi-builtins  -fnothrow-opt  -fno-operator-names
-fno-optional-diags -fno-pretty-templates  -frange-for-ext-temps
-fno-rtti  -fsized-deallocation -fstrong-eval-order[=kind]
-ftemplate-backtrace-limit=n -ftemplate-depth=n
-fno-threadsafe-statics  -fuse-cxa-atexit -fno-weak  -nostdinc++
-fvisibility-inlines-hidden -fvisibility-ms-compat
-fext-numeric-literals -flang-info-include-translate[=header]
-flang-info-include-translate-not -flang-info-module-cmi[=module]
-stdlib=libstdc++,libc++ -Wabi-tag  -Wcatch-value  -Wcatch-value=n
-Wno-class-conversion  -Wclass-memaccess -Wcomma-subscript
-Wconditionally-supported -Wno-conversion-null
-Wctad-maybe-unsupported -Wctor-dtor-privacy  -Wdangling-reference
-Wno-defaulted-function-deleted -Wno-delete-incomplete
-Wdelete-non-virtual-dtor  -Wno-deprecated-array-compare
-Wdeprecated-copy -Wdeprecated-copy-dtor
-Wno-deprecated-enum-enum-conversion
-Wno-deprecated-enum-float-conversion -Weffc++
-Wno-elaborated-enum-base -Wno-exceptions -Wextra-semi
-Wno-global-module -Wno-inaccessible-base
-Wno-inherited-variadic-ctor  -Wno-init-list-lifetime
-Winvalid-constexpr -Winvalid-imported-macros -Wno-invalid-offsetof
-Wno-literal-suffix -Wmismatched-new-delete -Wmismatched-tags
-Wmultiple-inheritance  -Wnamespaces  -Wnarrowing -Wnoexcept
-Wnoexcept-type  -Wnon-virtual-dtor -Wpessimizing-move
-Wno-placement-new  -Wplacement-new=n -Wrange-loop-construct
-Wredundant-move -Wredundant-tags -Wreorder  -Wregister
-Wstrict-null-sentinel  -Wno-subobject-linkage  -Wtemplates
-Wno-non-template-friend  -Wold-style-cast -Woverloaded-virtual
-Wno-pmf-conversions -Wself-move -Wsign-promo -Wsized-deallocation
-Wsuggest-final-methods -Wsuggest-final-types  -Wsuggest-override
-Wno-template-body -Wno-template-id-cdtor  -Wtemplate-names-tu-local
-Wno-terminate  -Wno-vexing-parse  -Wvirtual-inheritance
-Wno-virtual-move-assign  -Wvolatile

๐Ÿ’œ Objective-C and Objective-C++ Language Options ๐Ÿ’œ

-fconstant-string-class=class-name -fgnu-runtime  -fnext-runtime
-fno-nil-receivers -fobjc-abi-version=n -fobjc-call-cxx-cdtors
-fobjc-direct-dispatch -fobjc-exceptions -fobjc-gc -fobjc-nilcheck
-fobjc-std=objc1 -fno-local-ivars
-fivar-visibility=[public|protected|private|package]
-freplace-objc-classes -fzero-link -gen-decls -Wassign-intercept
-Wno-property-assign-default -Wno-protocol -Wobjc-root-class
-Wselector -Wstrict-selector-match -Wundeclared-selector

๐ŸŒธ OpenMP and OpenACC Options ๐ŸŒธ

-foffload=arg  -foffload-options=arg -fopenacc  -fopenacc-dim=geom
-fopenmp  -fopenmp-simd  -fopenmp-target-simd-clone[=device-type]

๐Ÿ“ Diagnostic Message Formatting Options ๐Ÿ“

-fmessage-length=n -fdiagnostics-plain-output
-fdiagnostics-show-location=[once|every-line]
-fdiagnostics-color=[auto|never|always]
-fdiagnostics-urls=[auto|never|always]
-fdiagnostics-format=[text|sarif-stderr|sarif-file|json|json-
stderr|json-file] -fdiagnostics-add-output=DIAGNOSTICS-OUTPUT-SPEC
-fdiagnostics-set-output=DIAGNOSTICS-OUTPUT-SPEC
-fno-diagnostics-json-formatting -fno-diagnostics-show-option
-fno-diagnostics-show-caret -fno-diagnostics-show-event-links
-fno-diagnostics-show-labels  -fno-diagnostics-show-line-numbers
-fno-diagnostics-show-cwe -fno-diagnostics-show-rules
-fno-diagnostics-show-highlight-colors
-fdiagnostics-minimum-margin-width=width
-fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch
-fdiagnostics-show-template-tree  -fno-elide-type
-fdiagnostics-path-format=[none|separate-events|inline-events]
-fdiagnostics-show-path-depths -fno-show-column
-fdiagnostics-column-unit=[display|byte]
-fdiagnostics-column-origin=origin
-fdiagnostics-escape-format=[unicode|bytes]
-fdiagnostics-text-art-charset=[none|ascii|unicode|emoji]

๐ŸŒธ Warning Options ๐ŸŒธ

okay so this is the big warnings lineup bestie ๐Ÿ˜ญ๐Ÿซถ every single one of these is a flag you can toss at GCC to make it speak up about sketchy code. slay.

-fsyntax-only  -fmax-errors=n  -Wpedantic -pedantic-errors
-fpermissive -w  -Wextra  -Wall  -Wabi=n -Waddress
-Wno-address-of-packed-member  -Waggregate-return -Walloc-size
-Walloc-size-larger-than=byte-size  -Walloc-zero -Walloca
-Walloca-larger-than=byte-size -Wno-aggressive-loop-optimizations
-Warith-conversion -Warray-bounds  -Warray-bounds=n  -Warray-compare
-Warray-parameter  -Warray-parameter=n -Wno-attributes
-Wattribute-alias=n -Wno-attribute-alias -Wno-attribute-warning
-Wbidi-chars=[none|unpaired|any|ucn] -Wbool-compare
-Wbool-operation -Wno-builtin-declaration-mismatch
-Wno-builtin-macro-redefined  -Wc90-c99-compat  -Wc99-c11-compat
-Wc11-c23-compat  -Wc23-c2y-compat -Wc++-compat  -Wc++11-compat
-Wc++14-compat  -Wc++17-compat -Wc++20-compat -Wno-c++11-extensions
-Wno-c++14-extensions -Wno-c++17-extensions -Wno-c++20-extensions
-Wno-c++23-extensions -Wcalloc-transposed-args -Wcast-align
-Wcast-align=strict  -Wcast-function-type  -Wcast-qual
-Wchar-subscripts -Wclobbered  -Wcomment
-Wcompare-distinct-pointer-types -Wno-complain-wrong-lang
-Wconversion  -Wno-coverage-mismatch  -Wno-cpp -Wdangling-else
-Wdangling-pointer  -Wdangling-pointer=n -Wdate-time -Wno-deprecated
-Wno-deprecated-declarations  -Wno-designated-init
-Wdisabled-optimization -Wno-discarded-array-qualifiers
-Wno-discarded-qualifiers -Wno-div-by-zero  -Wdouble-promotion
-Wduplicated-branches  -Wduplicated-cond -Wempty-body
-Wno-endif-labels  -Wenum-compare  -Wenum-conversion
-Wenum-int-mismatch -Werror  -Werror=*  -Wexpansion-to-defined
-Wfatal-errors -Wflex-array-member-not-at-end -Wfloat-conversion
-Wfloat-equal  -Wformat  -Wformat=2 -Wno-format-contains-nul
-Wno-format-extra-args -Wformat-nonliteral  -Wformat-overflow=n
-Wformat-security  -Wformat-signedness  -Wformat-truncation=n
-Wformat-y2k  -Wframe-address -Wframe-larger-than=byte-size
-Wno-free-nonheap-object -Wheader-guard  -Wno-if-not-aligned
-Wno-ignored-attributes -Wignored-qualifiers
-Wno-incompatible-pointer-types  -Whardened -Wimplicit
-Wimplicit-fallthrough  -Wimplicit-fallthrough=n
-Wno-implicit-function-declaration  -Wno-implicit-int
-Winfinite-recursion -Winit-self  -Winline  -Wno-int-conversion
-Wint-in-bool-context -Wno-int-to-pointer-cast
-Wno-invalid-memory-model -Winvalid-pch  -Winvalid-utf8
-Wno-unicode  -Wjump-misses-init -Wlarger-than=byte-size
-Wleading-whitespace=kind -Wlogical-not-parentheses  -Wlogical-op
-Wlong-long  -Wno-lto-type-mismatch -Wmain  -Wmaybe-uninitialized
-Wmemset-elt-size  -Wmemset-transposed-args -Wmisleading-indentation
-Wmissing-attributes  -Wmissing-braces -Wmissing-field-initializers
-Wmissing-format-attribute -Wmissing-include-dirs
-Wmissing-noreturn  -Wmusttail-local-addr
-Wmaybe-musttail-local-addr  -Wno-missing-profile -Wno-multichar
-Wmultistatement-macros  -Wnonnull  -Wnonnull-compare
-Wnormalized=[none|id|nfc|nfkc] -Wnull-dereference  -Wno-odr
-Wopenacc-parallelism -Wopenmp -Wopenmp-simd -Wno-overflow
-Woverlength-strings  -Wno-override-init-side-effects -Wpacked
-Wno-packed-bitfield-compat  -Wpacked-not-aligned  -Wpadded
-Wparentheses  -Wno-pedantic-ms-format -Wpointer-arith
-Wno-pointer-compare  -Wno-pointer-to-int-cast -Wno-pragmas
-Wno-pragma-once-outside-header  -Wno-prio-ctor-dtor -Wno-psabi
-Wredundant-decls  -Wrestrict -Wno-return-local-addr  -Wreturn-type
-Wno-scalar-storage-order  -Wsequence-point -Wshadow
-Wshadow=global  -Wshadow=local  -Wshadow=compatible-local
-Wno-shadow-ivar -Wno-shift-count-negative
-Wno-shift-count-overflow  -Wshift-negative-value
-Wno-shift-overflow  -Wshift-overflow=n -Wsign-compare
-Wsign-conversion -Wno-sizeof-array-argument -Wsizeof-array-div
-Wsizeof-pointer-div  -Wsizeof-pointer-memaccess -Wstack-protector
-Wstack-usage=byte-size  -Wstrict-aliasing -Wstrict-aliasing=n
-Wstrict-overflow  -Wstrict-overflow=n -Wstring-compare
-Wno-stringop-overflow -Wno-stringop-overread
-Wno-stringop-truncation  -Wstrict-flex-arrays
-Wsuggest-attribute=attribute-name -Wswitch  -Wno-switch-bool
-Wswitch-default  -Wswitch-enum -Wno-switch-outside-range
-Wno-switch-unreachable  -Wsync-nand -Wsystem-headers
-Wtautological-compare  -Wtrailing-whitespace
-Wtrailing-whitespace=kind  -Wtrampolines  -Wtrigraphs
-Wtrivial-auto-var-init  -Wno-tsan  -Wtype-limits  -Wundef
-Wuninitialized  -Wunknown-pragmas -Wunsuffixed-float-constants
-Wunterminated-string-initialization -Wunused
-Wunused-but-set-parameter  -Wunused-but-set-variable
-Wunused-const-variable  -Wunused-const-variable=n -Wunused-function
-Wunused-label  -Wunused-local-typedefs -Wunused-macros
-Wunused-parameter  -Wno-unused-result -Wunused-value
-Wunused-variable -Wuse-after-free  -Wuse-after-free=n
-Wuseless-cast -Wno-varargs  -Wvariadic-macros
-Wvector-operation-performance -Wvla  -Wvla-larger-than=byte-size
-Wno-vla-larger-than -Wvolatile-register-var  -Wwrite-strings
-Wno-xor-used-as-pow -Wzero-as-null-pointer-constant
-Wzero-length-bounds

๐Ÿฆ‹ Static Analyzer Options ๐Ÿฆ‹

the analyzer crew, bestie ๐Ÿ”โœจ these are the flags for the static analyzer. it's giving detective energy, and every checker has its own little -Wno-analyzer-... switch if you wanna mute one. no cap, it's a lot ๐Ÿ’€

-fanalyzer -fanalyzer-call-summaries -fanalyzer-checker=name
-fno-analyzer-feasibility -fanalyzer-fine-grained
-fanalyzer-show-events-in-system-headers -fno-analyzer-state-merge
-fno-analyzer-state-purge -fno-analyzer-suppress-followups
-fanalyzer-transitivity -fno-analyzer-undo-inlining
-fanalyzer-verbose-edges -fanalyzer-verbose-state-changes
-fanalyzer-verbosity=level -fdump-analyzer -fdump-analyzer-callgraph
-fdump-analyzer-exploded-graph -fdump-analyzer-exploded-nodes
-fdump-analyzer-exploded-nodes-2 -fdump-analyzer-exploded-nodes-3
-fdump-analyzer-exploded-paths -fdump-analyzer-feasibility
-fdump-analyzer-infinite-loop -fdump-analyzer-json
-fdump-analyzer-state-purge -fdump-analyzer-stderr
-fdump-analyzer-supergraph -fdump-analyzer-untracked
-Wno-analyzer-double-fclose -Wno-analyzer-double-free
-Wno-analyzer-exposure-through-output-file
-Wno-analyzer-exposure-through-uninit-copy
-Wno-analyzer-fd-access-mode-mismatch -Wno-analyzer-fd-double-close
-Wno-analyzer-fd-leak -Wno-analyzer-fd-phase-mismatch
-Wno-analyzer-fd-type-mismatch -Wno-analyzer-fd-use-after-close
-Wno-analyzer-fd-use-without-check -Wno-analyzer-file-leak
-Wno-analyzer-free-of-non-heap -Wno-analyzer-imprecise-fp-arithmetic
-Wno-analyzer-infinite-loop -Wno-analyzer-infinite-recursion
-Wno-analyzer-jump-through-null -Wno-analyzer-malloc-leak
-Wno-analyzer-mismatching-deallocation -Wno-analyzer-null-argument
-Wno-analyzer-null-dereference -Wno-analyzer-out-of-bounds
-Wno-analyzer-overlapping-buffers
-Wno-analyzer-possible-null-argument
-Wno-analyzer-possible-null-dereference
-Wno-analyzer-putenv-of-auto-var -Wno-analyzer-shift-count-negative
-Wno-analyzer-shift-count-overflow -Wno-analyzer-stale-setjmp-buffer
-Wno-analyzer-tainted-allocation-size
-Wno-analyzer-tainted-assertion -Wno-analyzer-tainted-array-index
-Wno-analyzer-tainted-divisor -Wno-analyzer-tainted-offset
-Wno-analyzer-tainted-size -Wanalyzer-symbol-too-complex
-Wanalyzer-too-complex -Wno-analyzer-undefined-behavior-ptrdiff
-Wno-analyzer-undefined-behavior-strtok
-Wno-analyzer-unsafe-call-within-signal-handler
-Wno-analyzer-use-after-free
-Wno-analyzer-use-of-pointer-in-stale-stack-frame
-Wno-analyzer-use-of-uninitialized-value
-Wno-analyzer-va-arg-type-mismatch -Wno-analyzer-va-list-exhausted
-Wno-analyzer-va-list-leak -Wno-analyzer-va-list-use-after-va-end
-Wno-analyzer-write-to-const -Wno-analyzer-write-to-string-literal

๐Ÿ’œ C and Objective-C-only Warning Options ๐Ÿ’œ

these ones are only for C and Objective-C, so if you're in C++ land they just aren't it ๐Ÿ™ˆ lowkey a super specific crew, but iconic for the people who need them ๐Ÿ‘‘

-Wbad-function-cast -Wdeprecated-non-prototype -Wfree-labels
-Wmissing-declarations -Wmissing-parameter-name
-Wmissing-parameter-type -Wdeclaration-missing-parameter-type
-Wmissing-prototypes -Wmissing-variable-declarations
-Wnested-externs -Wold-style-declaration -Wold-style-definition
-Wstrict-prototypes -Wtraditional -Wtraditional-conversion
-Wdeclaration-after-statement -Wpointer-sign

๐Ÿ” Debugging Options ๐Ÿ”

okay debugging era, bestie ๐Ÿชฒ๐Ÿ’– these flags are all about getting GCC to tuck extra debug info into your build so your debugger can actually help you. the -g family is the main squad, and there are a ton of little knobs for DWARF, CodeView, BTF, CTF and friends. let's gooo โœจ

-g  -glevel  -gdwarf  -gdwarf-version -gbtf -gctf  -gctflevel
-gprune-btf -gno-prune-btf -ggdb  -grecord-gcc-switches
-gno-record-gcc-switches -gstrict-dwarf  -gno-strict-dwarf
-gas-loc-support  -gno-as-loc-support -gas-locview-support
-gno-as-locview-support -gcodeview -gcolumn-info  -gno-column-info
-gdwarf32  -gdwarf64 -gstatement-frontiers  -gno-statement-frontiers
-gvariable-location-views  -gno-variable-location-views
-ginternal-reset-location-views  -gno-internal-reset-location-views
-ginline-points  -gno-inline-points -gvms -gz[=type] -gsplit-dwarf
-gdescribe-dies  -gno-describe-dies -fdebug-prefix-map=old=new
-fdebug-types-section -fno-eliminate-unused-debug-types
-femit-struct-debug-baseonly  -femit-struct-debug-reduced
-femit-struct-debug-detailed[=spec-list]
-fno-eliminate-unused-debug-symbols  -femit-class-debug-always
-fno-merge-debug-strings  -fno-dwarf2-cfi-asm -fvar-tracking
-fvar-tracking-assignments

โœจ Optimization Options โœจ

okay bestie, this is the big one ๐Ÿ’… so many flags that make your code go zoom ๐Ÿ”ฅ here's the whole lineup, no cap:

  • -faggressive-loop-optimizations
  • -falign-functions[=n[:m:[n2[:m2]]]]
  • -falign-jumps[=n[:m:[n2[:m2]]]]
  • -falign-labels[=n[:m:[n2[:m2]]]]
  • -falign-loops[=n[:m:[n2[:m2]]]]
  • -fmin-function-alignment=[n]
  • -fno-allocation-dce
  • -fallow-store-data-races
  • -fassociative-math
  • -fauto-profile
  • -fauto-profile[=path]
  • -fauto-inc-dec
  • -fbranch-probabilities
  • -fcaller-saves
  • -fcombine-stack-adjustments
  • -fconserve-stack
  • -ffold-mem-offsets
  • -fcompare-elim
  • -fcprop-registers
  • -fcrossjumping
  • -fcse-follow-jumps
  • -fcse-skip-blocks
  • -fcx-fortran-rules
  • -fcx-limited-range
  • -fdata-sections
  • -fdce
  • -fdelayed-branch
  • -fdelete-null-pointer-checks
  • -fdevirtualize
  • -fdevirtualize-speculatively
  • -fdevirtualize-at-ltrans
  • -fdse
  • -fearly-inlining
  • -fipa-sra
  • -fexpensive-optimizations
  • -ffat-lto-objects
  • -ffast-math
  • -ffinite-math-only
  • -ffloat-store
  • -fexcess-precision=style
  • -ffinite-loops
  • -fforward-propagate
  • -ffp-contract=style
  • -ffunction-sections
  • -fgcse
  • -fgcse-after-reload
  • -fgcse-las
  • -fgcse-lm
  • -fgraphite-identity
  • -fgcse-sm
  • -fhoist-adjacent-loads
  • -fif-conversion
  • -fif-conversion2
  • -findirect-inlining
  • -finline-stringops[=fn]
  • -finline-functions
  • -finline-functions-called-once
  • -finline-limit=n
  • -finline-small-functions
  • -fipa-modref
  • -fipa-cp
  • -fipa-cp-clone
  • -fipa-bit-cp
  • -fipa-vrp
  • -fipa-pta
  • -fipa-profile
  • -fipa-pure-const
  • -fipa-reference
  • -fipa-reference-addressable
  • -fipa-reorder-for-locality
  • -fipa-stack-alignment
  • -fipa-icf
  • -fira-algorithm=algorithm
  • -flate-combine-instructions
  • -flifetime-dse
  • -flive-patching=level
  • -fira-region=region
  • -fira-hoist-pressure
  • -fira-loop-pressure
  • -fno-ira-share-save-slots
  • -fno-ira-share-spill-slots
  • -fisolate-erroneous-paths-dereference
  • -fisolate-erroneous-paths-attribute
  • -fivopts
  • -fkeep-inline-functions
  • -fkeep-static-functions
  • -fkeep-static-consts
  • -flimit-function-alignment
  • -flive-range-shrinkage
  • -floop-block
  • -floop-interchange
  • -floop-strip-mine
  • -floop-unroll-and-jam
  • -floop-nest-optimize
  • -floop-parallelize-all
  • -flra-remat
  • -flto
  • -flto-compression-level
  • -flto-partition=alg
  • -flto-incremental=path
  • -flto-incremental-cache-size=n
  • -fmalloc-dce
  • -fmerge-all-constants
  • -fmerge-constants
  • -fmodulo-sched
  • -fmodulo-sched-allow-regmoves
  • -fmove-loop-invariants
  • -fmove-loop-stores
  • -fno-branch-count-reg
  • -fno-defer-pop
  • -fno-fp-int-builtin-inexact
  • -fno-function-cse
  • -fno-guess-branch-probability
  • -fno-inline
  • -fno-math-errno
  • -fno-peephole
  • -fno-peephole2
  • -fno-printf-return-value
  • -fno-sched-interblock
  • -fno-sched-spec
  • -fno-signed-zeros
  • -fno-toplevel-reorder
  • -fno-trapping-math
  • -fno-zero-initialized-in-bss
  • -fomit-frame-pointer
  • -foptimize-crc
  • -foptimize-sibling-calls
  • -fpartial-inlining
  • -fpeel-loops
  • -fpredictive-commoning
  • -fprefetch-loop-arrays
  • -fprofile-correction
  • -fprofile-use
  • -fprofile-use=path
  • -fprofile-partial-training
  • -fprofile-values
  • -fprofile-reorder-functions
  • -freciprocal-math
  • -free
  • -frename-registers
  • -freorder-blocks
  • -freorder-blocks-algorithm=algorithm
  • -freorder-blocks-and-partition
  • -freorder-functions
  • -frerun-cse-after-loop
  • -freschedule-modulo-scheduled-loops
  • -frounding-math
  • -fsave-optimization-record
  • -fsched2-use-superblocks
  • -fsched-pressure
  • -fsched-spec-load
  • -fsched-spec-load-dangerous
  • -fsched-stalled-insns-dep[=n]
  • -fsched-stalled-insns[=n]
  • -fsched-group-heuristic
  • -fsched-critical-path-heuristic
  • -fsched-spec-insn-heuristic
  • -fsched-rank-heuristic
  • -fsched-last-insn-heuristic
  • -fsched-dep-count-heuristic
  • -fschedule-fusion
  • -fschedule-insns
  • -fschedule-insns2
  • -fsection-anchors
  • -fselective-scheduling
  • -fselective-scheduling2
  • -fsel-sched-pipelining
  • -fsel-sched-pipelining-outer-loops
  • -fsemantic-interposition
  • -fshrink-wrap
  • -fshrink-wrap-separate
  • -fsignaling-nans
  • -fsingle-precision-constant
  • -fsplit-ivs-in-unroller
  • -fsplit-loops
  • -fsplit-paths
  • -fsplit-wide-types
  • -fsplit-wide-types-early
  • -fssa-backprop
  • -fssa-phiopt
  • -fstdarg-opt
  • -fstore-merging
  • -fstrict-aliasing
  • -fipa-strict-aliasing
  • -fthread-jumps
  • -ftracer
  • -ftree-bit-ccp
  • -ftree-builtin-call-dce
  • -ftree-ccp
  • -ftree-ch
  • -ftree-coalesce-vars
  • -ftree-copy-prop
  • -ftree-cselim
  • -ftree-dce
  • -ftree-dominator-opts
  • -ftree-dse
  • -ftree-forwprop
  • -ftree-fre
  • -fcode-hoisting
  • -ftree-loop-if-convert
  • -ftree-loop-im
  • -ftree-phiprop
  • -ftree-loop-distribution
  • -ftree-loop-distribute-patterns
  • -ftree-loop-ivcanon
  • -ftree-loop-linear
  • -ftree-loop-optimize
  • -ftree-loop-vectorize
  • -ftree-parallelize-loops=n
  • -ftree-pre
  • -ftree-partial-pre
  • -ftree-pta
  • -ftree-reassoc
  • -ftree-scev-cprop
  • -ftree-sink
  • -ftree-slsr
  • -ftree-sra
  • -ftree-switch-conversion
  • -ftree-tail-merge
  • -ftree-ter
  • -ftree-vectorize
  • -ftree-vrp
  • -ftrivial-auto-var-init
  • -funconstrained-commons
  • -funit-at-a-time
  • -funroll-all-loops
  • -funroll-loops
  • -funsafe-math-optimizations
  • -funswitch-loops
  • -fipa-ra
  • -fvariable-expansion-in-unroller
  • -fvect-cost-model
  • -fvpt
  • -fweb
  • -fwhole-program
  • -fwpa
  • -fuse-linker-plugin
  • -fzero-call-used-regs
  • --param name=value
  • -O
  • -O0
  • -O1
  • -O2
  • -O3
  • -Os
  • -Ofast
  • -Og
  • -Oz

โœจ Program Instrumentation Options โœจ

ooh, this bundle is all about watching your program's every move ๐Ÿ‘€๐ŸŽ€

  • -p
  • -pg
  • -fprofile-arcs
  • --coverage
  • -ftest-coverage
  • -fcondition-coverage
  • -fpath-coverage
  • -fprofile-abs-path
  • -fprofile-dir=path
  • -fprofile-generate
  • -fprofile-generate=path
  • -fprofile-info-section
  • -fprofile-info-section=name
  • -fprofile-note=path
  • -fprofile-prefix-path=path
  • -fprofile-update=method
  • -fprofile-filter-files=regex
  • -fprofile-exclude-files=regex
  • -fprofile-reproducible=[multithreaded|parallel-runs|serial]
  • -fsanitize=style
  • -fsanitize-recover
  • -fsanitize-recover=style
  • -fsanitize-trap
  • -fsanitize-trap=style
  • -fasan-shadow-offset=number
  • -fsanitize-sections=s1,s2,...
  • -fsanitize-undefined-trap-on-error
  • -fbounds-check
  • -fcf-protection
  • -fcf-protection=[full|branch|return|none|check]
  • -fharden-compares
  • -fharden-conditional-branches
  • -fhardened
  • -fharden-control-flow-redundancy
  • -fhardcfr-skip-leaf
  • -fhardcfr-check-exceptions
  • -fhardcfr-check-returning-calls
  • -fhardcfr-check-noreturn-calls=[always|no-xthrow|nothrow|never]
  • -fstack-protector
  • -fstack-protector-all
  • -fstack-protector-strong
  • -fstack-protector-explicit
  • -fstack-check
  • -fstack-limit-register=reg
  • -fstack-limit-symbol=sym
  • -fno-stack-limit
  • -fsplit-stack
  • -fstrub=disable
  • -fstrub=strict
  • -fstrub=relaxed
  • -fstrub=all
  • -fstrub=at-calls
  • -fstrub=internal
  • -fvtable-verify=[std|preinit|none]
  • -fvtv-counts
  • -fvtv-debug
  • -finstrument-functions
  • -finstrument-functions-once
  • -finstrument-functions-exclude-function-list=sym,sym,...
  • -finstrument-functions-exclude-file-list=file,file,...
  • -fprofile-prefix-map=old=new
  • -fpatchable-function-entry=N[,M]

โœจ Preprocessor Options โœจ

the preprocessor is basically the prep team before the main event ๐Ÿงš

  • -Aquestion=answer
  • -A-question[=answer]
  • -C
  • -CC
  • -Dmacro[=defn]
  • -dD
  • -dI
  • -dM
  • -dN
  • -dU
  • -fdebug-cpp
  • -fdirectives-only
  • -fdollars-in-identifiers
  • -fexec-charset=charset
  • -fextended-identifiers
  • -finput-charset=charset
  • -fmacro-prefix-map=old=new
  • -fmax-include-depth=depth
  • -fno-canonical-system-headers
  • -fpch-deps
  • -fpch-preprocess
  • -fpreprocessed
  • -ftabstop=width
  • -ftrack-macro-expansion
  • -fwide-exec-charset=charset
  • -fworking-directory
  • -H
  • -imacros file
  • -include file
  • -M
  • -MD
  • -MF
  • -MG
  • -MM
  • -MMD
  • -MP
  • -MQ
  • -MT
  • -Mno-modules
  • -no-integrated-cpp
  • -P
  • -pthread
  • -remap
  • -traditional
  • -traditional-cpp
  • -trigraphs
  • -Umacro
  • -undef
  • -Wp,option
  • -Xpreprocessor option

โœจ Assembler Options โœจ

just two, but they slay ๐Ÿซถ

  • -Wa,option
  • -Xassembler option

โœจ Linker Options โœจ

the linker is where everything comes together, bestie ๐Ÿ”—๐Ÿ‘‘

  • object-file-name
  • -fuse-ld=linker
  • -llibrary
  • -nostartfiles
  • -nodefaultlibs
  • -nolibc
  • -nostdlib
  • -nostdlib++
  • -e entry
  • --entry=entry
  • -pie
  • -pthread
  • -r
  • -rdynamic
  • -s
  • -static
  • -static-pie
  • -static-libgcc
  • -static-libstdc++
  • -static-libasan
  • -static-libtsan
  • -static-liblsan
  • -static-libubsan
  • -shared
  • -shared-libgcc
  • -symbolic
  • -T script
  • -Wl,option
  • -Xlinker option
  • -u symbol
  • -z keyword

โœจ Directory Options โœจ

where the compiler goes looking for stuff ๐Ÿ—บ๏ธ๐Ÿฆ‹

  • -Bprefix
  • -Idir
  • -I-
  • -idirafter dir
  • -imacros file
  • -imultilib dir
  • -iplugindir=dir
  • -iprefix file
  • -iquote dir
  • -isysroot dir
  • -isystem dir
  • -iwithprefix dir
  • -iwithprefixbefore dir
  • --embed-dir=dir
  • -Ldir
  • -no-canonical-prefixes
  • --no-sysroot-suffix
  • -nostdinc
  • -nostdinc++
  • --sysroot=dir

โœจ Code Generation Options โœจ

this is where the actual machine code gets shaped ๐Ÿ”ฅ๐Ÿ’–

  • -fcall-saved-reg
  • -fcall-used-reg
  • -ffixed-reg
  • -fexceptions
  • -fnon-call-exceptions
  • -fdelete-dead-exceptions
  • -funwind-tables
  • -fasynchronous-unwind-tables
  • -fno-gnu-unique
  • -finhibit-size-directive
  • -fcommon
  • -fno-ident
  • -fpcc-struct-return
  • -fpic
  • -fPIC
  • -fpie
  • -fPIE
  • -fno-plt
  • -fno-jump-tables
  • -fno-bit-tests
  • -frecord-gcc-switches
  • -freg-struct-return
  • -fshort-enums
  • -fshort-wchar
  • -fverbose-asm
  • -fpack-struct[=n]
  • -fleading-underscore
  • -ftls-model=model
  • -fstack-reuse=reuse_level
  • -ftrampolines
  • -ftrampoline-impl=[stack|heap]
  • -ftrapv
  • -fwrapv
  • -fvisibility=[default|internal|hidden|protected]
  • -fstrict-volatile-bitfields
  • -fsync-libcalls
  • -fzero-init-padding-bits=value

โœจ Developer Options โœจ

for the power users ๐Ÿค“๐Ÿ’… these are the debugging and dumping flags, so handle with care (and snacks) ๐Ÿ“

  • -dletters
  • -dumpspecs
  • -dumpmachine
  • -dumpversion
  • -dumpfullversion
  • -fcallgraph-info[=su,da]
  • -fchecking
  • -fchecking=n
  • -fdbg-cnt-list
  • -fdbg-cnt=counter-value-list
  • -fdisable-ipa-pass_name
  • -fdisable-rtl-pass_name
  • -fdisable-rtl-pass-name=range-list
  • -fdisable-tree-pass_name
  • -fdisable-tree-pass-name=range-list
  • -fdump-debug
  • -fdump-earlydebug
  • -fdump-noaddr
  • -fdump-unnumbered
  • -fdump-unnumbered-links
  • -fdump-final-insns[=file]
  • -fdump-ipa-all
  • -fdump-ipa-cgraph
  • -fdump-ipa-inline
  • -fdump-lang-all
  • -fdump-lang-switch
  • -fdump-lang-switch-options
  • -fdump-lang-switch-options=filename
  • -fdump-passes
  • -fdump-rtl-pass
  • -fdump-rtl-pass=filename
  • -fdump-statistics
  • -fdump-tree-all
  • -fdump-tree-switch
  • -fdump-tree-switch-options
  • -fdump-tree-switch-options=filename
  • -fcompare-debug[=opts]
  • -fcompare-debug-second
  • -fenable-kind-pass
  • -fenable-kind-pass=range-list
  • -fira-verbose=n
  • -flto-report
  • -flto-report-wpa
  • -fmem-report-wpa
  • -fmem-report
  • -fpre-ipa-mem-report
  • -fpost-ipa-mem-report
  • -fopt-info
  • -fopt-info-options[=file]
  • -fmultiflags
  • -fprofile-report
  • -frandom-seed=string
  • -fsched-verbose=n
  • -fsel-sched-verbose
  • -fsel-sched-dump-cfg
  • -fsel-sched-pipelining-verbose
  • -fstats
  • -fstack-usage
  • -ftime-report
  • -ftime-report-details
  • -fvar-tracking-assignments-toggle
  • -gtoggle
  • -print-file-name=library
  • -print-libgcc-file-name
  • -print-multi-directory
  • -print-multi-lib
  • -print-multi-os-directory
  • -print-prog-name=program
  • -print-search-dirs
  • -Q
  • -print-sysroot
  • -print-sysroot-headers-suffix
  • -save-temps
  • -save-temps=cwd
  • -save-temps=obj
  • -time[=file]

๐Ÿฆ‹ Machine-Dependent Options ๐Ÿฆ‹

okay so this part is the machine-specific squad ๐Ÿ’… every architecture gets its own little group of flags, and they only matter for their own chip family ๐Ÿงš let's go bestie ๐Ÿ’–

โœจ AArch64 Options (AArch64 Options) โœจ

  • -mabi=name โ€” slay flag ๐Ÿ’…
  • -mbig-endian โ€” big-endian drip ๐ŸŒธ
  • -mlittle-endian โ€” little-endian drip ๐ŸŒธ
  • -mgeneral-regs-only โ€” general registers only ๐ŸŽ€
  • -mcmodel=tiny โ€” tiny code model ๐Ÿงš
  • -mcmodel=small โ€” small code model ๐Ÿงš
  • -mcmodel=large โ€” large code model ๐Ÿงš
  • -mstrict-align โ€” strict alignment ๐Ÿ“
  • -mno-strict-align โ€” strict alignment, turned off ๐Ÿ™ˆ
  • -momit-leaf-frame-pointer โ€” leaf frame pointer goes bye ๐Ÿ‘‹
  • -mtls-dialect=desc โ€” desc TLS dialect ๐Ÿฆ‹
  • -mtls-dialect=traditional โ€” traditional TLS dialect ๐Ÿฆ‹
  • -mtls-size=size โ€” TLS size ๐Ÿ“
  • -mfix-cortex-a53-835769 โ€” Cortex-A53 erratum fix ๐Ÿ”ง
  • -mfix-cortex-a53-843419 โ€” another Cortex-A53 fix ๐Ÿ”ง
  • -mlow-precision-recip-sqrt โ€” low-precision reciprocal sqrt ๐Ÿงฎ
  • -mlow-precision-sqrt โ€” low-precision sqrt ๐Ÿงฎ
  • -mlow-precision-div โ€” low-precision division ๐Ÿงฎ
  • -mpc-relative-literal-loads โ€” PC-relative literal loads ๐ŸŒˆ
  • -msign-return-address=scope โ€” return address signing โœ๏ธ
  • -mbranch-protection=none|standard|pac-ret[+leaf +b-key]|bti|gcs โ€” branch protection, pick your vibe ๐Ÿ›ก๏ธ
  • -mharden-sls=opts โ€” straight-line speculation hardening ๐Ÿ›ก๏ธ
  • -march=name โ€” architecture of choice ๐Ÿ›๏ธ
  • -mcpu=name โ€” CPU of choice ๐Ÿง 
  • -mtune=name โ€” tuning target ๐ŸŽš๏ธ
  • -moverride=string โ€” override string ๐Ÿ“
  • -mverbose-cost-dump โ€” verbose cost dump ๐Ÿ“œ
  • -mstack-protector-guard=guard โ€” stack protector guard ๐Ÿ›ก๏ธ
  • -mstack-protector-guard-reg=sysreg โ€” guard system register ๐Ÿ›ก๏ธ
  • -mstack-protector-guard-offset=offset โ€” guard offset ๐Ÿ›ก๏ธ
  • -mtrack-speculation โ€” speculation tracking ๐Ÿ‘€
  • -moutline-atomics โ€” outline atomics ๐Ÿ”ฅ
  • -mearly-ldp-fusion โ€” early LDP fusion ๐Ÿ”—
  • -mlate-ldp-fusion โ€” late LDP fusion ๐Ÿ”—
  • -Wexperimental-fmv-target โ€” experimental FMV target warning โš ๏ธ

๐Ÿ Adapteva Epiphany Options (Adapteva Epiphany Options) ๐Ÿ

  • -mhalf-reg-file โ€” half register file ๐ŸŒ™
  • -mprefer-short-insn-regs โ€” prefer short instruction registers ๐ŸŒ™
  • -mbranch-cost=num โ€” branch cost ๐Ÿ’ธ
  • -mcmove โ€” conditional move ๐ŸŽ€
  • -mnops=num โ€” number of nops ๐Ÿซง
  • -msoft-cmpsf โ€” soft compare for floats ๐Ÿงƒ
  • -msplit-lohi โ€” split low and high ๐Ÿ“
  • -mpost-inc โ€” post-increment ๐Ÿ“ˆ
  • -mpost-modify โ€” post-modify ๐Ÿ“ˆ
  • -mstack-offset=num โ€” stack offset ๐Ÿ“š
  • -mround-nearest โ€” round to nearest ๐ŸŽฏ
  • -mlong-calls โ€” long calls ๐Ÿ“ž
  • -mshort-calls โ€” short calls ๐Ÿ“ฑ
  • -msmall16 โ€” small16 ๐Ÿงธ
  • -mfp-mode=mode โ€” FP mode ๐ŸŒŠ
  • -mvect-double โ€” vector double ๐Ÿ‘ฏ
  • -max-vect-align=num โ€” max vector alignment ๐Ÿ“
  • -msplit-vecmove-early โ€” split vector moves early โฉ
  • -m1reg-reg โ€” one reg-reg ๐Ÿซถ

๐ŸŒ€ AMD GCN Options (AMD GCN Options) ๐ŸŒ€

  • -march=gpu โ€” GPU architecture ๐ŸŽฎ
  • -mtune=gpu โ€” GPU tuning ๐ŸŽฎ
  • -mstack-size=bytes โ€” stack size in bytes ๐Ÿ“

๐ŸŒบ ARC Options (ARC Options) ๐ŸŒบ

  • -mbarrel-shifter โ€” barrel shifter ๐ŸŒ€
  • -mjli-always โ€” JLI always ๐ŸŒ€
  • -mcpu=cpu โ€” CPU pick ๐Ÿง 
  • -mA6 โ€” A6 ๐Ÿงฉ
  • -mARC600 โ€” ARC600 ๐Ÿงฉ
  • -mA7 โ€” A7 ๐Ÿงฉ
  • -mARC700 โ€” ARC700 ๐Ÿงฉ
  • -mdpfp โ€” double-precision FP ๐Ÿ”ข
  • -mdpfp-compact โ€” compact double-precision FP ๐Ÿ”ข
  • -mdpfp-fast โ€” fast double-precision FP ๐Ÿ”ข
  • -mno-dpfp-lrsr โ€” no DPFP LR/SR โŒ
  • -mea โ€” extension access ๐Ÿ”“
  • -mno-mpy โ€” no multiply โŒ
  • -mmul32x16 โ€” 32x16 multiply โœ–๏ธ
  • -mmul64 โ€” 64-bit multiply โœ–๏ธ
  • -matomic โ€” atomics โš›๏ธ
  • -mnorm โ€” norm instruction ๐Ÿงญ
  • -mspfp โ€” single-precision FP ๐Ÿ”ข
  • -mspfp-compact โ€” compact single-precision FP ๐Ÿ”ข
  • -mspfp-fast โ€” fast single-precision FP ๐Ÿ”ข
  • -msimd โ€” SIMD ๐ŸŽถ
  • -msoft-float โ€” soft float ๐Ÿงˆ
  • -mswap โ€” swap ๐Ÿ”„
  • -mcrc โ€” CRC ๐Ÿงฎ
  • -mdsp-packa โ€” DSP pack-A ๐Ÿ“ฆ
  • -mdvbf โ€” DVBF ๐Ÿงฉ
  • -mlock โ€” lock ๐Ÿ”’
  • -mmac-d16 โ€” MAC D16 ๐Ÿงฎ
  • -mmac-24 โ€” MAC 24 ๐Ÿงฎ
  • -mrtsc โ€” RTSC โฑ๏ธ
  • -mswape โ€” swape ๐Ÿ”„
  • -mtelephony โ€” telephony ๐Ÿ“ž
  • -mxy โ€” XY memory ๐Ÿง 
  • -misize โ€” instruction size ๐Ÿ“
  • -mannotate-align โ€” annotate alignment ๐Ÿ“
  • -marclinux โ€” ARC Linux ๐Ÿง
  • -marclinux_prof โ€” ARC Linux with profiling ๐Ÿง
  • -mlong-calls โ€” long calls ๐Ÿ“ž
  • -mmedium-calls โ€” medium calls ๐Ÿ“ž
  • -msdata โ€” small data ๐Ÿ“ฆ
  • -mirq-ctrl-saved โ€” IRQ control saved ๐Ÿ›‘
  • -mrgf-banked-regs โ€” banked register file ๐Ÿฆ
  • -mlpc-width=width โ€” LPC width ๐Ÿ“
  • -G num โ€” small-data threshold ๐Ÿ“ฆ
  • -mvolatile-cache โ€” volatile cache ๐Ÿ—„๏ธ
  • -mtp-regno=regno โ€” TP register number ๐Ÿ”ข
  • -malign-call โ€” align calls ๐Ÿ“
  • -mauto-modify-reg โ€” auto-modify register ๐Ÿ”ง
  • -mbbit-peephole โ€” BBIT peephole ๐Ÿ”
  • -mno-brcc โ€” no BRcc โŒ
  • -mcase-vector-pcrel โ€” PC-relative case vectors ๐Ÿงญ
  • -mcompact-casesi โ€” compact casesi ๐Ÿ“ฆ
  • -mno-cond-exec โ€” no conditional execution โŒ
  • -mearly-cbranchsi โ€” early cbranchsi โฉ
  • -mexpand-adddi โ€” expand adddi โž•
  • -mindexed-loads โ€” indexed loads ๐Ÿ“š
  • -mlra โ€” LRA ๐Ÿง 
  • -mlra-priority-none โ€” LRA priority none ๐Ÿง 
  • -mlra-priority-compact โ€” LRA priority compact ๐Ÿง 
  • -mlra-priority-noncompact โ€” LRA priority noncompact ๐Ÿง 
  • -mmillicode โ€” millicode ๐Ÿงž
  • -mmixed-code โ€” mixed code ๐ŸŽจ
  • -mq-class โ€” Q class ๐Ÿท๏ธ
  • -mRcq โ€” Rcq ๐Ÿท๏ธ
  • -mRcw โ€” Rcw ๐Ÿท๏ธ
  • -msize-level=level โ€” size level ๐Ÿ“
  • -mtune=cpu โ€” tuning CPU ๐ŸŽš๏ธ
  • -mmultcost=num โ€” multiply cost ๐Ÿ’ธ
  • -mcode-density-frame โ€” code density frame ๐Ÿงฑ
  • -munalign-prob-threshold=probability โ€” unaligned probability threshold ๐ŸŽฒ
  • -mmpy-option=multo โ€” multiply option ๐Ÿงฎ
  • -mdiv-rem โ€” divide and remainder โž—
  • -mcode-density โ€” code density ๐Ÿงฑ
  • -mll64 โ€” LL64 ๐Ÿงฉ
  • -mfpu=fpu โ€” FPU pick ๐Ÿ”ข
  • -mrf16 โ€” RF16 ๐Ÿงฉ
  • -mbranch-index โ€” branch index ๐Ÿงญ

๐ŸŽจ ARM Options (ARM Options) ๐ŸŽจ

  • -mapcs-frame โ€” APCS frame ๐Ÿ–ผ๏ธ
  • -mno-apcs-frame โ€” no APCS frame โŒ
  • -mabi=name โ€” ABI pick ๐Ÿงพ
  • -mapcs-stack-check โ€” APCS stack check ๐Ÿ”
  • -mno-apcs-stack-check โ€” no APCS stack check โŒ
  • -mapcs-reentrant โ€” APCS reentrant ๐Ÿ”
  • -mno-apcs-reentrant โ€” no APCS reentrant โŒ
  • -mgeneral-regs-only โ€” general registers only ๐ŸŽ€
  • -msched-prolog โ€” scheduled prologue ๐Ÿ—“๏ธ
  • -mno-sched-prolog โ€” no scheduled prologue โŒ
  • -mlittle-endian โ€” little-endian ๐ŸŒธ
  • -mbig-endian โ€” big-endian ๐ŸŒธ
  • -mbe8 โ€” BE-8 ๐ŸŒธ
  • -mbe32 โ€” BE-32 ๐ŸŒธ
  • -mfloat-abi=name โ€” float ABI ๐Ÿงˆ
  • -mfp16-format=name โ€” FP16 format ๐Ÿ”ข
  • -mthumb-interwork โ€” Thumb interworking ๐Ÿ”€
  • -mno-thumb-interwork โ€” no Thumb interworking โŒ
  • -mcpu=name โ€” CPU pick ๐Ÿง 
  • -march=name โ€” architecture pick ๐Ÿ›๏ธ
  • -mfpu=name โ€” FPU pick ๐Ÿ”ข
  • -mtune=name โ€” tuning pick ๐ŸŽš๏ธ
  • -mprint-tune-info โ€” print tuning info ๐Ÿ–จ๏ธ
  • -mstructure-size-boundary=n โ€” structure size boundary ๐Ÿ“
  • -mabort-on-noreturn โ€” abort on noreturn ๐Ÿ›‘
  • -mlong-calls โ€” long calls ๐Ÿ“ž
  • -mno-long-calls โ€” no long calls โŒ
  • -msingle-pic-base โ€” single PIC base ๐Ÿงญ
  • -mno-single-pic-base โ€” no single PIC base โŒ
  • -mpic-register=reg โ€” PIC register ๐Ÿงญ
  • -mnop-fun-dllimport โ€” nop for dllimport ๐Ÿซง
  • -mpoke-function-name โ€” poke function name ๐Ÿ“
  • -mthumb โ€” Thumb ๐Ÿงต
  • -marm โ€” ARM mode ๐ŸŽจ
  • -mflip-thumb โ€” flip Thumb ๐Ÿ”
  • -mtpcs-frame โ€” TPCS frame ๐Ÿ–ผ๏ธ
  • -mtpcs-leaf-frame โ€” TPCS leaf frame ๐Ÿ–ผ๏ธ
  • -mcaller-super-interworking โ€” caller super interworking ๐Ÿ”€
  • -mcallee-super-interworking โ€” callee super interworking ๐Ÿ”€
  • -mtp=name โ€” TP pick ๐Ÿงต
  • -mtls-dialect=dialect โ€” TLS dialect ๐Ÿฆ‹
  • -mword-relocations โ€” word relocations ๐Ÿงฉ
  • -mfix-cortex-m3-ldrd โ€” Cortex-M3 LDRD fix ๐Ÿ”ง
  • -mfix-cortex-a57-aes-1742098 โ€” Cortex-A57 AES fix ๐Ÿ”ง
  • -mfix-cortex-a72-aes-1655431 โ€” Cortex-A72 AES fix ๐Ÿ”ง
  • -munaligned-access โ€” unaligned access ๐Ÿซธ
  • -mneon-for-64bits โ€” NEON for 64-bit ๐ŸŽถ
  • -mslow-flash-data โ€” slow flash data ๐Ÿข
  • -masm-syntax-unified โ€” unified assembler syntax ๐Ÿ“
  • -mrestrict-it โ€” restrict IT ๐Ÿ”’
  • -mverbose-cost-dump โ€” verbose cost dump ๐Ÿ“œ
  • -mpure-code โ€” pure code ๐Ÿงผ
  • -mcmse โ€” CMSE ๐Ÿ›ก๏ธ
  • -mfix-cmse-cve-2021-35465 โ€” CMSE CVE fix ๐Ÿ›ก๏ธ
  • -mstack-protector-guard=guard โ€” stack protector guard ๐Ÿ›ก๏ธ
  • -mstack-protector-guard-offset=offset โ€” guard offset ๐Ÿ›ก๏ธ
  • -mfdpic โ€” FDPIC ๐Ÿงญ
  • -mbranch-protection=none|standard|pac-ret[+leaf] โ€” branch protection, pick your vibe ๐Ÿ›ก๏ธ
  • [+bti]|bti[+pac-ret[+leaf]] โ€” the rest of the branch protection combos ๐Ÿ›ก๏ธ

โšก AVR Options (AVR Options) โšก

  • -mmcu=mcu โ€” MCU pick ๐Ÿง 
  • -mabsdata โ€” absolute data ๐Ÿ“ฆ
  • -maccumulate-args โ€” accumulate args ๐Ÿ“š
  • -mcvt โ€” CVT ๐Ÿ”
  • -mbranch-cost=cost โ€” branch cost ๐Ÿ’ธ
  • -mfuse-add=level โ€” fuse add level โž•
  • -mfuse-move=level โ€” fuse move level โžก๏ธ
  • -mfuse-move2 โ€” fuse move 2 โžก๏ธ
  • -mcall-prologues โ€” call prologues ๐Ÿ—“๏ธ
  • -mgas-isr-prologues โ€” GAS ISR prologues ๐Ÿ—“๏ธ
  • -mint8 โ€” int8 ๐Ÿ”ข
  • -mflmap โ€” flmap ๐Ÿ—บ๏ธ
  • -mdouble=bits โ€” double width ๐Ÿ”ข
  • -mlong-double=bits โ€” long double width ๐Ÿ”ข
  • -mno-call-main โ€” no call to main โŒ
  • -mn_flash=size โ€” flash size ๐Ÿ—„๏ธ
  • -mfract-convert-truncate โ€” fract convert truncate โœ‚๏ธ
  • -mno-interrupts โ€” no interrupts โŒ
  • -mmain-is-OS_task โ€” main is an OS task ๐Ÿงต
  • -mrelax โ€” relax ๐Ÿง˜
  • -mrmw โ€” read-modify-write โœ๏ธ
  • -mstrict-X โ€” strict X ๐Ÿงญ
  • -mtiny-stack โ€” tiny stack ๐Ÿงธ
  • -mrodata-in-ram โ€” rodata in RAM ๐Ÿง 
  • -msplit-bit-shift โ€” split bit shift โ†”๏ธ
  • -msplit-ldst โ€” split load/store ๐Ÿ“ฆ
  • -mshort-calls โ€” short calls ๐Ÿ“ฑ
  • -mskip-bug โ€” skip bug ๐Ÿ›
  • -muse-nonzero-bits โ€” use nonzero bits ๐ŸŽฏ
  • -nodevicelib โ€” no device library ๐Ÿšซ
  • -nodevicespecs โ€” no device specs ๐Ÿšซ
  • -Waddr-space-convert โ€” address space convert warning โš ๏ธ
  • -Wmisspelled-isr โ€” misspelled ISR warning โš ๏ธ

๐ŸŽต Blackfin Options (Blackfin Options) ๐ŸŽต

  • -mcpu=cpu[-sirevision] โ€” CPU and silicon revision ๐Ÿง 
  • -msim โ€” simulator ๐ŸŽฎ
  • -momit-leaf-frame-pointer โ€” leaf frame pointer goes bye ๐Ÿ‘‹
  • -mno-omit-leaf-frame-pointer โ€” leaf frame pointer stays ๐Ÿซถ
  • -mspecld-anomaly โ€” speculative load anomaly ๐Ÿ›
  • -mno-specld-anomaly โ€” no speculative load anomaly โŒ
  • -mcsync-anomaly โ€” CSYNC anomaly ๐Ÿ›
  • -mno-csync-anomaly โ€” no CSYNC anomaly โŒ
  • -mlow-64k โ€” low 64k ๐Ÿง 
  • -mno-low64k โ€” no low 64k โŒ
  • -mstack-check-l1 โ€” stack check L1 ๐Ÿ”
  • -mid-shared-library โ€” ID shared library ๐Ÿ“š
  • -mno-id-shared-library โ€” no ID shared library โŒ
  • -mshared-library-id=n โ€” shared library ID ๐Ÿ”ข
  • -mleaf-id-shared-library โ€” leaf ID shared library ๐Ÿ“š
  • -mno-leaf-id-shared-library โ€” no leaf ID shared library โŒ
  • -msep-data โ€” separate data ๐Ÿงฉ
  • -mno-sep-data โ€” no separate data โŒ
  • -mlong-calls โ€” long calls ๐Ÿ“ž
  • -mno-long-calls โ€” no long calls โŒ
  • -mfast-fp โ€” fast FP ๐Ÿš€
  • -minline-plt โ€” inline PLT ๐Ÿงต
  • -mmulticore โ€” multicore ๐Ÿง 
  • -mcorea โ€” core A ๐Ÿ…ฐ๏ธ
  • -mcoreb โ€” core B ๐Ÿ…ฑ๏ธ
  • -msdram โ€” SDRAM ๐Ÿ—„๏ธ
  • -micplb โ€” ICPLB ๐Ÿงฑ

๐Ÿงฉ C6X Options (C6X Options) ๐Ÿงฉ

  • -mbig-endian โ€” big-endian ๐ŸŒธ
  • -mlittle-endian โ€” little-endian ๐ŸŒธ
  • -march=cpu โ€” architecture pick ๐Ÿ›๏ธ
  • -msim โ€” simulator ๐ŸŽฎ
  • -msdata=sdata-type โ€” small data type ๐Ÿ“ฆ

๐ŸฆŽ CRIS Options (CRIS Options) ๐ŸฆŽ

  • -mcpu=cpu โ€” CPU pick ๐Ÿง 
  • -march=cpu โ€” architecture pick ๐Ÿ›๏ธ
  • -mtune=cpu โ€” tuning pick ๐ŸŽš๏ธ
  • -mmax-stack-frame=n โ€” max stack frame size ๐Ÿ“
  • -metrax4 โ€” ETRAX 4 ๐Ÿงฉ
  • -metrax100 โ€” ETRAX 100 ๐Ÿงฉ
  • -mpdebug โ€” P-debug ๐Ÿ”
  • -mcc-init โ€” CC init ๐Ÿงญ
  • -mno-side-effects โ€” no side effects โŒ
  • -mstack-align โ€” stack alignment ๐Ÿ“
  • -mdata-align โ€” data alignment ๐Ÿ“
  • -mconst-align โ€” constant alignment ๐Ÿ“
  • -m32-bit โ€” 32-bit ๐Ÿ”ข
  • -m16-bit โ€” 16-bit ๐Ÿ”ข
  • -m8-bit โ€” 8-bit ๐Ÿ”ข
  • -mno-prologue-epilogue โ€” no prologue or epilogue โŒ
  • -melf โ€” ELF ๐Ÿงพ
  • -maout โ€” a.out ๐Ÿงพ
  • -sim โ€” simulator ๐ŸŽฎ
  • -sim2 โ€” simulator 2 ๐ŸŽฎ
  • -mmul-bug-workaround โ€” multiply bug workaround ๐Ÿ›
  • -mno-mul-bug-workaround โ€” no multiply bug workaround โŒ

๐ŸŒ™ C-SKY Options (C-SKY Options) ๐ŸŒ™

  • -march=arch โ€” architecture pick ๐Ÿ›๏ธ
  • -mcpu=cpu โ€” CPU pick ๐Ÿง 
  • -mbig-endian โ€” big-endian ๐ŸŒธ
  • -EB โ€” big-endian, the short way ๐ŸŒธ
  • -mlittle-endian โ€” little-endian ๐ŸŒธ
  • -EL โ€” little-endian, the short way ๐ŸŒธ
  • -mhard-float โ€” hard float ๐Ÿงˆ
  • -msoft-float โ€” soft float ๐Ÿงˆ
  • -mfpu=fpu โ€” FPU pick ๐Ÿ”ข
  • -mdouble-float โ€” double float ๐Ÿ”ข
  • -mfdivdu โ€” FDIVDU โž—
  • -mfloat-abi=name โ€” float ABI ๐Ÿงˆ
  • -melrw โ€” ELRW ๐Ÿงฉ
  • -mistack โ€” interrupt stack ๐Ÿ›‘
  • -mmp โ€” MP ๐Ÿงต
  • -mcp โ€” CP ๐Ÿงฉ
  • -mcache โ€” cache ๐Ÿ—„๏ธ
  • -msecurity โ€” security ๐Ÿ›ก๏ธ
  • -mtrust โ€” trust ๐Ÿ›ก๏ธ
  • -mdsp โ€” DSP ๐ŸŽถ
  • -medsp โ€” enhanced DSP ๐ŸŽถ
  • -mvdsp โ€” vector DSP ๐ŸŽถ
  • -mdiv โ€” divide โž—
  • -msmart โ€” smart mode ๐Ÿง 
  • -mhigh-registers โ€” high registers ๐Ÿ”๏ธ
  • -manchor โ€” anchor ๐Ÿงญ
  • -mpushpop โ€” push and pop ๐Ÿ“š
  • -mmultiple-stld โ€” multiple load/store ๐Ÿ“ฆ
  • -mconstpool โ€” constant pool ๐Ÿงบ
  • -mstack-size โ€” stack size ๐Ÿ“
  • -mccrt โ€” CCRT ๐Ÿงฉ
  • -mbranch-cost=n โ€” branch cost ๐Ÿ’ธ
  • -mcse-cc โ€” CSE CC ๐Ÿ”
  • -msched-prolog โ€” scheduled prologue ๐Ÿ—“๏ธ
  • -msim โ€” simulator ๐ŸŽฎ

๐ŸชŸ Cygwin and MinGW Options (Cygwin and MinGW Options) ๐ŸชŸ

  • -mconsole โ€” console ๐Ÿ–ฅ๏ธ
  • -mcrtdll=library โ€” CRT DLL library ๐Ÿ“š
  • -mdll โ€” DLL ๐Ÿงฉ
  • -mnop-fun-dllimport โ€” nop for dllimport ๐Ÿซง
  • -mthread โ€” thread ๐Ÿงต
  • -municode โ€” Unicode ๐Ÿ”ค
  • -mwin32 โ€” Win32 ๐ŸชŸ
  • -mwindows โ€” Windows ๐ŸชŸ
  • -fno-set-stack-executable โ€” no executable stack setting ๐Ÿšซ

๐ŸŽ Darwin Options (Darwin Options) ๐ŸŽ

  • -all_load โ€” all load ๐Ÿ“ฅ
  • -allowable_client โ€” allowable client ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘
  • -arch โ€” arch ๐Ÿ›๏ธ
  • -arch_errors_fatal โ€” arch errors fatal ๐Ÿ›‘
  • -arch_only โ€” arch only ๐Ÿ›๏ธ
  • -bind_at_load โ€” bind at load ๐Ÿ”—
  • -bundle โ€” bundle ๐Ÿ“ฆ
  • -bundle_loader โ€” bundle loader ๐Ÿ“ฆ
  • -client_name โ€” client name ๐Ÿท๏ธ
  • -compatibility_version โ€” compatibility version ๐Ÿ”ข
  • -current_version โ€” current version ๐Ÿ”ข
  • -dead_strip โ€” dead strip โœ‚๏ธ
  • -dependency-file โ€” dependency file ๐Ÿ“„
  • -dylib_file โ€” dylib file ๐Ÿ“„
  • -dylinker_install_name โ€” dylinker install name ๐Ÿท๏ธ
  • -dynamic โ€” dynamic ๐ŸŒ€
  • -dynamiclib โ€” dynamic library ๐Ÿ“š
  • -exported_symbols_list โ€” exported symbols list ๐Ÿ“œ
  • -filelist โ€” file list ๐Ÿ“œ
  • -flat_namespace โ€” flat namespace ๐Ÿ—‚๏ธ
  • -force_cpusubtype_ALL โ€” force CPU subtype ALL ๐Ÿง 
  • -force_flat_namespace โ€” force flat namespace ๐Ÿ—‚๏ธ
  • -headerpad_max_install_names โ€” header pad for install names ๐Ÿ“
  • -iframework โ€” include framework ๐Ÿ“š
  • -image_base โ€” image base ๐Ÿ–ผ๏ธ
  • -init โ€” init ๐ŸŒฑ
  • -install_name โ€” install name ๐Ÿท๏ธ
  • -keep_private_externs โ€” keep private externs ๐Ÿ”’
  • -multi_module โ€” multi-module ๐Ÿงฉ
  • -multiply_defined โ€” multiply defined ๐Ÿ”
  • -multiply_defined_unused โ€” multiply defined unused ๐Ÿ”
  • -noall_load โ€” no all load โŒ
  • -no_dead_strip_inits_and_terms โ€” no dead stripping of inits and terms ๐Ÿšซ
  • -nodefaultrpaths โ€” no default rpaths ๐Ÿšซ
  • -nofixprebinding โ€” no fix prebinding ๐Ÿšซ
  • -nomultidefs โ€” no multidefs ๐Ÿšซ
  • -noprebind โ€” no prebind ๐Ÿšซ
  • -noseglinkedit โ€” no segment link edit ๐Ÿšซ
  • -pagezero_size โ€” page zero size ๐Ÿ“
  • -prebind โ€” prebind ๐Ÿ”—
  • -prebind_all_twolevel_modules โ€” prebind all two-level modules ๐Ÿ”—
  • -private_bundle โ€” private bundle ๐Ÿ”’
  • -read_only_relocs โ€” read-only relocs ๐Ÿ“–
  • -sectalign โ€” section align ๐Ÿ“
  • -sectobjectsymbols โ€” section object symbols ๐Ÿงพ
  • -whyload โ€” why load โ“
  • -seg1addr โ€” segment 1 address ๐Ÿ“
  • -sectcreate โ€” section create ๐Ÿงฑ
  • -sectobjectsymbols โ€” section object symbols ๐Ÿงพ
  • -sectorder โ€” section order ๐Ÿ“š
  • -segaddr โ€” segment address ๐Ÿ“
  • -segs_read_only_addr โ€” segments read-only address ๐Ÿ“
  • -segs_read_write_addr โ€” segments read-write address ๐Ÿ“
  • -seg_addr_table โ€” segment address table ๐Ÿ—‚๏ธ
  • -seg_addr_table_filename โ€” segment address table filename ๐Ÿ“„
  • -seglinkedit โ€” segment link edit ๐Ÿ”—
  • -segprot โ€” segment protection ๐Ÿ›ก๏ธ
  • -segs_read_only_addr โ€” segments read-only address ๐Ÿ“
  • -segs_read_write_addr โ€” segments read-write address ๐Ÿ“
  • -single_module โ€” single module ๐Ÿงฉ
  • -static โ€” static ๐Ÿงฑ
  • -sub_library โ€” sub library ๐Ÿ“š
  • -sub_umbrella โ€” sub umbrella โ˜‚๏ธ
  • -twolevel_namespace โ€” two-level namespace ๐Ÿ—‚๏ธ
  • -umbrella โ€” umbrella โ˜‚๏ธ
  • -undefined โ€” undefined โ“
  • -unexported_symbols_list โ€” unexported symbols list ๐Ÿ“œ
  • -weak_reference_mismatches โ€” weak reference mismatches โš ๏ธ
  • -whatsloaded โ€” what's loaded ๐Ÿ“‹
  • -F โ€” framework path ๐Ÿ“
  • -gused โ€” gused ๐Ÿงพ
  • -gfull โ€” gfull ๐Ÿงพ
  • -mmacosx-version-min=version โ€” minimum macOS version ๐Ÿ
  • -mkernel โ€” kernel ๐Ÿง 
  • -mone-byte-bool โ€” one-byte bool ๐Ÿ”ข

๐Ÿงญ DEC Alpha Options (DEC Alpha Options) ๐Ÿงญ

  • -mno-fp-regs โ€” no FP registers โŒ
  • -msoft-float โ€” soft float ๐Ÿงˆ
  • -mieee โ€” IEEE ๐Ÿ”ข
  • -mieee-with-inexact โ€” IEEE with inexact ๐Ÿ”ข
  • -mieee-conformant โ€” IEEE conformant ๐Ÿ”ข
  • -mfp-trap-mode=mode โ€” FP trap mode ๐Ÿชค
  • -mfp-rounding-mode=mode โ€” FP rounding mode ๐ŸŽฏ
  • -mtrap-precision=mode โ€” trap precision ๐ŸŽฏ
  • -mbuild-constants โ€” build constants ๐Ÿงฑ
  • -mcpu=cpu-type โ€” CPU type pick ๐Ÿง 
  • -mtune=cpu-type โ€” tuning CPU type ๐ŸŽš๏ธ
  • -mbwx โ€” BWX ๐Ÿงฉ
  • -mmax โ€” MAX ๐Ÿงฉ
  • -mfix โ€” FIX ๐Ÿงฉ
  • -mcix โ€” CIX ๐Ÿงฉ
  • -msafe-bwa โ€” safe BWA ๐Ÿ›ก๏ธ
  • -msafe-partial โ€” safe partial ๐Ÿ›ก๏ธ
  • -mfloat-vax โ€” VAX float ๐Ÿงˆ
  • -mfloat-ieee โ€” IEEE float ๐Ÿงˆ
  • -mexplicit-relocs โ€” explicit relocs ๐Ÿงฉ
  • -msmall-data โ€” small data ๐Ÿ“ฆ
  • -mlarge-data โ€” large data ๐Ÿ“ฆ
  • -msmall-text โ€” small text ๐Ÿ“ฆ
  • -mlarge-text โ€” large text ๐Ÿ“ฆ
  • -mmemory-latency=time โ€” memory latency ๐Ÿ•ฐ๏ธ

๐Ÿ eBPF Options (eBPF Options) ๐Ÿ

  • -mbig-endian โ€” big-endian ๐ŸŒธ
  • -mlittle-endian โ€” little-endian ๐ŸŒธ
  • -mframe-limit=bytes โ€” frame limit in bytes ๐Ÿ“
  • -mxbpf โ€” XBPF ๐Ÿงฉ
  • -mco-re โ€” CO-RE ๐Ÿงฉ
  • -mno-co-re โ€” no CO-RE โŒ
  • -mjmpext โ€” jump extension โฉ
  • -mjmp32 โ€” 32-bit jumps โฉ
  • -malu32 โ€” ALU32 โž•
  • -mv3-atomics โ€” v3 atomics โš›๏ธ
  • -mbswap โ€” byte swap ๐Ÿ”„
  • -msdiv โ€” signed divide โž—
  • -msmov โ€” signed move โžก๏ธ
  • -mcpu=version โ€” CPU version ๐Ÿง 
  • -masm=dialect โ€” assembler dialect ๐Ÿ“
  • -minline-memops-threshold=bytes โ€” inline memops threshold ๐Ÿ“

๐Ÿฆ‹ FR30 Options (FR30 Options) ๐Ÿฆ‹

  • -msmall-model โ€” small model ๐Ÿงธ
  • -mno-lsim โ€” no LSIM โŒ

๐ŸŒท FT32 Options (FT32 Options) ๐ŸŒท

  • -msim โ€” simulator ๐ŸŽฎ
  • -mlra โ€” LRA ๐Ÿง 
  • -mnodiv โ€” no divide โŒ
  • -mft32b โ€” FT32B ๐Ÿงฉ
  • -mcompress โ€” compress ๐Ÿ—œ๏ธ
  • -mnopm โ€” no PM โŒ

๐ŸŒˆ FRV Options (FRV Options) ๐ŸŒˆ

  • -mgpr-32 โ€” 32 GPRs ๐Ÿ”ข
  • -mgpr-64 โ€” 64 GPRs ๐Ÿ”ข
  • -mfpr-32 โ€” 32 FPRs ๐Ÿ”ข
  • -mfpr-64 โ€” 64 FPRs ๐Ÿ”ข
  • -mhard-float โ€” hard float ๐Ÿงˆ
  • -msoft-float โ€” soft float ๐Ÿงˆ
  • -malloc-cc โ€” alloc CC ๐Ÿงญ
  • -mfixed-cc โ€” fixed CC ๐Ÿงญ
  • -mdword โ€” doubleword ๐Ÿ”ข
  • -mno-dword โ€” no doubleword โŒ
  • -mdouble โ€” double ๐Ÿ”ข
  • -mno-double โ€” no double โŒ
  • -mmedia โ€” media ๐ŸŽถ
  • -mno-media โ€” no media โŒ
  • -mmuladd โ€” multiply-add โœ–๏ธ
  • -mno-muladd โ€” no multiply-add โŒ
  • -mfdpic โ€” FDPIC ๐Ÿงญ
  • -minline-plt โ€” inline PLT ๐Ÿงต
  • -mgprel-ro โ€” GP-relative read-only ๐Ÿ“–
  • -multilib-library-pic โ€” multilib library PIC ๐Ÿ“š
  • -mlinked-fp โ€” linked frame pointer ๐Ÿ”—
  • -mlong-calls โ€” long calls ๐Ÿ“ž
  • -malign-labels โ€” align labels ๐Ÿ“
  • -mlibrary-pic โ€” library PIC ๐Ÿ“š
  • -macc-4 โ€” ACC 4 ๐Ÿงฉ
  • -macc-8 โ€” ACC 8 ๐Ÿงฉ
  • -mpack โ€” pack ๐Ÿ“ฆ
  • -mno-pack โ€” no pack โŒ
  • -mno-eflags โ€” no eflags โŒ
  • -mcond-move โ€” conditional move ๐ŸŽ€
  • -mno-cond-move โ€” no conditional move โŒ
  • -moptimize-membar โ€” optimize membar ๐Ÿงน
  • -mno-optimize-membar โ€” no membar optimization โŒ
  • -mscc โ€” SCC ๐Ÿงฉ
  • -mno-scc โ€” no SCC โŒ
  • -mcond-exec โ€” conditional execution ๐ŸŽ›๏ธ
  • -mno-cond-exec โ€” no conditional execution โŒ
  • -mvliw-branch โ€” VLIW branch ๐Ÿง 
  • -mno-vliw-branch โ€” no VLIW branch โŒ
  • -mmulti-cond-exec โ€” multi conditional execution ๐ŸŽ›๏ธ
  • -mno-multi-cond-exec โ€” no multi conditional execution โŒ
  • -mnested-cond-exec โ€” nested conditional execution ๐Ÿช†
  • -mno-nested-cond-exec โ€” no nested conditional execution โŒ
  • -mtomcat-stats โ€” Tomcat stats ๐Ÿ“Š
  • -mTLS โ€” TLS ๐Ÿฆ‹
  • -mtls โ€” TLS ๐Ÿฆ‹
  • -mcpu=cpu โ€” CPU pick ๐Ÿง 

๐Ÿง GNU/Linux Options (GNU/Linux Options) ๐Ÿง

  • -mglibc โ€” glibc ๐Ÿง
  • -muclibc โ€” uClibc ๐Ÿง
  • -mmusl โ€” musl ๐Ÿง
  • -mbionic โ€” Bionic ๐Ÿค–
  • -mandroid โ€” Android ๐Ÿค–
  • -tno-android-cc โ€” no Android CC โŒ
  • -tno-android-ld โ€” no Android LD โŒ

๐Ÿ•น๏ธ H8/300 Options (H8/300 Options) ๐Ÿ•น๏ธ

  • -mrelax โ€” relax ๐Ÿง˜
  • -mh โ€” H ๐Ÿ”ค
  • -ms โ€” S ๐Ÿ”ค
  • -mn โ€” N ๐Ÿ”ค
  • -mexr โ€” EXR ๐Ÿงฉ
  • -mno-exr โ€” no EXR โŒ
  • -mint32 โ€” int32 ๐Ÿ”ข
  • -malign-300 โ€” align 300 ๐Ÿ“

๐Ÿงฑ HPPA Options (HPPA Options) ๐Ÿงฑ

  • -march=architecture-type โ€” architecture type ๐Ÿ›๏ธ
  • -matomic-libcalls โ€” atomic libcalls โš›๏ธ
  • -mbig-switch โ€” big switch ๐Ÿ”€
  • -mcaller-copies โ€” caller copies ๐Ÿ“‹
  • -mdisable-fpregs โ€” disable FP registers ๐Ÿšซ
  • -mdisable-indexing โ€” disable indexing ๐Ÿšซ
  • -mordered โ€” ordered ๐Ÿ“š
  • -mfast-indirect-calls โ€” fast indirect calls ๐Ÿš€
  • -mgas โ€” GAS ๐Ÿงฉ
  • -mgnu-ld โ€” GNU ld ๐Ÿงฉ
  • -mhp-ld โ€” HP ld ๐Ÿงฉ
  • -mfixed-range=register-range โ€” fixed register range ๐Ÿ“
  • -mcoherent-ldcw โ€” coherent LDCW ๐Ÿงฒ
  • -mjump-in-delay โ€” jump in delay slot โฉ
  • -mlinker-opt โ€” linker optimization ๐Ÿงน
  • -mlong-calls โ€” long calls ๐Ÿ“ž
  • -mlong-load-store โ€” long load/store ๐Ÿ“ฆ
  • -mno-atomic-libcalls โ€” no atomic libcalls โŒ
  • -mno-disable-fpregs โ€” no disabling FP registers โœ…
  • -mno-disable-indexing โ€” no disabling indexing โœ…
  • -mno-fast-indirect-calls โ€” no fast indirect calls โŒ
  • -mno-gas โ€” no GAS โŒ
  • -mno-jump-in-delay โ€” no jump in delay slot โŒ
  • -mno-long-load-store โ€” no long load/store โŒ
  • -mno-portable-runtime โ€” no portable runtime โŒ
  • -mno-soft-float โ€” no soft float โŒ
  • -mno-space-regs โ€” no space registers โŒ
  • -msoft-float โ€” soft float ๐Ÿงˆ
  • -mpa-risc-1-0 โ€” PA-RISC 1.0 ๐Ÿงฑ
  • -mpa-risc-1-1 โ€” PA-RISC 1.1 ๐Ÿงฑ
  • -mpa-risc-2-0 โ€” PA-RISC 2.0 ๐Ÿงฑ
  • -mportable-runtime โ€” portable runtime ๐Ÿ“ฑ
  • -mschedule=cpu-type โ€” schedule CPU type ๐Ÿ—“๏ธ
  • -mspace-regs โ€” space registers ๐ŸŒŒ
  • -msoft-mult โ€” soft multiply โœ–๏ธ
  • -msio โ€” SIO ๐Ÿงฉ
  • -mwsio โ€” WSIO ๐Ÿงฉ
  • -munix=unix-std โ€” UNIX standard ๐Ÿงพ
  • -nolibdld โ€” no libdld ๐Ÿšซ
  • -static โ€” static ๐Ÿงฑ
  • -threads โ€” threads ๐Ÿงต

๐Ÿ”๏ธ IA-64 Options (IA-64 Options) ๐Ÿ”๏ธ

  • -mbig-endian โ€” big-endian ๐ŸŒธ
  • -mlittle-endian โ€” little-endian ๐ŸŒธ
  • -mgnu-as โ€” GNU as ๐Ÿงฉ
  • -mgnu-ld โ€” GNU ld ๐Ÿงฉ
  • -mno-pic โ€” no PIC โŒ
  • -mvolatile-asm-stop โ€” volatile asm stop ๐Ÿ›‘
  • -mregister-names โ€” register names ๐Ÿท๏ธ
  • -msdata โ€” small data ๐Ÿ“ฆ
  • -mno-sdata โ€” no small data โŒ
  • -mconstant-gp โ€” constant GP ๐Ÿงญ
  • -mauto-pic โ€” auto PIC ๐Ÿงญ
  • -mfused-madd โ€” fused multiply-add โœ–๏ธ
  • -minline-float-divide-min-latency โ€” inline float divide, min latency โž—
  • -minline-float-divide-max-throughput โ€” inline float divide, max throughput โž—
  • -mno-inline-float-divide โ€” no inline float divide โŒ
  • -minline-int-divide-min-latency โ€” inline int divide, min latency โž—
  • -minline-int-divide-max-throughput โ€” inline int divide, max throughput โž—
  • -mno-inline-int-divide โ€” no inline int divide โŒ
  • -minline-sqrt-min-latency โ€” inline sqrt, min latency ๐Ÿงฎ
  • -minline-sqrt-max-throughput โ€” inline sqrt, max throughput ๐Ÿงฎ
  • -mno-inline-sqrt โ€” no inline sqrt โŒ
  • -mdwarf2-asm โ€” DWARF2 asm ๐Ÿงพ
  • -mearly-stop-bits โ€” early stop bits ๐Ÿ›‘
  • -mfixed-range=register-range โ€” fixed register range ๐Ÿ“
  • -mtls-size=tls-size โ€” TLS size ๐Ÿ“
  • -mtune=cpu-type โ€” tuning CPU type ๐ŸŽš๏ธ
  • -milp32 โ€” ILP32 ๐Ÿ”ข
  • -mlp64 โ€” LP64 ๐Ÿ”ข
  • -msched-br-data-spec โ€” branch data speculation ๐Ÿ”ฎ
  • -msched-ar-data-spec โ€” AR data speculation ๐Ÿ”ฎ
  • -msched-control-spec โ€” control speculation ๐Ÿ”ฎ
  • -msched-br-in-data-spec โ€” branch in data speculation ๐Ÿ”ฎ
  • -msched-ar-in-data-spec โ€” AR in data speculation ๐Ÿ”ฎ
  • -msched-in-control-spec โ€” in-control speculation ๐Ÿ”ฎ
  • -msched-spec-ldc โ€” speculative LDC ๐Ÿ”ฎ
  • -msched-spec-control-ldc โ€” speculative control LDC ๐Ÿ”ฎ
  • -msched-prefer-non-data-spec-insns โ€” prefer non-data-speculative insns ๐ŸŽฏ
  • -msched-prefer-non-control-spec-insns โ€” prefer non-control-speculative insns ๐ŸŽฏ
  • -msched-stop-bits-after-every-cycle โ€” stop bits after every cycle ๐Ÿ›‘
  • -msched-count-spec-in-critical-path โ€” count speculation in critical path ๐Ÿงฎ
  • -msel-sched-dont-check-control-spec โ€” selective scheduler, skip control spec check ๐Ÿ™ˆ
  • -msched-fp-mem-deps-zero-cost โ€” FP memory deps at zero cost ๐Ÿ’ธ
  • -msched-max-memory-insns-hard-limit โ€” max memory insns hard limit ๐Ÿ›‘
  • -msched-max-memory-insns=max-insns โ€” max memory insns ๐Ÿ”ข

๐Ÿง  LM32 Options (LM32 Options) ๐Ÿง 

  • -mbarrel-shift-enabled โ€” barrel shift enabled ๐ŸŒ€
  • -mdivide-enabled โ€” divide enabled โž—
  • -mmultiply-enabled โ€” multiply enabled โœ–๏ธ
  • -msign-extend-enabled โ€” sign extend enabled โž•
  • -muser-enabled โ€” user enabled ๐Ÿซถ

Okay bestie, this is the machine-specific flag pile, so grab a snack ๐Ÿ“ Each target gets its own little drawer, and the page just lists the flags with no fine print, so I'm keeping it exactly as-is ๐Ÿ’…

๐Ÿฆ‹ LoongArch Options ๐Ÿฆ‹

-march=arch-type -mtune=tune-type -mabi=base-abi-type -mfpu=fpu-type
-msimd=simd-type -msoft-float -msingle-float -mdouble-float -mlsx -mno-lsx
-mlasx -mno-lasx -mbranch-cost=n -maddr-reg-reg-cost=n
-mcheck-zero-division -mno-check-zero-division -mcond-move-int
-mno-cond-move-int -mcond-move-float -mno-cond-move-float -memcpy
-mno-memcpy -mstrict-align -mno-strict-align -G num
-mmax-inline-memcpy-size=n -mexplicit-relocs=style -mexplicit-relocs
-mno-explicit-relocs -mdirect-extern-access -mno-direct-extern-access
-mcmodel=code-model -mrelax -mpass-mrelax-to-as -mrecip -mrecip=opt
-mfrecipe -mno-frecipe -mdiv32 -mno-div32 -mlam-bh -mno-lam-bh -mlamcas
-mno-lamcas -mld-seq-sa -mno-ld-seq-sa -mtls-dialect=opt
-mannotate-tablejump -mno-annotate-tablejump

๐ŸŒธ M32C Options ๐ŸŒธ

-mcpu=cpu -msim -memregs=number

๐Ÿงš M32R/D Options ๐Ÿงš

-m32r2 -m32rx -m32r -mdebug -malign-loops -mno-align-loops
-missue-rate=number -mbranch-cost=number -mmodel=code-size-model-type
-msdata=sdata-type -mno-flush-func -mflush-func=name -mno-flush-trap
-mflush-trap=number -G num

๐Ÿ’œ M680x0 Options ๐Ÿ’œ

-march=arch -mcpu=cpu -mtune=tune -m68000 -m68020 -m68020-40 -m68020-60
-m68030 -m68040 -m68060 -mcpu32 -m5200 -m5206e -m528x -m5307 -m5407
-mcfv4e -mbitfield -mno-bitfield -mc68000 -mc68020 -mnobitfield -mrtd
-mno-rtd -mdiv -mno-div -mshort -mno-short -mhard-float -m68881
-msoft-float -mpcrel -malign-int -mstrict-align -msep-data -mno-sep-data
-mshared-library-id=n -mid-shared-library -mno-id-shared-library -mxgot
-mno-xgot -mlong-jump-table-offsets

๐ŸŽ€ MCore Options ๐ŸŽ€

-mhardlit -mno-hardlit -mdiv -mno-div -mrelax-immediates
-mno-relax-immediates -mwide-bitfields -mno-wide-bitfields
-m4byte-functions -mno-4byte-functions -mcallgraph-data
-mno-callgraph-data -mslow-bytes -mno-slow-bytes -mno-lsim -mlittle-endian
-mbig-endian -m210 -m340 -mstack-increment

๐Ÿ”ฅ MicroBlaze Options ๐Ÿ”ฅ

-msoft-float -mhard-float -msmall-divides -mcpu=cpu -mmemcpy
-mxl-soft-mul -mxl-soft-div -mxl-barrel-shift -mxl-pattern-compare
-mxl-stack-check -mxl-gp-opt -mno-clearbss -mxl-multiply-high
-mxl-float-convert -mxl-float-sqrt -mbig-endian -mlittle-endian
-mxl-reorder -mxl-mode-app-model -mpic-data-is-text-relative

๐Ÿ‘‘ MIPS Options ๐Ÿ‘‘

Lowkey the biggest drawer in the whole closet, so let's gooo โœจ

-EL -EB -march=arch -mtune=arch -mips1 -mips2 -mips3 -mips4 -mips32
-mips32r2 -mips32r3 -mips32r5 -mips32r6 -mips64 -mips64r2 -mips64r3
-mips64r5 -mips64r6 -mips16 -mno-mips16 -mflip-mips16
-minterlink-compressed -mno-interlink-compressed -minterlink-mips16
-mno-interlink-mips16 -mabi=abi -mabicalls -mno-abicalls -mshared
-mno-shared -mplt -mno-plt -mxgot -mno-xgot -mgp32 -mgp64 -mfp32 -mfpxx
-mfp64 -mhard-float -msoft-float -mno-float -msingle-float
-mdouble-float -modd-spreg -mno-odd-spreg -mabs=mode -mnan=encoding -mdsp
-mno-dsp -mdspr2 -mno-dspr2 -mmcu -mmno-mcu -meva -mno-eva -mvirt
-mno-virt -mxpa -mno-xpa -mcrc -mno-crc -mginv -mno-ginv -mmicromips
-mno-micromips -mmsa -mno-msa -mloongson-mmi -mno-loongson-mmi
-mloongson-ext -mno-loongson-ext -mloongson-ext2 -mno-loongson-ext2
-mfpu=fpu-type -msmartmips -mno-smartmips -mpaired-single
-mno-paired-single -mdmx -mno-mdmx -mips3d -mno-mips3d -mmt -mno-mt
-mllsc -mno-llsc -mlong64 -mlong32 -msym32 -mno-sym32 -Gnum -mlocal-sdata
-mno-local-sdata -mextern-sdata -mno-extern-sdata -mgpopt -mno-gopt
-membedded-data -mno-embedded-data -muninit-const-in-rodata
-mno-uninit-const-in-rodata -mcode-readable=setting -msplit-addresses
-mno-split-addresses -mexplicit-relocs -mno-explicit-relocs
-mexplicit-relocs=release -mcheck-zero-division -mno-check-zero-division
-mdivide-traps -mdivide-breaks -mload-store-pairs -mno-load-store-pairs
-mstrict-align -mno-strict-align -mno-unaligned-access -munaligned-access
-mmemcpy -mno-memcpy -mlong-calls -mno-long-calls -mmad -mno-mad -mimadd
-mno-imadd -mfused-madd -mno-fused-madd -nocpp -mfix-24k -mno-fix-24k
-mfix-r4000 -mno-fix-r4000 -mfix-r4400 -mno-fix-r4400 -mfix-r5900
-mno-fix-r5900 -mfix-r10000 -mno-fix-r10000 -mfix-rm7000 -mno-fix-rm7000
-mfix-vr4120 -mno-fix-vr4120 -mfix-vr4130 -mno-fix-vr4130 -mfix-sb1
-mno-fix-sb1 -mflush-func=func -mno-flush-func -mbranch-cost=num
-mbranch-likely -mno-branch-likely -mcompact-branches=policy
-mfp-exceptions -mno-fp-exceptions -mvr4130-align -mno-vr4130-align
-msynci -mno-synci -mlxc1-sxc1 -mno-lxc1-sxc1 -mmadd4 -mno-madd4
-mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address
-mframe-header-opt -mno-frame-header-opt

๐ŸŒˆ MMIX Options ๐ŸŒˆ

-mlibfuncs -mno-libfuncs -mepsilon -mno-epsilon -mabi=gnu -mabi=mmixware
-mzero-extend -mknuthdiv -mtoplevel-symbols -melf -mbranch-predict
-mno-branch-predict -mbase-addresses -mno-base-addresses -msingle-exit
-mno-single-exit

๐Ÿซถ MN10300 Options ๐Ÿซถ

-mmult-bug -mno-mult-bug -mno-am33 -mam33 -mam33-2 -mam34 -mtune=cpu-type
-mreturn-pointer-on-d0 -mno-crt0 -mrelax -mliw -msetlb

๐Ÿ“ Moxie Options ๐Ÿ“

-meb -mel -mmul.x -mno-crt0

โญ MSP430 Options โญ

-msim -masm-hex -mmcu= -mcpu= -mlarge -msmall -mrelax -mwarn-mcu
-mcode-region= -mdata-region= -msilicon-errata= -msilicon-errata-warn=
-mhwmult= -minrt -mtiny-printf -mmax-inline-shift=

๐Ÿฆ‹ NDS32 Options ๐Ÿฆ‹

-mbig-endian -mlittle-endian -mreduced-regs -mfull-regs -mcmov -mno-cmov
-mext-perf -mno-ext-perf -mext-perf2 -mno-ext-perf2 -mext-string
-mno-ext-string -mv3push -mno-v3push -m16bit -mno-16bit
-misr-vector-size=num -mcache-block-size=num -march=arch
-mcmodel=code-model -mctor-dtor -mrelax

๐Ÿ’… Nvidia PTX Options ๐Ÿ’…

-m64 -mmainkernel -moptimize

๐ŸŒธ OpenRISC Options ๐ŸŒธ

-mboard=name -mnewlib -mhard-mul -mhard-div -msoft-mul -msoft-div
-msoft-float -mhard-float -mdouble-float -munordered-float -mcmov -mror
-mrori -msext -msfimm -mshftimm -mcmodel=code-model

๐Ÿงš PDP-11 Options ๐Ÿงš

-mfpu -msoft-float -mac0 -mno-ac0 -m40 -m45 -m10 -mint32 -mno-int16
-mint16 -mno-int32 -msplit -munix-asm -mdec-asm -mgnu-asm -mlra

๐Ÿ’œ PowerPC Options ๐Ÿ’œ

See RS/6000 and PowerPC Options.

๐ŸŽ€ PRU Options ๐ŸŽ€

-mmcu=mcu -minrt -mno-relax -mloop -mabi=variant

๐Ÿ”ฅ RISC-V Options ๐Ÿ”ฅ

-mbranch-cost=N-instruction -mplt -mno-plt -mabi=ABI-string -mfdiv
-mno-fdiv -mfence-tso -mno-fence-tso -mdiv -mno-div -misa-spec=ISA-spec-string
-march=ISA-string -mtune=processor-string -mpreferred-stack-boundary=num
-msmall-data-limit=N-bytes -msave-restore -mno-save-restore
-mshorten-memrefs -mno-shorten-memrefs -mstrict-align -mno-strict-align
-mcmodel=medlow -mcmodel=medany -mcmodel=large -mexplicit-relocs
-mno-explicit-relocs -mrelax -mno-relax -mriscv-attribute
-mno-riscv-attribute -malign-data=type -mbig-endian -mlittle-endian
-mstack-protector-guard=guard -mstack-protector-guard-reg=reg
-mstack-protector-guard-offset=offset -mcsr-check -mno-csr-check
-mmovcc -mno-movcc -minline-atomics -mno-inline-atomics -minline-strlen
-mno-inline-strlen -minline-strcmp -mno-inline-strcmp -minline-strncmp
-mno-inline-strncmp -mtls-dialect=desc -mtls-dialect=trad

๐Ÿ‘‘ RL78 Options ๐Ÿ‘‘

-msim -mmul=none -mmul=g13 -mmul=g14 -mallregs -mcpu=g10 -mcpu=g13
-mcpu=g14 -mg10 -mg13 -mg14 -m64bit-doubles -m32bit-doubles
-msave-mduc-in-interrupts

โœจ RS/6000 and PowerPC Options โœจ

Last drawer in this part, and it's a whole vibe ๐ŸŒˆ

-mcpu=cpu-type -mtune=cpu-type -mcmodel=code-model -mpowerpc64 -maltivec
-mno-altivec -mpowerpc-gpopt -mno-powerpc-gpopt -mpowerpc-gfxopt
-mno-powerpc-gfxopt -mmfcrf -mno-mfcrf -mpopcntb -mno-popcntb -mpopcntd
-mno-popcntd -mfprnd -mno-fprnd -mcmpb -mno-cmpb -mhard-dfp -mno-hard-dfp
-mfull-toc -mminimal-toc -mno-fp-in-toc -mno-sum-in-toc -m64 -m32
-mxl-compat -mno-xl-compat -mpe -malign-power -malign-natural
-msoft-float -mhard-float -mmultiple -mno-multiple -mupdate -mno-update
-mavoid-indexed-addresses -mno-avoid-indexed-addresses -mfused-madd
-mno-fused-madd -mbit-align -mno-bit-align -mstrict-align -mno-strict-align
-mrelocatable -mno-relocatable -mrelocatable-lib -mno-relocatable-lib
-mtoc -mno-toc -mlittle -mlittle-endian -mbig -mbig-endian
-mdynamic-no-pic -mswdiv -msingle-pic-base
-mprioritize-restricted-insns=priority
-msched-costly-dep=dependence_type -minsert-sched-nops=scheme
-mcall-aixdesc -mcall-eabi -mcall-freebsd -mcall-linux -mcall-netbsd
-mcall-openbsd -mcall-sysv -mcall-sysv-eabi -mcall-sysv-noeabi
-mtraceback=traceback_type -maix-struct-return -msvr4-struct-return
-mabi=abi-type -msecure-plt -mbss-plt -msplit-patch-nops -mlongcall
-mno-longcall -mpltseq -mno-pltseq -mblock-move-inline-limit=num
-mblock-compare-inline-limit=num -mblock-compare-inline-loop-limit=num
-mno-block-ops-unaligned-vsx -mstring-compare-inline-limit=num -misel
-mno-isel -mvrsave -mno-vrsave -mmulhw -mno-mulhw -mdlmzb -mno-dlmzb
-mprototype -mno-prototype -msim -mmvme -mads -myellowknife -memb -msdata
-msdata=opt -mreadonly-in-sdata -mvxworks -G num -mrecip -mrecip=opt
-mno-recip -mrecip-precision -mno-recip-precision -mveclibabi=type -mfriz
-mno-friz -mpointers-to-nested-functions -mno-pointers-to-nested-functions
-msave-toc-indirect -mno-save-toc-indirect -mpower8-fusion
-mno-mpower8-fusion -mcrypto -mno-crypto -mhtm -mno-htm -mquad-memory
-mno-quad-memory -mquad-memory-atomic -mno-quad-memory-atomic
-mcompat-align-parm -mno-compat-align-parm -mfloat128 -mno-float128
-mfloat128-hardware -mno-float128-hardware -mgnu-attribute
-mno-gnu-attribute -mstack-protector-guard=guard
-mstack-protector-guard-reg=reg -mstack-protector-guard-offset=offset
-mprefixed -mno-prefixed -mpcrel -mno-pcrel -mmma -mno-mmma -mrop-protect
-mno-rop-protect -mprivileged -mno-privileged

๐ŸŽ€ RX Options ๐ŸŽ€

okay so RX has its own little squad of options, bestie ๐Ÿ’– -m64bit-doubles -m32bit-doubles -fpu -nofpu -mcpu= -mbig-endian-data -mlittle-endian-data -msmall-data -msim -mno-sim -mas100-syntax -mno-as100-syntax -mrelax -mmax-constant-size= -mint-register= -mpid -mallow-string-insns -mno-allow-string-insns -mjsr -mno-warn-multiple-fast-interrupts -msave-acc-in-interrupts ๐Ÿงš

โœจ S/390 and zSeries Options โœจ

this one is giving big mainframe energy ๐Ÿ’… -mtune=cpu-type -march=cpu-type -mhard-float -msoft-float -mhard-dfp -mno-hard-dfp -mlong-double-64 -mlong-double-128 -mbackchain -mno-backchain -mpacked-stack -mno-packed-stack -msmall-exec -mno-small-exec -mmvcle -mno-mvcle -m64 -m31 -mdebug -mno-debug -mesa -mzarch -mhtm -mvx -mzvector -mtpf-trace -mno-tpf-trace -mtpf-trace-skip -mno-tpf-trace-skip -mfused-madd -mno-fused-madd -mwarn-framesize -mwarn-dynamicstack -mstack-size -mstack-guard -mhotpatch=halfwords,halfwords ๐Ÿ”ฅ

๐Ÿฆ‹ SH Options ๐Ÿฆ‹

SH has a whole lineup, no cap ๐Ÿ˜ญ -m1 -m2 -m2e -m2a-nofpu -m2a-single-only -m2a-single -m2a -m3 -m3e -m4-nofpu -m4-single-only -m4-single -m4 -m4a-nofpu -m4a-single-only -m4a-single -m4a -m4al -mb -ml -mdalign -mrelax -mbigtable -mfmovd -mrenesas -mno-renesas -mnomacsave -mieee -mno-ieee -mbitops -misize -minline-ic_invalidate -mpadstruct -mprefergot -musermode -multcost=number -mdiv=strategy -mdivsi3_libfunc=name -mfixed-range=register-range -maccumulate-outgoing-args -matomic-model=atomic-model -mbranch-cost=num -mzdcbranch -mno-zdcbranch -mcbranch-force-delay-slot -mfused-madd -mno-fused-madd -mfsca -mno-fsca -mfsrra -mno-fsrra -mpretend-cmove -mtas ๐ŸŒธ

๐Ÿซถ Solaris 2 Options ๐Ÿซถ

the Solaris crew is small but mighty โœจ -mclear-hwcap -mno-clear-hwcap -mimpure-text -mno-impure-text -pthreads ๐Ÿ’œ

๐Ÿ’… SPARC Options ๐Ÿ’…

SPARC has so many toggles it's unreal ๐Ÿ˜ญ -mcpu=cpu-type -mtune=cpu-type -mcmodel=code-model -mmemory-model=mem-model -m32 -m64 -mapp-regs -mno-app-regs -mfaster-structs -mno-faster-structs -mflat -mno-flat -mfpu -mno-fpu -mhard-float -msoft-float -mhard-quad-float -msoft-quad-float -mstack-bias -mno-stack-bias -mstd-struct-return -mno-std-struct-return -munaligned-doubles -mno-unaligned-doubles -muser-mode -mno-user-mode -mv8plus -mno-v8plus -mvis -mno-vis -mvis2 -mno-vis2 -mvis3 -mno-vis3 -mvis3b -mno-vis3b -mvis4 -mno-vis4 -mvis4b -mno-vis4b -mcbcond -mno-cbcond -mfmaf -mno-fmaf -mfsmuld -mno-fsmuld -mpopc -mno-popc -msubxc -mno-subxc -mfix-at697f -mfix-ut699 -mfix-ut700 -mfix-gr712rc ๐Ÿ“

๐ŸŽ€ System V Options ๐ŸŽ€

only a few here, but they're iconic ๐Ÿ‘‘ -Qy -Qn -YP,paths -Ym,dir ๐Ÿ’–

๐ŸŒˆ V850 Options ๐ŸŒˆ

the V850 lineup is serving so many variants ๐Ÿ˜ญ -mlong-calls -mno-long-calls -mep -mno-ep -mprolog-function -mno-prolog-function -mspace -mtda=n -msda=n -mzda=n -mapp-regs -mno-app-regs -mdisable-callt -mno-disable-callt -mv850e2v3 -mv850e2 -mv850e1 -mv850es -mv850e -mv850 -mv850e3v5 -mloop -mrelax -mlong-jumps -msoft-float -mhard-float -mgcc-abi -mrh850-abi -mbig-switch โญ

๐Ÿงš VAX Options ๐Ÿงš

VAX keeps it classic ๐Ÿ™ˆ -munix -mgnu -md -md-float -mg -mg-float -mlra ๐Ÿฆ‹

๐ŸŒธ Visium Options ๐ŸŒธ

Visium is lowkey underrated ๐Ÿ’œ -mdebug -msim -mfpu -mno-fpu -mhard-float -msoft-float -mcpu=cpu-type -mtune=cpu-type -msv-mode -muser-mode โœจ

๐Ÿ”ฅ VMS Options ๐Ÿ”ฅ

VMS has its own vibe, okay ๐Ÿ’€ -mvms-return-codes -mdebug-main=prefix -mmalloc64 -mpointer-size=size ๐Ÿซถ

๐ŸŒŠ VxWorks Options ๐ŸŒŠ

the VxWorks options are a whole mood ๐ŸŒธ -mrtp -msmp -non-static -Bstatic -Bdynamic -Xbind-lazy -Xbind-now ๐Ÿ’–

๐Ÿ‘‘ x86 Options ๐Ÿ‘‘

okay x86 is the main character of this whole page, bestie ๐Ÿ˜ญโœจ it has so many options it's actually unhinged in the best way. here they all are: -mtune=cpu-type -march=cpu-type -mtune-ctrl=feature-list -mdump-tune-features -mno-default -mfpmath=unit -masm=dialect -mno-fancy-math-387 -mno-fp-ret-in-387 -m80387 -mhard-float -msoft-float -mno-wide-multiply -mrtd -malign-double -mpreferred-stack-boundary=num -mincoming-stack-boundary=num -mcld -mcx16 -msahf -mmovbe -mcrc32 -mmwait -mrecip -mrecip=opt -mvzeroupper -mprefer-avx128 -mprefer-vector-width=opt -mpartial-vector-fp-math -mmove-max=bits -mstore-max=bits -mnoreturn-no-callee-saved-registers -mmmx -msse -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -msse4 -mavx -mavx2 -mavx512f -mavx512cd -mavx512vl -mavx512bw -mavx512dq -mavx512ifma -mavx512vbmi -msha -maes -mpclmul -mfsgsbase -mrdrnd -mf16c -mfma -mpconfig -mwbnoinvd -mptwrite -mclflushopt -mclwb -mxsavec -mxsaves -msse4a -m3dnow -m3dnowa -mpopcnt -mabm -mbmi -mtbm -mfma4 -mxop -madx -mlzcnt -mbmi2 -mfxsr -mxsave -mxsaveopt -mrtm -mhle -mlwp -mmwaitx -mclzero -mpku -mthreads -mgfni -mvaes -mwaitpkg -mshstk -mmanual-endbr -mcet-switch -mforce-indirect-call -mavx512vbmi2 -mavx512bf16 -menqcmd -mvpclmulqdq -mavx512bitalg -mmovdiri -mmovdir64b -mavx512vpopcntdq -mavx512vnni -mprfchw -mrdpid -mrdseed -msgx -mavx512vp2intersect -mserialize -mtsxldtrk -mamx-tile -mamx-int8 -mamx-bf16 -muintr -mhreset -mavxvnni -mamx-fp8 -mavx512fp16 -mavxifma -mavxvnniint8 -mavxneconvert -mcmpccxadd -mamx-fp16 -mprefetchi -mraoint -mamx-complex -mavxvnniint16 -msm3 -msha512 -msm4 -mapxf -musermsr -mavx10.1 -mavx10.1-256 -mavx10.1-512 -mevex512 -mavx10.2 -mamx-avx512 -mamx-tf32 -mamx-transpose -mmovrs -mamx-movrs -mcldemote -mms-bitfields -mno-align-stringops -minline-all-stringops -minline-stringops-dynamically -mstringop-strategy=alg -mkl -mwidekl -mmemcpy-strategy=strategy -mmemset-strategy=strategy -mpush-args -maccumulate-outgoing-args -m128bit-long-double -m96bit-long-double -mlong-double-64 -mlong-double-80 -mlong-double-128 -mregparm=num -msseregparm -mveclibabi=type -mvect8-ret-in-mem -mpc32 -mpc64 -mpc80 -mdaz-ftz -mstackrealign -momit-leaf-frame-pointer -mno-red-zone -mno-tls-direct-seg-refs -mcmodel=code-model -mabi=name -maddress-mode=mode -m32 -m64 -mx32 -m16 -miamcu -mlarge-data-threshold=num -msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv -minstrument-return=type -mfentry-name=name -mfentry-section=name -mavx256-split-unaligned-load -mavx256-split-unaligned-store -malign-data=type -mstack-protector-guard=guard -mstack-protector-guard-reg=reg -mstack-protector-guard-offset=offset -mstack-protector-guard-symbol=symbol -mgeneral-regs-only -mcall-ms2sysv-xlogues -mrelax-cmpxchg-loop -mindirect-branch=choice -mfunction-return=choice -mindirect-branch-register -mharden-sls=choice -mindirect-branch-cs-prefix -mneeded -mno-direct-extern-access -munroll-only-small-loops -mlam=choice ๐ŸŽ€

๐Ÿ“ x86 Windows Options ๐Ÿ“

for the x86 Windows stuff, just peek at Cygwin and MinGW Options, it's got you ๐Ÿ’–

โญ Xstormy16 Options โญ

just -msim for this one, short and sweet ๐Ÿงš

๐Ÿฆ‹ Xtensa Options ๐Ÿฆ‹

Xtensa has a glow-up list too โœจ -mconst16 -mno-const16 -mfused-madd -mno-fused-madd -mforce-no-pic -mserialize-volatile -mno-serialize-volatile -mtext-section-literals -mno-text-section-literals -mauto-litpools -mno-auto-litpools -mtarget-align -mno-target-align -mlongcalls -mno-longcalls -mabi=abi-type -mextra-l32r-costs=cycles -mstrict-align -mno-strict-align ๐Ÿ’œ

๐ŸŒท zSeries Options ๐ŸŒท

zSeries is the same as S/390 and zSeries Options, so check that one out ๐Ÿ’…


๐ŸŒˆ Options Controlling the Kind of Output ๐ŸŒˆ

okay so compiling is basically a four-step glow-up: preprocessing, compilation proper, assembly, and linking, always in that order ๐Ÿ’– GCC can preprocess and compile several files into several assembler input files, or squish them into one assembler input file. then each assembler input file makes an object file, and linking combines all the object files (the ones freshly compiled plus any you handed it) into an executable file โœจ

for any given input file, the file name suffix decides what kind of compilation happens, so the ending really matters bestie ๐Ÿซถ

  • file.c โ€” C source code that must be preprocessed.
  • file.i โ€” C source code that should not be preprocessed.
  • file.ii โ€” C++ source code that should not be preprocessed.
  • file.m โ€” Objective-C source code. heads up, you must link with the libobjc library to make an Objective-C program work ๐Ÿ”ฅ
  • file.mi โ€” Objective-C source code that should not be preprocessed.
  • file.mm, file.M โ€” Objective-C++ source code. again, you must link with the libobjc library to make an Objective-C++ program work. and .M refers to a literal capital M ๐Ÿ’€
  • file.mii โ€” Objective-C++ source code that should not be preprocessed.
  • file.h โ€” C, C++, Objective-C or Objective-C++ header file to be turned into a precompiled header (default), or C, C++ header file to be turned into an Ada spec (via the -fdump-ada-spec switch) ๐ŸŽ€
  • file.cc, file.cp, file.cxx, file.cpp, file.CPP, file.c++, file.C โ€” C++ source code that must be preprocessed. quick note: in .cxx, the last two letters must both be literally x, and .C refers to a literal capital C ๐Ÿงš
  • file.mm, file.M โ€” Objective-C++ source code that must be preprocessed.
  • file.mii โ€” Objective-C++ source code that should not be preprocessed.
  • file.hh, file.H, file.hp, file.hxx, file.hpp, file.HPP, file.h++, file.tcc โ€” C++ header file to be turned into a precompiled header or Ada spec. ๐Ÿฆ‹
  • file.f, file.for, file.ftn, file.fi โ€” Fixed form Fortran source code that should not be preprocessed.
  • file.F, file.FOR, file.fpp, file.FPP, file.FTN โ€” Fixed form Fortran source code that must be preprocessed (with the traditional preprocessor). ๐ŸŒธ
  • file.f90, file.f95, file.f03, file.f08, file.fii โ€” Free form Fortran source code that should not be preprocessed.
  • file.F90, file.F95, file.F03, file.F08 โ€” Free form Fortran source code that must be preprocessed (with the traditional preprocessor). ๐Ÿ’œ
  • file.cob, file.COB, file.cbl, file.CBL โ€” COBOL source code. โœจ
  • file.go โ€” Go source code. ๐Ÿซถ
  • file.d โ€” D source code.
  • file.di โ€” D interface file.
  • file.dd โ€” D documentation code (Ddoc). ๐Ÿ’–
  • file.ads โ€” Ada source code file that contains a library unit declaration (a declaration of a package, subprogram, or generic, or a generic instantiation), or a library unit renaming declaration (a package, generic, or subprogram renaming declaration). these are also called specs ๐Ÿ‘‘
  • file.adb โ€” Ada source code file containing a library unit body (a subprogram or package body). these are also called bodies. ๐ŸŒŠ
  • file.s โ€” Assembler code. ๐Ÿงš
  • file.S, file.sx โ€” Assembler code that must be preprocessed. ๐Ÿ’…
  • other โ€” an object file to be fed straight into linking. any file name with no recognized suffix gets treated this way, so it's giving "skip the line" energy ๐Ÿ™ˆ

You can pick the input language yourself with the -x option, bestie ๐Ÿ’…

  • -x language โ€” okay so this one lets you declare the language for the input files that come after it, instead of letting the compiler guess from the file name suffix ๐Ÿ”ฎ It stays active for every following input file until the next -x shows up. The valid values are:
c  c-header  cpp-output
c++  c++-header  c++-system-header c++-user-header c++-cpp-output
objective-c  objective-c-header  objective-c-cpp-output
objective-c++ objective-c++-header objective-c++-cpp-output
assembler  assembler-with-cpp
ada
cobol
d
f77  f77-cpp-input f95  f95-cpp-input
go

Note that -x does not imply a particular language standard, bestie ๐Ÿ™ˆ For example, -x f77 may also need -std=legacy for some older source codes. Don't skip it!

  • -x none โ€” the main character reset ๐Ÿงน This turns off any language specification, so the files after it get handled by their file name suffixes again, as if -x had never been used at all โœจ

If you only want some stages of compilation, you can use -x (or filename suffixes) to tell gcc where to start, and one of the options -c, -S, or -E to say where gcc should stop ๐Ÿฆ‹ Heads up: some combinations, like -x cpp-output -E, instruct gcc to do absolutely nothing at all ๐Ÿ’€

  • -c โ€” compile or assemble the source files, but do NOT link ๐ŸŒธ The linking stage simply doesn't happen. The final output is an object file for each source file.

By default, the object file name for a source file is made by swapping the suffix .c, .i, .s, etc. for .o.

Unrecognized input files that don't need compiling or assembling are ignored, so they just get left alone ๐Ÿซถ

  • -S โ€” stop right after the compilation proper and skip assembling ๐Ÿ’œ The output is an assembler code file for each non-assembler input file you gave it.

By default, the assembler file name for a source file is made by swapping the suffix .c, .i, etc. for .s.

Input files that don't need compilation are ignored.

  • -E โ€” stop after the preprocessing stage and don't run the compiler proper ๐Ÿงš The output is preprocessed source code, which gets sent to the standard output.

Input files that don't need preprocessing are ignored.

  • -o file โ€” place the primary output in file ๐ŸŽ€ This applies to whatever kind of output you're making, whether that's an executable, an object file, an assembler file, or preprocessed C code.

If you don't use -o, the default is to put an executable file in a.out, the object file for source.suffix in source.o, its assembler file in source.s, a precompiled header file in source.suffix.gch, and all preprocessed C source on standard output ๐Ÿ“

Okay, so -o only names the primary output, but it also affects how the auxiliary and dump outputs get named ๐Ÿ‘‘ Unless overridden, both auxiliary outputs and dump outputs get placed in the same directory as the primary output. For auxiliary outputs, the input file's suffix gets replaced with the suffix of the auxiliary output file type. For dump outputs, the dump file's suffix gets appended to the input file's suffix. In compilation commands, the base name of both auxiliary and dump outputs is the base name of the primary output. In compile and link commands, the primary output name, minus the executable suffix, gets combined with the input file name. If both share the same base name (ignoring the suffix), the result of the combination is that base name. Otherwise they get concatenated, separated by a dash ๐ŸŒˆ

Here's the vibe with -c:

gcc -c foo.c ...

This will use foo.o as the primary output, and place aux outputs and dumps next to it, e.g., aux file foo.dwo for -gsplit-dwarf, and dump file foo.c.???r.final for -fdump-rtl-final.

If a non-linker output file is explicitly specified, aux and dump files take the same base name by default:

gcc -c foo.c -o dir/foobar.o ...

This will name aux outputs dir/foobar.* and dump outputs dir/foobar.c.*.

A linker output will prefix aux and dump outputs instead ๐Ÿ˜ญ

gcc foo.c bar.c -o dir/foobar ...

This will generally name aux outputs dir/foobar-foo.* and dir/foobar-bar.*, and dump outputs dir/foobar-foo.c.* and dir/foobar-bar.c.*.

The one exception is when the executable shares the base name with the single input:

gcc foo.c -o dir/foo ...

In that case, aux outputs are named dir/foo.* and dump outputs are named dir/foo.c.* โœจ

The location and names of auxiliary and dump outputs can be adjusted with the options -dumpbase, -dumpbase-ext, -dumpdir, -save-temps=cwd, and -save-temps=obj ๐Ÿฆ‹

  • -dumpbase dumpbase โ€” this sets the base name for auxiliary and dump output files ๐Ÿซถ It does NOT affect the name of the primary output file. Intermediate outputs, when preserved, aren't treated as primary outputs. They're treated as auxiliary outputs instead.

Like, this one:

gcc -save-temps -S foo.c

saves the (no longer) temporary preprocessed file in foo.i, and then compiles to the (implied) output file foo.s. Whereas this one:

gcc -save-temps -dumpbase save-foo -c foo.c

preprocesses to save-foo.i, compiles to save-foo.s (now an intermediate, and therefore an auxiliary output), and then assembles to the (implied) output file foo.o ๐Ÿ’…

Without this option, dump and aux files take their names from the input file, or from the (non-linker) output file if one is explicitly specified. Dump output files (for example, those requested by -fdump-* options) keep the input name suffix. Aux output files (those requested by other non-dump options, e.g. -save-temps, -gsplit-dwarf, -fcallgraph-info) drop it.

Similar suffix differentiation of dump and aux outputs can be achieved for an explicitly given -dumpbase basename.suf by also specifying -dumpbase-ext .suf.

If dumpbase is explicitly specified with any directory component, any dumppfx specification (e.g. -dumpdir or -save-temps=*) is ignored. Instead of appending to it, dumpbase fully overrides it:

gcc foo.c -c -o dir/foo.o -dumpbase alt/foo \
  -dumpdir pfx- -save-temps=cwd ...

This creates auxiliary and dump outputs named alt/foo.*, disregarding dir/ in -o, the ./ prefix implied by -save-temps=cwd, and pfx- in -dumpdir ๐ŸŒธ

When -dumpbase is specified in a command that compiles multiple inputs, or that compiles and then links, it may be combined with dumppfx, as described under -dumpdir. Then each input file is compiled using the combined dumppfx, and default values for dumpbase and auxdropsuf are computed for each input file:

gcc foo.c bar.c -c -dumpbase main ...

This creates foo.o and bar.o as primary outputs. It avoids overwriting the auxiliary and dump outputs by using the dumpbase as a prefix, creating auxiliary and dump outputs named main-foo.* and main-bar.* ๐Ÿงš

An empty string given as dumpbase avoids the influence of the output basename on the naming of auxiliary and dump outputs during compilation, and computes default values instead:

gcc -c foo.c -o dir/foobar.o -dumpbase " ...

This will name aux outputs dir/foo.* and dump outputs dir/foo.c.*. Notice how their basenames come from the input name, but the directory still defaults to that of the output ๐Ÿ’–

The empty-string dumpbase does not stop the output basename from being used for outputs during linking:

gcc foo.c bar.c -o dir/foobar -dumpbase " -flto ...

The compilation of the source files will name auxiliary outputs dir/foo.* and dir/bar.*, and dump outputs dir/foo.c.* and dir/bar.c.*. LTO recompilation during linking will use dir/foobar. as the prefix for dumps and auxiliary files โœจ

  • -dumpbase-ext auxdropsuf โ€” when forming the name of an auxiliary (but NOT a dump) output file, this drops the trailing auxdropsuf from dumpbase before any suffixes get appended ๐ŸŽ€ If you don't specify it, this option defaults to the suffix of a default dumpbase. That's the suffix of the input file when -dumpbase isn't on the command line, or when dumpbase is combined with dumppfx.
gcc foo.c -c -o dir/foo.o -dumpbase x-foo.c -dumpbase-ext .c ...

This creates dir/foo.o as the main output, and generates auxiliary outputs in dir/x-foo.*. It takes the location of the primary output and drops the .c suffix from the dumpbase. Dump outputs keep the suffix: dir/x-foo.c.* ๐Ÿฆ‹

This option is disregarded if it doesn't match the suffix of a specified dumpbase, except as an alternative to the executable suffix when appending the linker output base name to dumppfx, as described below:

gcc foo.c bar.c -o main.out -dumpbase-ext .out ...

This creates main.out as the primary output, and avoids overwriting the auxiliary and dump outputs by using the executable name minus auxdropsuf as a prefix. It creates auxiliary outputs named main-foo.* and main-bar.*, and dump outputs named main-foo.c.* and main-bar.c.* ๐Ÿ’œ

  • -dumpdir dumppfx โ€” when forming the name of an auxiliary or dump output file, use dumppfx as a prefix ๐ŸŒˆ
gcc -dumpdir pfx- -c foo.c ...

This creates foo.o as the primary output, and auxiliary outputs named pfx-foo.*. It combines the given dumppfx with the default dumpbase, which is derived from the default primary output, which is in turn derived from the input name. Dump outputs also take the input name suffix: pfx-foo.c.* ๐Ÿ“

If dumppfx is going to be used as a directory name, it must end with a directory separator:

gcc -dumpdir dir/ -c foo.c -o obj/bar.o ...

This creates obj/bar.o as the primary output, and auxiliary outputs named dir/bar.*, combining the given dumppfx with the default dumpbase derived from the primary output name. Dump outputs also take the input name suffix: dir/bar.c.* ๐Ÿ”ฅ

It defaults to the location of the output file, unless the output file is a special file like "/dev/null". The options -save-temps=cwd and -save-temps=obj override this default, just like an explicit -dumpdir option does. If multiple such options are given, the last one prevails:

gcc -dumpdir pfx- -c foo.c -save-temps=obj ...

So it still spits out foo.o, but the auxiliary outputs get named foo.* ๐Ÿ™ˆ because -save-temps=* overrides the dumppfx you gave with the earlier -dumpdir option. ๐Ÿ’… It's not a big deal that =obj is the default for -save-temps, or that the output directory is implicitly the current directory. Dump outputs are named foo.c.*.

When you compile from multiple input files and pass -dumpbase, the dumpbase minus an auxdropsuf suffix, plus a dash, gets tacked onto (or overrides, if it has any directory components) an explicit or defaulted dumppfx. That way each of the compilations gets its own differently-named aux and dump outputs โœจ

gcc foo.c bar.c -c -dumpdir dir/pfx- -dumpbase main ...

This outputs auxiliary dumps to dir/pfx-main-foo.* and dir/pfx-main-bar.*, appending dumpbase- to dumppfx. Dump outputs keep the input file suffix: dir/pfx-main-foo.c.* and dir/pfx-main-bar.c.*, respectively. Now compare that with the single-input version:

gcc foo.c -c -dumpdir dir/pfx- -dumpbase main ...

Applying -dumpbase to a single source does not compute and append a separate dumpbase per input file, so its auxiliary and dump outputs just go in dir/pfx-main.* ๐ŸŽ€

When you compile and then link from multiple input files, a defaulted or explicitly specified dumppfx also gets the dumpbase- treatment from above (think the foo.c and bar.c compilation, but without -c). If neither -dumpdir nor -dumpbase is given, the linker output base name, minus auxdropsuf if specified (or the executable suffix otherwise), plus a dash gets appended to the default dumppfx instead. ๐Ÿฆ‹ Heads up though, this is different from the earlier cases of linking:

gcc foo.c bar.c -dumpdir dir/pfx- -o main ...

This does not append the output name main to dumppfx, because -dumpdir was explicitly specified. The vibe here is that an explicitly-specified dumppfx can include the output name as part of the prefix, if you want that. Only an explicitly-specified -dumpbase gets combined with it, so a meaningful option never just gets thrown away ๐Ÿซถ

When you compile and then link from a single input file, the linker output base name only gets appended to the default dumppfx (like above) if it doesn't share the base name with the single input file name. This was covered in the single-input linking cases above, but not with an explicit -dumpdir that stops the combination, even if -save-temps=* overrides it:

gcc foo.c -dumpdir alt/pfx- -o dir/main.exe -save-temps=cwd ...

Auxiliary outputs are named foo.*, and dump outputs foo.c.*, in the current working directory, as ultimately requested by -save-temps=cwd ๐ŸŒธ

Okay, so here's the big-picture data flow. It's a little imprecise, but it's intuitive ๐Ÿงš The primary output name gets split into a directory part and a basename part. dumppfx is set to the directory part, unless -dumpdir or -save-temps=* overrides it, and dumpbase is set to the basename part, unless -dumpbase overrides it. With multiple inputs or linking, this dumpbase may get combined with dumppfx and taken from each input file. Auxiliary output names for each input are formed by combining dumppfx, dumpbase minus its suffix, and the auxiliary output suffix. Dump output names only differ in that the suffix from dumpbase is kept ๐Ÿ’œ

When it comes to auxiliary and dump outputs created during LTO recompilation ๐ŸŒˆ, a combination of dumppfx and dumpbase gets passed down to lto-wrapper as a -dumpdir option. This happens whether it's given explicitly or derived from the linker output name (but not from inputs), and even in cases where this combination wouldn't otherwise get used as such. The trailing period replaces the compiler-added dash, if there is one. Since linking is involved, this program doesn't normally get any -dumpbase and -dumpbase-ext, and it ignores them ๐Ÿ™ˆ

When lto-wrapper runs sub-compilers, it appends the LTO stage names to the dumppfx it received, makes sure it contains a directory component so it overrides any -dumpdir, and passes that along as -dumpbase to the sub-compilers โœจ


โœจ OPTIONS โœจ

  • -v โ€” Prints the commands the compiler runs for each stage of compilation (on standard error output). It also prints the version number of the compiler driver program, plus the preprocessor and the compiler proper ๐Ÿ“ฃ
  • -### โ€” Basically -v, except the commands are not actually executed. Arguments get quoted unless they only contain alphanumeric characters or "./-_". This is super handy for shell scripts that want to capture the driver-generated command lines ๐Ÿค“
  • --help โ€” Prints (on the standard output) a description of the command-line options gcc understands. If -v is also specified, then --help gets passed on to the various processes gcc invokes, so they can show the command-line options they accept. If -Wextra has also been specified (before the --help option), then command-line options with no documentation attached are displayed too ๐Ÿ“š
  • --target-help โ€” Prints (on the standard output) a description of the target-specific command-line options for each tool. For some targets, extra target-specific info may also get printed ๐ŸŽฏ
  • --help={class|[^]qualifier}[,...] โ€” Prints (on the standard output) a description of the command-line options the compiler understands that fit into all of the specified classes and qualifiers. Here are the supported classes:
    • optimizers โ€” Displays all of the optimization options the compiler supports.
    • warnings โ€” Displays all of the options that control warning messages the compiler produces.
    • target โ€” Displays target-specific options. Unlike --target-help, target-specific options of the linker and assembler are not displayed, because those tools don't currently support the extended --help= syntax ๐Ÿ™ƒ
    • params โ€” Displays the values recognized by the --param option.
    • language โ€” Displays the options supported for language, where language is the name of one of the languages supported in this version of GCC. If an option is supported by all languages, you need to select the common class.
    • common โ€” Displays the options that are common to all languages.

And here are the supported qualifiers:

  • undocumented โ€” Displays only the options that are undocumented.
  • joined โ€” Displays options that take an argument appearing after an equal sign in the same continuous piece of text, such as: --help=target.
  • separate โ€” Displays options that take an argument appearing as a separate word following the original option, such as: -o output-file.

Want all the undocumented target-specific switches the compiler supports? Use:

--help=target,undocumented

You can invert a qualifier by putting the ^ character in front of it. So if you want every binary warning option (the ones that are either on or off and don't take an argument) that has a description, use:

--help=warnings,^joined,^undocumented
The argument to --help= should not consist solely of inverted qualifiers.

Combining several classes is possible, but it usually narrows the output so much that there's nothing to show. One case where it does work is when one of the classes is target. For example, to display all the target-specific optimization options, use:

--help=target,optimizers

The --help= option can be repeated on the command line. Each use displays its requested class of options, skipping any that were already displayed. If --help is also specified anywhere on the command line, it takes precedence over any --help= option ๐Ÿ‘‘

If the -Q option appears on the command line before --help=, the descriptive text that --help= shows changes. Instead of describing the options, it indicates whether each option is enabled, disabled, or set to a specific value (assuming the compiler knows this at the point where --help= is used) ๐Ÿ’…

Here's a truncated example from the ARM port of GCC:

% gcc -Q -mabi=2 --help=target -c
The following options are target specific:
-mabi=                                2
-mabort-on-noreturn                   [disabled]
-mapcs                                [disabled]

The output is sensitive to the effects of previous command-line options, so for example you can find out which optimizations are enabled at -O2 by using:

-Q -O2 --help=optimizers

Or, if you want to see which binary optimizations are enabled by -O3, do this:

gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
diff /tmp/O2-opts /tmp/O3-opts | grep enabled
  • --version โ€” Displays the version number and copyrights of the invoked GCC ๐ŸŽ€
  • -pass-exit-codes โ€” Normally, the gcc program exits with the code of 1 if any phase of the compiler returns a non-success return code. If you specify -pass-exit-codes, then gcc instead returns with the numerically highest error produced by any phase that returned an error indication. The C, C++, and Fortran front ends return 4 if they hit an internal compiler error ๐Ÿ˜ญ
  • -pipe โ€” Uses pipes rather than temporary files for communication between the various stages of compilation. This fails to work on some systems where the assembler can't read from a pipe, but the GNU assembler has no trouble with it ๐Ÿฆ‹
  • -specs=file โ€” Processes file after the compiler reads in the standard specs file, in order to override the defaults that the gcc driver program uses when deciding which switches to pass to cc1, cc1plus, as, ld, etc. You can specify more than one -specs=file on the command line, and they're processed in order, from left to right ๐Ÿง
  • -wrapper โ€” Invokes all subcommands under a wrapper program. The name of the wrapper program and its parameters are passed as a comma-separated list:
gcc -c t.c -wrapper gdb,--args

This invokes all subprograms of gcc under gdb --args, so the invocation of cc1 becomes gdb --args cc1 .... ๐ŸŒˆ

โœจ -ffile-prefix-map=old=new ๐Ÿ—บ๏ธ๐Ÿ’–

When you're compiling files that live in the directory old, GCC will record any references to them in the compilation result as if those files lived in new instead. Writing this option is the same as writing out every individual -f*-prefix-map option, so it's basically the one-stop shop ๐Ÿ›๏ธ. It's a lowkey perfect tool for reproducible builds that don't care where they're built ๐ŸŒธ. Directories referenced by directives are not affected by these options. See also -fmacro-prefix-map, -fdebug-prefix-map, -fprofile-prefix-map and -fcanon-prefix-map ๐Ÿฆ‹.

  • -fcanon-prefix-map โ€” okay so, when you use the -f*-prefix-map options, GCC normally compares the old prefix against the filename it would reference by doing a plain textual comparison of the prefixes. On case-insensitive filesystems it ignores character case, and on DOS-based filesystems it treats slashes and backslashes as the same thing ๐Ÿ’…. -fcanon-prefix-map switches this so the comparisons happen on canonicalized paths for both old and the referenced filename. Main character energy ๐Ÿ‘‘.
  • -fplugin=name.so โ€” loads the plugin code from the file name.so, which is assumed to be a shared object that the compiler will dlopen. The base name of the shared object file is what identifies the plugin when parsing arguments (see -fplugin-arg-name-key=value below) ๐Ÿงš. Each plugin should define the callback functions specified in the Plugins API โœจ.
  • -fplugin-arg-name-key=value โ€” defines an argument called key with a value of value for the plugin called name ๐ŸŽ€.
  • -fdump-ada-spec[-slim] โ€” for C and C++ source and include files, this generates the matching Ada specs ๐Ÿ’œ.
  • -fada-spec-parent=unit โ€” used together with -fdump-ada-spec[-slim] above, this makes the generated Ada specs child units of the parent unit ๐ŸŒˆ.
  • -fdump-go-spec=file โ€” for input files in any language, this generates matching Go declarations in file. You get Go const, type, var, and func declarations, which is a really useful way to start writing a Go interface to code written in some other language ๐Ÿน๐Ÿซถ.
  • @file โ€” reads command-line options from file. The options it reads get slotted in right where the original @file option was ๐Ÿ“. If file doesn't exist or can't be read, then the option gets treated literally and isn't removed ๐Ÿ’€.

Options inside file are separated by whitespace. If you need a whitespace character inside an option, wrap the entire option in single or double quotes. Any character (including a backslash) can be included by putting a backslash in front of it ๐Ÿ™ˆ. The file can also contain more @file options of its own, and those get processed recursively, so yes, it can go deep ๐ŸŒ€.


โœจ Compiling C++ Programs โœจ

C++ source files conventionally use one of the suffixes .C, .cc, .cpp, .CPP, .c++, .cp, or .cxx ๐Ÿ’–. C++ header files often use .hh, .hpp, .H, or (for shared template code) .tcc ๐Ÿฆ‹. Preprocessed C++ files use the suffix .ii โœจ, and C++20 module interface units sometimes use .ixx, .cppm, .cxxm, .c++m, or .ccm ๐Ÿซถ.

GCC recognizes files with these names and compiles them as C++ programs, even if you call the compiler the same way you do for C programs (usually with the name gcc) ๐Ÿ’….

Heads up though: using gcc does not add the C++ library ๐Ÿ˜ญ. g++ is a program that calls GCC and automatically specifies linking against the C++ library, so it's the one you want for C++ vibes ๐Ÿ‘‘. It treats .c, .h and .i files as C++ source files instead of C source files, unless you use -x ๐Ÿ™ˆ. This program is also useful when precompiling a C header file with a .h extension for use in C++ compilations ๐Ÿงš. On many systems, g++ is also installed with the name c++ ๐ŸŒธ.

When you compile C++ programs, you can use many of the same command-line options you'd use for programs in any language ๐Ÿ“. You can also use command-line options meaningful for C and related languages, or options that only mean something for C++ programs ๐Ÿ’œ.


โœจ Options Controlling C Dialect โœจ

The following options control the dialect of C (or languages derived from C, like C++, Objective-C and Objective-C++) that the compiler accepts ๐ŸŒˆ.

  • -ansi โ€” in C mode, this is equivalent to -std=c90. In C++ mode, it's equivalent to -std=c++98 ๐ŸŽ€.
  • -std= โ€” determines the language standard ๐Ÿ”ฅ. This option is currently only supported when compiling C or C++ ๐Ÿ™ˆ.

The compiler can accept several base standards, like c90 or c++98, and GNU dialects of those standards, like gnu90 or gnu++98 ๐Ÿ’–. When you specify a base standard, the compiler accepts all programs following that standard, plus those using GNU extensions that don't contradict it. For example, -std=c90 turns off certain GCC features that are incompatible with ISO C90, like the "asm" and "typeof" keywords ๐Ÿ™ƒ. It doesn't turn off other GNU extensions that have no meaning in ISO C90, like omitting the middle term of a "?:" expression ๐Ÿฆ‹.

On the flip side, when you specify a GNU dialect of a standard, every feature the compiler supports gets enabled, even when those features change the meaning of the base standard ๐Ÿ˜ญ. Because of that, some strictly conforming programs may get rejected ๐Ÿ’€. The particular standard is also what -Wpedantic uses to figure out which features are GNU extensions for that version of the standard. For example, -std=gnu90 -Wpedantic warns about C++ style // comments, while -std=gnu99 -Wpedantic does not ๐Ÿง.

You must provide a value for this option. The possible values are:

  • c90, c89, iso9899:1990 โ€” supports all ISO C90 programs (certain GNU extensions that conflict with ISO C90 are disabled). Same as -ansi for C code ๐ŸŒธ.
  • iso9899:199409 โ€” ISO C90 as modified in amendment 1 ๐Ÿ“œ.
  • c99, c9x, iso9899:1999, iso9899:199x โ€” ISO C99 ๐ŸŒŸ. This standard is substantially completely supported, modulo bugs and floating-point issues (mainly, but not entirely, relating to optional C99 features from Annexes F and G). Check out <https://gcc.gnu.org/c99status.html> for more info ๐Ÿ‘€. The names c9x and iso9899:199x are deprecated ๐Ÿšซ.
  • c11, c1x, iso9899:2011 โ€” ISO C11, the 2011 revision of the ISO C standard โœจ. This one is substantially completely supported, modulo bugs, floating-point issues (mainly, but not entirely, relating to optional C11 features from Annexes F and G), and the optional Annexes K (Bounds-checking interfaces) and L (Analyzability) ๐ŸŽ€. The name c1x is deprecated ๐Ÿšซ.
  • c17, c18, iso9899:2017, iso9899:2018 โ€” ISO C17, the 2017 revision of the ISO C standard (published in 2018) ๐ŸŒˆ. This is the same as C11 except for corrections of defects (all of which are also applied with -std=c11) and a new value of "__STDC_VERSION__", so it's supported to the same extent as C11 ๐Ÿ’….
  • c23, c2x, iso9899:2024 โ€” ISO C23, the 2023 revision of the ISO C standard (published in 2024) ๐Ÿ”ฅ. The name c2x is deprecated ๐Ÿšซ.
  • c2y โ€” the next version of the ISO C standard, still under development ๐Ÿงช. Support for this version is experimental and incomplete ๐Ÿ™ˆ.
  • gnu90, gnu89 โ€” GNU dialect of ISO C90 (including some C99 features) ๐Ÿฆ‹.
  • gnu99, gnu9x โ€” GNU dialect of ISO C99 ๐Ÿ’œ. The name gnu9x is deprecated ๐Ÿšซ.
  • gnu11, gnu1x โ€” GNU dialect of ISO C11 ๐ŸŒธ. The name gnu1x is deprecated ๐Ÿšซ.
  • gnu17, gnu18 โ€” GNU dialect of ISO C17 ๐ŸŽ€.
  • gnu23, gnu2x โ€” GNU dialect of ISO C23. This is the default for C code ๐Ÿ‘‘. The name gnu2x is deprecated ๐Ÿšซ.
  • gnu2y โ€” the next version of the ISO C standard, still under development, plus GNU extensions ๐Ÿงช. Support for this version is experimental and incomplete. The name gnu2x is deprecated ๐Ÿšซ.
  • c++98, c++03 โ€” the 1998 ISO C++ standard plus the 2003 technical corrigendum and some additional defect reports ๐Ÿ“œ. Same as -ansi for C++ code ๐Ÿ“.
  • gnu++98, gnu++03 โ€” GNU dialect of -std=c++98 โœจ.
  • c++11, c++0x โ€” the 2011 ISO C++ standard plus amendments ๐ŸŒˆ. The name c++0x is deprecated ๐Ÿšซ.
  • gnu++11, gnu++0x โ€” GNU dialect of -std=c++11 ๐Ÿ’–. The name gnu++0x is deprecated ๐Ÿšซ.
  • c++14, c++1y โ€” the 2014 ISO C++ standard plus amendments ๐Ÿฆ‹. The name c++1y is deprecated ๐Ÿšซ.
  • gnu++14, gnu++1y โ€” GNU dialect of -std=c++14 ๐Ÿซถ. The name gnu++1y is deprecated ๐Ÿšซ.
  • c++17, c++1z โ€” the 2017 ISO C++ standard plus amendments ๐ŸŽ€. The name c++1z is deprecated ๐Ÿšซ.
  • gnu++17, gnu++1z โ€” GNU dialect of -std=c++17. This is the default for C++ code ๐Ÿ‘‘. The name gnu++1z is deprecated ๐Ÿšซ.
  • c++20, c++2a โ€” the 2020 ISO C++ standard plus amendments ๐Ÿงช. Support is experimental and could change in incompatible ways in future releases ๐Ÿ˜ฌ. The name c++2a is deprecated ๐Ÿšซ.
  • gnu++20, gnu++2a โ€” GNU dialect of -std=c++20 ๐Ÿงช. Support is experimental and could change in incompatible ways in future releases ๐Ÿ˜ฌ. The name gnu++2a is deprecated ๐Ÿšซ.
  • c++23, c++2b โ€” the 2023 ISO C++ standard plus amendments (published in 2024) ๐ŸŒŸ. Support is experimental and could change in incompatible ways in future releases ๐Ÿ˜ฌ. The name c++2b is deprecated ๐Ÿšซ.
  • gnu++23, gnu++2b โ€” GNU dialect of -std=c++23 ๐Ÿงช. Support is experimental and could change in incompatible ways in future releases ๐Ÿ˜ฌ. The name gnu++2b is deprecated ๐Ÿšซ.
  • c++2c, c++26 โ€” the next revision of the ISO C++ standard, planned for 2026 ๐Ÿ”ฎ. Support is highly experimental, and will almost certainly change in incompatible ways in future releases ๐Ÿ’€.
  • gnu++2c, gnu++26 โ€” GNU dialect of -std=c++2c ๐Ÿ”ฎ. Support is highly experimental, and will almost certainly change in incompatible ways in future releases ๐Ÿ’€.
  • -aux-info filename โ€” outputs prototyped declarations to the given filename for all functions declared and/or defined in a translation unit, including the ones in header files ๐Ÿ“. This option is silently ignored in any language other than C ๐Ÿคซ.

Okay so bestie, the aux-info file is lowkey iconic ๐Ÿ’… Besides the declarations, it leaves little comments showing where each one came from (source file and line) ๐Ÿ“. It also tags each declaration as I (implicit), N (new, prototyped) or O (old, unprototyped), right after the line number and colon โœจ. The next character says whether it came from a declaration (C) or a definition (F) ๐ŸŽ€. And for function definitions, it also drops a K&R-style list of arguments and their declarations in comments right after the declaration ๐Ÿฆ‹


  • -fno-asm โ€” Stops asm, inline and typeof from being treated as keywords, so you can use them as regular identifiers ๐Ÿ™ˆ

Your code can use __asm__, __inline__ and __typeof__ instead, which is giving full keyword energy โœจ In C, -ansi already implies -fno-asm, so no need to add it twice ๐Ÿ’…

In C++, inline is a standard keyword, so this switch doesn't touch it at all ๐Ÿ™…โ€โ™€๏ธ If you only want typeof gone and want to keep asm and inline, use -fno-gnu-keywords instead ๐ŸŽ€ In C99 mode (-std=c99 or -std=gnu99), this switch only affects asm and typeof, because inline is already a standard keyword in ISO C99 ๐ŸŒธ In C23 mode (-std=c23 or -std=gnu23), it only affects asm, since typeof is standard in ISO C23 ๐Ÿงš

  • -fno-builtin, -fno-builtin-function โ€” Don't recognize built-in functions whose names don't start with __builtin_ ๐Ÿ™…โ€โ™€๏ธ

GCC usually generates special code for certain built-in functions so they run faster ๐ŸŽ๏ธ For example, calls to alloca might turn into a single instruction that adjusts the stack directly, and calls to memcpy might become inline copy loops ๐Ÿ” The result is often smaller and faster, but the calls don't show up as calls anymore. That means you can't set a breakpoint on them, and you can't change what they do by linking against a different library ๐Ÿ˜ญ Also, once GCC recognizes a built-in function, it can use what it knows about that function to warn you about bad calls, or to generate more efficient code, even when the code still contains calls to it ๐Ÿ‘€ For example, warnings are given with -Wformat for bad calls to printf when printf is built in and strlen is known not to modify global memory ๐Ÿ’…

With the -fno-builtin-function option, only that one built-in function gets disabled ๐ŸŽฏ function must not begin with __builtin_. If you name a function that isn't built-in in this version of GCC, the option is simply ignored ๐Ÿคทโ€โ™€๏ธ There's no matching -fbuiltin-function option. If you want to turn built-in functions back on selectively while using -fno-builtin or -ffreestanding, you can define macros like these ๐Ÿฆ‹

        #define abs(n)          __builtin_abs ((n))
        #define strcpy(d, s)    __builtin_strcpy ((d), (s))
  • -fcond-mismatch โ€” Lets conditional expressions have mismatched types in their second and third arguments ๐ŸŒˆ The value of that kind of expression is void. This option is not supported for C++ ๐Ÿ™…โ€โ™€๏ธ
  • -ffreestanding โ€” Tells GCC that compilation targets a freestanding environment โœจ This implies -fno-builtin. A freestanding environment is one where the standard library might not exist, and program startup might not necessarily start at main ๐Ÿงš The most obvious example is an OS kernel ๐Ÿ‘‘ This is equivalent to -fno-hosted.
  • -fgimple โ€” Turns on parsing of function definitions marked with __GIMPLE. It's an experimental feature that lets you unit test GIMPLE passes ๐Ÿงช
  • -fgnu-tm โ€” When you specify -fgnu-tm, the compiler generates code for the Linux variant of Intel's current Transactional Memory ABI specification document (Revision 1.1, May 6 2009) ๐Ÿ’œ This is experimental, and its interface may change in future versions of GCC as the official specification changes ๐Ÿ˜ฌ Not every architecture supports this feature, so heads up ๐Ÿซถ

For more information on GCC's support for transactional memory,

Transactional memory is not supported together with non-call exceptions (-fnon-call-exceptions). Don't mix them, bestie ๐Ÿ’€
  • -fgnu89-inline โ€” Tells GCC to use the traditional GNU semantics for inline functions when in C99 mode ๐ŸŒธ

Using this option is roughly the same as adding the gnu_inline function attribute to every inline function ๐ŸŽ€

The option -fno-gnu89-inline explicitly tells GCC to use the C99 semantics for inline when in C99 or gnu99 mode. In other words, it sets the default behavior ๐Ÿ’… This option is not supported in -std=c90 or -std=gnu90 mode ๐Ÿ™ˆ

The preprocessor macros __GNUC_GNU_INLINE__ and __GNUC_STDC_INLINE__ can be used to check which semantics are in effect for inline functions ๐Ÿ”

  • -fhosted โ€” Tells GCC that compilation targets a hosted environment โœจ This implies -fbuiltin. A hosted environment is one where the entire standard library is available and main has a return type of int ๐Ÿ‘‘ Basically everything except a kernel fits this description ๐Ÿงš This is equivalent to -fno-freestanding.
  • -flax-vector-conversions โ€” Allows implicit conversions between vectors with different numbers of elements and/or incompatible element types ๐Ÿ˜ฌ Please don't use this in new code, it's giving "old and messy" ๐Ÿ’€
  • -fms-extensions โ€” Accepts some non-standard constructs found in Microsoft header files ๐ŸชŸ

In C++ code, this lets member names in structures match earlier type declarations, which is so chaotic but also kind of fun ๐Ÿ™ˆ

        typedef int UOW;
        struct ABC {
          UOW UOW;
        };

Some cases of unnamed fields in structures and unions are only accepted with this option ๐Ÿซฃ

This option is off for all targets except x86 targets using ms-abi, so don't expect it to just work everywhere ๐Ÿ™…โ€โ™€๏ธ
  • -fpermitted-flt-eval-methods=style โ€” Controls which values of FLT_EVAL_METHOD the compiler is allowed to use ๐Ÿฆ‹

ISO/IEC TS 18661-3 defines new permissible values for FLT_EVAL_METHOD. These say that operations and constants whose semantic type is an interchange or extended format should be evaluated to the precision and range of that type ๐Ÿค“ The new values are a superset of what C99/C11 allows, and C99/C11 doesn't specify what other positive values of FLT_EVAL_METHOD mean. So code that's C11-conformant might never have expected these new values to show up ๐Ÿ˜ญ

-fpermitted-flt-eval-methods decides whether the compiler only allows the FLT_EVAL_METHOD values from C99/C11, or the extended set from ISO/IEC TS 18661-3 โœจ

style is either c11 or ts-18661-3, depending on what you need ๐Ÿ’–

In a standards-compliant mode (-std=c11 or similar), the default is -fpermitted-flt-eval-methods=c11. In a GNU dialect (-std=gnu11 or similar), the default is -fpermitted-flt-eval-methods=ts-18661-3 ๐ŸŒˆ

The -fdeps-* options extract structured dependency information for a source file ๐Ÿง  This means figuring out which resources from other source files the source needs in order to compile, and which resources the source itself provides. That info can add required dependencies between compilation rules of dependent sources based on their actual contents, so the build tools don't have to reflect that information themselves ๐Ÿซถ

  • -fdeps-file=file โ€” Where to write the structured dependency information ๐Ÿ“
  • -fdeps-format=format โ€” The format to use for structured dependency information. p1689r5 is the only supported format right now ๐Ÿ’… When this argument is specified, the output of -MF gets stripped of some information (namely C++ modules), so it doesn't use extended makefile syntax that most tools won't understand ๐Ÿ™ˆ
  • -fdeps-target=file โ€” This is the -MT equivalent for structured dependency information ๐ŸŽฏ It names the target that will ultimately need any required resources, and that will provide any resources extracted from the source that other sources might need ๐Ÿงš
  • -fplan9-extensions โ€” Accepts some non-standard constructs used in Plan 9 code ๐Ÿน

This turns on -fms-extensions, lets you pass pointers to structures with anonymous fields to functions that expect pointers to elements of the field's type, and lets you refer to anonymous fields declared using a typedef ๐ŸŒธ This is only supported for C, not C++ ๐Ÿ™…โ€โ™€๏ธ

  • -fsigned-bitfields, -funsigned-bitfields, -fno-signed-bitfields, -fno-unsigned-bitfields โ€” Control whether a bit-field is signed or unsigned when the declaration doesn't use either signed or unsigned ๐Ÿ”ข By default, such a bit-field is signed, which is consistent, since basic integer types like int are signed too ๐Ÿ’–
  • -fsigned-char โ€” Makes the type char signed, just like signed char โœจ

This is equivalent to -fno-unsigned-char, which is the negative form of -funsigned-char. Likewise, -fno-signed-char is equivalent to -funsigned-char ๐Ÿ™ƒ

  • -funsigned-char โ€” Makes the type char unsigned, just like unsigned char ๐ŸŒˆ

Every kind of machine has a default for what char should be. It's either like unsigned char by default, or like signed char by default ๐Ÿ–ฅ๏ธ

Ideally, a portable program always uses signed char or unsigned char when it depends on the signedness of an object ๐Ÿงผ But lots of programs were written to use plain char and expect it to be signed, or expect it to be unsigned, depending on the machines they were written for ๐Ÿ˜ฉ This option, and its inverse, let you make those programs work with the opposite default ๐Ÿซถ

The type char is always a distinct type from both signed char and unsigned char, even though it behaves exactly like one of those two ๐Ÿค“

  • -fstrict-flex-arrays (C and C++ only), -fstrict-flex-arrays=level (C and C++ only) โ€” Controls when the trailing array of a structure gets treated as a flexible array member when you're accessing its elements ๐Ÿฆ‹ The value of level sets how strict that is ๐Ÿ’…

-fstrict-flex-arrays is equivalent to -fstrict-flex-arrays=3, which is the strictest option. A trailing array only counts as a flexible array member when it's declared as one, per the C99 standard onwards ๐Ÿ“

The negative form, -fno-strict-flex-arrays, is equivalent to -fstrict-flex-arrays=0, which is the least strict. In that case, every trailing array of a structure is treated as a flexible array member ๐ŸŒท

okay so there are two more levels hiding between 0 and 3 โœจ they exist for older code that uses the GCC zero-length array extension ([0]) or one-element arrays as flexible array members ([1]) ๐Ÿงš when level is 1, the trailing array gets treated as a flexible array member when it's declared as [], [0], or [1]. when level is 2, it only counts when it's declared as [] or [0] ๐Ÿ’…

you can control this for one specific trailing array field in a struct by using the variable attribute "strict_flex_array" attribute ๐ŸŽ€

and yes, the -fstrict_flex_arrays option interacts with the -Wstrict-flex-arrays option ๐Ÿซถ

  • -fsso-struct=endianness โ€” okay this one sets the default scalar storage order of structures and unions to whatever endianness you pick ๐ŸŒˆ the accepted values are big-endian, little-endian, and native (the native endianness of the target, which is the default). this option is not supported for C++ ๐Ÿ™ˆ
โš ๏ธ warning, bestie: -fsso-struct makes GCC generate code that is not binary compatible with code generated without it if the endianness you specify isn't the native endianness of the target ๐Ÿ˜ญ handle with care

โœจ Options Controlling C++ Dialect โœจ

this section is all about the command-line options that only mean something for C++ programs ๐Ÿ’œ you can still use most of the GNU compiler options no matter what language your program is in, though. for example, you might compile a file firstClass.C like this:

g++ -g -fstrict-enums -O -c firstClass.C

in this example, only -fstrict-enums is meant only for C++ programs ๐Ÿ“ you can use the other options with any language GCC supports.

some options for compiling C programs, like -std, are also relevant for C++ programs too ๐Ÿฆ‹

here's the list of options that are only for compiling C++ programs ๐Ÿ‘‘

  • -fabi-version=n โ€” use version n of the C++ ABI ๐Ÿ’– the default is version 0.
    • version 0 refers to the version that conforms most closely to the C++ ABI specification. so the ABI you get with version 0 will change between versions of G++ as ABI bugs get fixed ๐Ÿ™ˆ
    • version 1 is the version of the C++ ABI that first showed up in G++ 3.2.
    • version 2 is the version that first showed up in G++ 3.4, and it was the default through G++ 4.9.
    • version 3 fixes an error in mangling a constant address as a template argument.
    • version 4, which first showed up in G++ 4.5, implements a standard mangling for vector types.
    • version 5, which first showed up in G++ 4.6, fixes the mangling of attribute const/volatile on function pointer types, decltype of a plain decl, and use of a function parameter in the declaration of another parameter.
    • version 6, which first showed up in G++ 4.7, fixes the promotion behavior of C++11 scoped enums and the mangling of template argument packs, const/static_cast, prefix ++ and --, and a class scope function used as a template argument.
    • version 7, which first showed up in G++ 4.8, treats nullptr_t as a builtin type and fixes the mangling of lambdas in default argument scope.
    • version 8, which first showed up in G++ 4.9, fixes the substitution behavior of function types with function-cv-qualifiers.
    • version 9, which first showed up in G++ 5.2, fixes the alignment of "nullptr_t".
    • version 10, which first showed up in G++ 6.1, adds mangling of attributes that affect type identity, such as ia32 calling convention attributes (e.g. stdcall).
    • version 11, which first showed up in G++ 7, fixes the mangling of sizeof... expressions and operator names. for multiple entities with the same name within a function, declared in different scopes, the mangling now changes starting with the twelfth occurrence. it also implies -fnew-inheriting-ctors.
    • version 12, which first showed up in G++ 8, fixes the calling conventions for empty classes on the x86_64 target and for classes with only deleted copy/move constructors. it accidentally changes the calling convention for classes with a deleted copy constructor and a trivial move constructor.
    • version 13, which first showed up in G++ 8.2, fixes the accidental change in version 12 ๐Ÿซถ
    • version 14, which first showed up in G++ 10, fixes the mangling of the nullptr expression.
    • version 15, which first showed up in G++ 10.3, fixes the G++ 10 ABI tag regression.
    • version 16, which first showed up in G++ 11, changes the mangling of "__alignof__" so it's distinct from that of "alignof", plus dependent operator names.
    • version 17, which first showed up in G++ 12, fixes the layout of classes that inherit from aggregate classes with default member initializers in C++14 and up.
    • version 18, which first showed up in G++ 13, fixes manglings of lambdas that have additional context.
    • version 19, which first showed up in G++ 14, fixes manglings of structured bindings to include ABI tags.
    • version 20, which first showed up in G++ 15, fixes manglings of lambdas in static data member initializers.
    • see also -Wabi.
  • -fabi-compat-version=n โ€” on targets that support strong aliases, G++ works around mangling changes by creating an alias with the correct mangled name whenever it defines a symbol with an incorrect mangled name ๐ŸŒธ this switch picks which ABI version to use for that alias.
    • with -fabi-version=0 (the default), this defaults to 13 (GCC 8.2 compatibility). if another ABI version is explicitly selected, it defaults to 0. for compatibility with GCC versions 3.2 through 4.9, use -fabi-compat-version=2.
    • if this option isn't provided but -Wabi=n is, that version is used for compatibility aliases. if this option is provided along with -Wabi (without the version), the version from this option gets used for the warning โœจ
  • -fno-access-control โ€” turns off all access checking ๐Ÿ”“ this switch is mainly useful for working around bugs in the access control code.
  • -faligned-new โ€” enables support for C++17 "new" of types that need more alignment than "void* ::operator new(std::size_t)" provides ๐ŸŽ€ a numeric argument like "-faligned-new=32" can say how much alignment (in bytes) that function provides, but few users will need to override the default of alignof(std::max_align_t).
    • this flag is enabled by default for -std=c++17.
  • -fno-assume-sane-operators-new โ€” the C++ standard lets you replace the global "new", "new[]", "delete" and "delete[]" operators, though a lot of C++ programs don't replace them and just use the implementation-provided version ๐Ÿง the standard also lets you omit those calls when they're made from new or delete expressions (and by extension the same is assumed if "__builtin_operator_new" or "__builtin_operator_delete" functions are used). this option controls some optimizations around calls to those operators.
    • with "-fassume-sane-operators-new-delete", GCC may assume that calls to the replaceable global operators from new or delete expressions, or from "__builtin_operator_new" or "__builtin_operator_delete" calls, don't read or modify any global variables, or variables whose address could escape to the operators (global state; except for "errno" for the "new" and "new[]" operators). this allows most optimizations across those calls. the implementation-provided operators satisfy this unless "malloc" implementation details are observable in the code or "malloc" hooks are used, but it might not hold if a program replaces those operators.
    • this behavior is enabled by default.
    • with "-fno-assume-sane-operators-new-delete", GCC must assume all these calls (whether from new or delete expressions or called directly) may read and write global state unless proven otherwise (e.g. when GCC compiles their implementation). use this option if those operators are or may be replaced and your code needs to expect that behavior ๐Ÿ˜ค
  • -fchar8_t, -fno-char8_t โ€” enables support for "char8_t" as adopted for C++20 ๐Ÿ’… that includes a new "char8_t" fundamental type, changes to the types of UTF-8 string and character literals, new signatures for user-defined literals, associated standard library updates, and new "__cpp_char8_t" and "__cpp_lib_char8_t" feature test macros.

this option lets functions be overloaded for ordinary and UTF-8 strings:

        int f(const char *);    // #1
        int f(const char8_t *); // #2
        int v1 = f("text");     // Calls #1
        int v2 = f(u8"text");   // Calls #2

and it introduces new signatures for user-defined literals:

        int operator""_udl1(char8_t);
        int v3 = u8'x'_udl1;
        int operator""_udl2(const char8_t*, std::size_t);
        int v4 = u8"text"_udl2;
        template<typename T, T...> int operator""_udl3();
        int v5 = u8"text"_udl3;
๐Ÿšจ heads up: the change to the types of UTF-8 string and character literals introduces incompatibilities with ISO C++11 and later standards. for example, the following code is well-formed under ISO C++11, but it's ill-formed when -fchar8_t is specified ๐Ÿ’€
        const char *cp = u8"xx";// error: invalid conversion from
                                //        `const char8_t*' to `const char*'
        int f(const char*);
        auto v = f(u8"xx");     // error: invalid conversion from
                                //        `const char8_t*' to `const char*'
        std::string s{u8"xx"};  // error: no matching function for call to
                                //        `std::basic_string<char>::basic_string()'
        using namespace std::literals;
        s = u8"xx"s;            // error: conversion from
                                //        `basic_string<char8_t>' to non-scalar
                                //        type `basic_string<char>' requested
  • -fcheck-new โ€” okay so this one makes G++ check that the pointer operator new hands back isn't null before it starts messing with the memory ๐ŸŽ€. Honestly it's usually unnecessary, because the C++ standard says operator new only returns 0 if it's declared throw(), and in that case the compiler checks the return value anyway, even without this flag. In every other case, where operator new has a non-empty exception specification, running out of memory gets signalled by throwing std::bad_alloc ๐Ÿ’€. See also new (nothrow).
  • -fconcepts โ€” the bestie of template arguments ๐Ÿฆ‹. This turns on support for the C++ Concepts feature, which lets you constrain template arguments. With -std=c++20 and above, Concepts are part of the language standard, so -fconcepts is on by default โœจ.

Some constructs from the earlier C++ Extensions for Concepts Technical Specification, ISO 19217 (2015), didn't make it into the standard, but they can still be enabled with -fconcepts-ts ๐ŸŒธ. Real talk though, -fconcepts-ts was deprecated in GCC 14 and removed in GCC 15, so users are expected to convert their code to C++20 concepts ๐Ÿ˜ญ.

  • -fconstexpr-depth=n โ€” sets the maximum nested evaluation depth for C++11 constexpr functions to n ๐Ÿงš. You need a limit like this so endless recursion gets caught during constant expression evaluation. The standard's minimum is 512.
  • -fconstexpr-cache-depth=n โ€” sets the maximum level of nested evaluation depth for C++11 constexpr functions that gets cached, to n ๐Ÿ’œ. This is a heuristic, so it's a trade-off: caching can speed up compilation when it avoids repeated calculations, but it eats memory when highly recursive evaluations make the cache grow huge. The default is 8. Very few people need to touch it, but if your code does heavy constexpr maths, feel free to experiment and find what works best for you โœจ.
  • -fconstexpr-fp-except โ€” okay this one is spicy ๐Ÿ”ฅ. Annex F of the C standard says IEC559 floating point exceptions hit at compile time shouldn't stop compilation. C++ compilers have historically ignored that and treated floating point division by zero as non-constant, even though it has a well defined value. This flag tells the compiler to give Annex F priority over other rules that say an operation is undefined:
        constexpr float inf = 1./0.; // OK with -fconstexpr-fp-except
  • -fconstexpr-loop-limit=n โ€” sets the maximum number of iterations for a loop in C++14 constexpr functions to n ๐Ÿ”. Endless loops need a cap so they get caught during constant expression evaluation. The default is 262144 (1<<18).
  • -fconstexpr-ops-limit=n โ€” sets the maximum number of operations during a single constexpr evaluation ๐Ÿ™ˆ. Even if each loop is under the iteration limit above, several nested loops with lots of iterations, or a ton of expressions inside a loop or outside of one, can make the evaluation take forever. The default is 33554432 (1<<25).
  • -fcontracts โ€” enables experimental support for the C++ Contracts feature ๐Ÿงช. It was briefly added to and then removed from the C++20 working paper (N4820), and the implementation also includes proposed enhancements from papers P1290, P1332, and P1429. This is mostly for people who want to experiment and help shape the feature for a future C++ standard ๐Ÿซถ.

When a checked contract is violated, the violation handler gets called. You can swap in your own handler by defining:

        void
        handle_contract_violation (const std::experimental::contract_violation&);

There are different sets of extra flags for N4820, P1332, or P1429 contracts, and you can't mix these sets together ๐ŸŒˆ. Here they are:

  • -fcontract-mode=[on|off] โ€” controls whether contracts have any semantics at all. Defaults to on.
  • -fcontract-assumption-mode=[on|off] โ€” [N4820] controls whether contracts with level axiom should have the assume semantic. Defaults to on.
  • -fcontract-build-level=[off|default|audit] โ€” [N4820] picks which level of contracts gets checks generated for it. Defaults to default.
  • -fcontract-continuation-mode=[on|off] โ€” [N4820] controls whether the program is allowed to keep running after a contract violation. In other words, do checked contracts get the maybe semantic instead of the never semantic. Defaults to off.
  • -fcontract-role=<name>:<default>,<audit>,<axiom> โ€” [P1332] sets the concrete semantics for each contract level of a particular contract role ๐Ÿ’–.
  • -fcontract-semantic=[default|audit|axiom]:<semantic> โ€” [P1429] sets the concrete semantic for a particular contract level โœจ.
  • -fcontract-strict-declarations=[on|off] โ€” controls whether adding contracts to a function after its first declaration gets rejected. Defaults to off.

The concrete semantics you can pick with -fcontract-role or -fcontract-semantic are:

  • ignore โ€” this contract has no effect ๐Ÿ™ˆ.
  • assume โ€” this contract is treated like C++23 [[assume]].
  • check_never_continue, never, abort โ€” this contract is checked. If it fails, the violation handler gets called. If the handler returns, std::terminate is called ๐Ÿ’€.
  • check_maybe_continue, maybe โ€” this contract is checked. If it fails, the violation handler gets called. If the handler returns, execution keeps going normally ๐Ÿฆ‹.
  • -fcoroutines โ€” enables support for the C++ coroutines extension. It's experimental, so handle with a little care ๐Ÿงš.
  • -fdiagnostics-all-candidates โ€” lets the C++ front end note all the candidates during an overload resolution failure ๐Ÿ”. That includes the case where a deleted function gets selected.
  • -fno-elide-constructors โ€” the C++ standard lets an implementation skip creating a temporary that's only used to initialize another object of the same type ๐ŸŽ€. This flag turns that optimization off and forces G++ to call the copy constructor in all cases. It also makes G++ call trivial member functions that would otherwise be expanded inline.

In C++17, the compiler is required to omit these temporaries, but this option still affects trivial member functions ๐Ÿซ .

  • -fno-enforce-eh-specs โ€” don't generate code that checks for violations of exception specifications at run time ๐Ÿงƒ. This breaks the C++ standard, but it can be handy for shrinking code size in production builds, kind of like defining NDEBUG. It does not give your code permission to throw exceptions that break the exception specifications. The compiler still optimizes based on those specs, so throwing an unexpected exception gives you undefined behavior at run time ๐Ÿ˜ฌ.
  • -fextern-tls-init, -fno-extern-tls-init โ€” the C++11 and OpenMP standards let thread_local and threadprivate variables have dynamic (runtime) initialization ๐Ÿงต. To make that work, any use of such a variable goes through a wrapper function that does the necessary initialization. When the use and the definition are in the same translation unit, the compiler can optimize that overhead away. But when the use is in a different translation unit, there's a big overhead even if the variable doesn't actually need dynamic initialization. If you can be sure that no use in a non-defining TU needs to trigger dynamic initialization, either because the variable is statically initialized or because a use in the defining TU runs before any use in another TU, you can skip that overhead with -fno-extern-tls-init ๐Ÿ’….

On targets that support symbol aliases, the default is -fextern-tls-init. On targets that don't support symbol aliases, the default is -fno-extern-tls-init.

  • -ffold-simple-inlines, -fno-fold-simple-inlines โ€” lets the C++ frontend fold calls to std::move, std::forward, std::addressof, and std::as_const ๐ŸŒท. Unlike inlining, this means no debug information gets generated for those calls. Since these functions are rarely interesting to debug, this flag is enabled by default unless -fno-inline is active.
  • -fno-gnu-keywords โ€” typeof stops being a keyword, so your code can use that word as an identifier ๐Ÿ™ˆ. You can use the keyword __typeof__ instead. This option is implied by the strict ISO C++ dialects: -ansi, -std=c++98, -std=c++11, etc.
  • -fno-immediate-escalation โ€” stops immediate function escalation, where certain functions can get promoted to consteval, as specified in P2564R3 ๐Ÿ‘‘. Here's an example:
        consteval int id(int i) { return i; }

        constexpr int f(auto t)
        {
          return t + id(t); // id causes f<int> to be promoted to consteval
        }

        void g(int i)
        {
          f (3);
        }

That compiles in C++20: f is an immediate-escalating function (because of the auto it's a function template and it's declared constexpr), and id(t) is an immediate-escalating expression, so f gets promoted to consteval. As a result, the call to id(t) sits in an immediate context, so it doesn't have to produce a constant. That's the mechanism that allows consteval function composition ๐Ÿงš. However, with -fno-immediate-escalation, f doesn't get promoted to consteval, and since the call to the consteval function id(t) isn't a constant expression, the compiler rejects the code โŒ.

This option is turned on by default, and it only does anything in C++20 mode or later.

  • -fimplicit-constexpr โ€” makes inline functions implicitly constexpr if they meet the requirements for a constexpr function โœจ. This option can be used in C++14 mode or later. It can change initialization from dynamic to static, plus some other optimizations come along for the ride ๐ŸŒˆ.

โœจ OPTIONS (continued) โœจ

  • -fno-implicit-templates โ€” okay so this one is a lot. The compiler will never emit code for non-inline templates that get instantiated implicitly (aka just by using them) ๐Ÿ™ˆ. It only emits code for explicit instantiations. If you turn this on, you have to structure your code so you include every explicit instantiation you need, or you'll get undefined symbols at link time ๐Ÿ’€.
โš ๏ธ bestie, if you skip the explicit instantiations with this flag on, the linker will throw undefined symbol errors at you. Plan ahead!
  • -fno-implicit-inline-templates โ€” don't emit code for implicit instantiations of inline templates either ๐Ÿ’…. By default the compiler handles inlines differently, so that compiles with and without optimization need the same set of explicit instantiations. Iconic consistency ๐Ÿซถ.
  • -fno-implement-inlines โ€” to save space, don't emit out-of-line copies of inline functions controlled by #pragma implementation โœจ. Heads up: this causes linker errors if those functions aren't inlined everywhere they're called ๐Ÿ˜ญ.
  • -fmodules, -fno-modules โ€” enable support for C++20 modules ๐Ÿงš. The -fno-modules flag is usually not needed, since that's already the default. Even though modules are a C++20 feature, selecting that standard version does not currently turn them on implicitly, so you have to ask for -fmodules yourself ๐ŸŽ€.
  • -fmodule-header, -fmodule-header=user, -fmodule-header=system โ€” compile a header file so it becomes an importable header unit ๐Ÿฆ‹.
  • -fmodule-implicit-inline โ€” okay so in modular code, member functions defined inside their class definition are not implicitly inline. That's different from traditional C++ behavior, and it's for good reasons ๐Ÿค“. It can cause a bit of a headache when porting code though. This option makes those function definitions implicitly inline. Be warned: it creates an ABI incompatibility, so you must use it everywhere or nowhere ๐Ÿšซ. (Definitions outside of a named module stay implicitly inline no matter what ๐ŸŒธ.)
  • -fno-module-lazy โ€” disable lazy module importing and module mapper creation ๐Ÿ’ค.
  • -fmodule-mapper=[hostname]:port[?ident], -fmodule-mapper=|program[?ident] args..., -fmodule-mapper==socket[?ident], -fmodule-mapper=<>[inout][?ident], -fmodule-mapper=<in>out[?ident], -fmodule-mapper=file[?ident] โ€” this is the oracle that answers "which filename belongs to this module name?" ๐Ÿ”ฎ. If you don't specify one, the CXX_MODULE_MAPPER environment variable gets used, and if that's unset too, an in-process default is provided โœจ.
  • -fmodule-only โ€” only emit the Compiled Module Interface, and skip the object file entirely ๐Ÿ“ฆ.
  • -fms-extensions โ€” disable -Wpedantic warnings about constructs used in MFC ๐ŸชŸ. Examples are implicit int, and getting a pointer to a member function using non-standard syntax ๐Ÿ™ˆ.
  • -fnew-inheriting-ctors โ€” enable the P0136 adjustment to the semantics of C++11 constructor inheritance ๐Ÿ‘‘. It's part of C++17, but it's also considered a Defect Report against C++11 and C++14. This flag is enabled by default unless -fabi-version=10 or lower is specified ๐Ÿ’–.
  • -fnew-ttp-matching โ€” enable the P0522 resolution to Core issue 150, which covers template template parameters and default arguments ๐ŸŒˆ. It allows a template with default template arguments to be passed as an argument for a template template parameter that has fewer template parameters. This flag is enabled by default for -std=c++17 โญ.
  • -fno-nonansi-builtins โ€” disable built-in declarations of functions that aren't mandated by ANSI/ISO C ๐Ÿงน. That includes "ffs", "alloca", "_exit", "index", "bzero", "conjf", and other related functions ๐Ÿ“.
  • -fnothrow-opt โ€” treat a throw() exception specification as if it were a "noexcept" specification ๐ŸŒธ. This reduces or eliminates the text size overhead compared to a function with no exception specification. If the function has local variables with non-trivial destructors, the exception specification actually makes the function smaller, because the EH cleanups for those variables can be optimized away ๐Ÿคฏ. The semantic effect is that an exception thrown out of a function with this exception specification results in a call to "terminate" instead of "unexpected" ๐Ÿ’€.
  • -fno-operator-names โ€” do not treat the operator name keywords "and", "bitand", "bitor", "compl", "not", "or", and "xor" as synonyms for the operators ๐Ÿ™….
  • -fno-optional-diags โ€” disable diagnostics that the standard says a compiler doesn't need to issue ๐Ÿซ . Right now, the only one G++ issues like that is the one for a name having multiple meanings within a class ๐ŸŽญ.
  • -fno-pretty-templates โ€” okay so when an error message refers to a specialization of a function template, the compiler normally prints the template's signature, followed by the template arguments and any typedefs or typenames in that signature. So you get something like "void f(T) [with T = int]" instead of "void f(int)", so you can tell which template is involved ๐Ÿ”. When an error refers to a specialization of a class template, it leaves out any template arguments that match the default template arguments for that template โœ‚๏ธ. If either of those behaviors makes errors harder to read instead of easier, use -fno-pretty-templates to turn them off ๐Ÿ’….
  • -frange-for-ext-temps โ€” enable lifetime extension of C++ range-based for temporaries ๐ŸŽ€. With -std=c++23 and above this is part of the language standard, so the temporaries get extended until the end of the loop by default. This option lets you turn that behavior on in earlier versions of the standard too โณ.
  • -fno-rtti โ€” disable generation of run-time type information for every class with virtual functions ๐Ÿงฌ. That info is what the "dynamic_cast" and "typeid" features use. If you don't use those parts of the language, you can save some space with this flag ๐Ÿ’œ. Exception handling uses the same information, but G++ generates it only when needed ๐ŸŒท. The "dynamic_cast" operator can still be used for casts that don't need run-time type information, like casts to "void *" or to unambiguous base classes โœจ.
โš ๏ธ Mixing code compiled with -frtti and code compiled with -fno-rtti may not work ๐Ÿ˜ฌ. For example, programs may fail to link if a class compiled with -fno-rtti is used as a base for a class compiled with -frtti.
  • -fsized-deallocation โ€” enable the built-in global declarations below, which were introduced in C++14 ๐Ÿ—‘๏ธ:
void operator delete (void *, std::size_t) noexcept;
void operator delete[] (void *, std::size_t) noexcept;

This is useful for user-defined replacement deallocation functions that, for example, use the size of the object to make deallocation faster ๐Ÿš€. It's enabled by default under -std=c++14 and above. The flag -Wsized-deallocation warns about places that might want to add a definition ๐Ÿ‘€.

  • -fstrict-enums โ€” lets the compiler optimize using the assumption that a value of an enumerated type can only be one of the values of the enumeration ๐Ÿ”ข. (As defined in the C++ standard: basically, a value that can be represented in the minimum number of bits needed to represent all the enumerators.) This assumption may not hold if the program uses a cast to convert an arbitrary integer value to the enumerated type ๐Ÿ™ƒ. This option has no effect for an enumeration type with a fixed underlying type ๐Ÿซฅ.
  • -fstrong-eval-order, -fstrong-eval-order=kind โ€” evaluate member access, array subscripting, and shift expressions in left-to-right order, and evaluate assignment in right-to-left order, as adopted for C++17 ๐Ÿน. -fstrong-eval-order is equivalent to -fstrong-eval-order=all, and it's enabled by default with -std=c++17 or later ๐Ÿ‘‘.
    • -fstrong-eval-order=some enables just the ordering of member access and shift expressions, and it's the default for C++ dialects prior to C++17 ๐ŸŒ™.
    • -fstrong-eval-order=none is equivalent to -fno-strong-eval-order ๐Ÿซง.
  • -ftemplate-backtrace-limit=n โ€” set the maximum number of template instantiation notes for a single warning or error to n ๐Ÿ“œ. The default value is 10 ๐Ÿ”Ÿ.
  • -ftemplate-depth=n โ€” set the maximum instantiation depth for template classes to n ๐ŸŒ€. A limit on template instantiation depth is needed to detect endless recursions during template class instantiation ๐Ÿ”. ANSI/ISO C++ conforming programs must not rely on a maximum depth greater than 17 (changed to 1024 in C++11) ๐Ÿ“. The default value is 900, since the compiler can run out of stack space before hitting 1024 in some situations ๐Ÿ˜ต.
  • -fno-threadsafe-statics โ€” don't emit the extra code that uses the routines from the C++ ABI for thread-safe initialization of local statics ๐Ÿงต. You can use this to shave a bit off code size in code that doesn't need to be thread-safe ๐Ÿƒ.
  • -fuse-cxa-atexit โ€” register destructors for objects with static storage duration with the "__cxa_atexit" function, instead of the "atexit" function ๐Ÿšช. This option is required for fully standards-compliant handling of static destructors, but it only works if your C library supports "__cxa_atexit" ๐Ÿ“š.
  • -fno-use-cxa-get-exception-ptr โ€” don't use the "__cxa_get_exception_ptr" runtime routine ๐Ÿ™…โ€โ™€๏ธ. This causes "std::uncaught_exception" to be incorrect, but it's necessary if the runtime routine isn't available ๐Ÿ› ๏ธ.
  • -fvisibility-inlines-hidden โ€” this switch declares that the user doesn't try to compare pointers to inline functions or methods where the addresses of the two functions are taken in different shared objects ๐Ÿซฃ.

The effect is that GCC may, effectively, mark inline methods with __attribute__ ((visibility ("hidden"))), so they don't show up in the export table of a DSO and don't need a PLT indirection when used inside the DSO ๐Ÿฆ‹. Turning this on can have a dramatic effect on the load and link times of a DSO, because it massively shrinks the dynamic export table when the library makes heavy use of templates ๐Ÿš€.

The behavior isn't quite the same as marking the methods as hidden directly, because it doesn't affect static variables local to the function, and it doesn't make the compiler deduce that the function is defined in only one shared object ๐Ÿง.

You can give a method an explicit visibility to cancel out the effect of the switch for that method ๐Ÿ’ซ. For example, if you do want to compare pointers to a particular inline method, you might mark it as having default visibility ๐Ÿ‘€. Marking the enclosing class with explicit visibility has no effect ๐Ÿ™ƒ.

Explicitly instantiated inline methods are unaffected by this option, since their linkage might otherwise cross a shared library boundary ๐ŸŒ‰.

  • -fvisibility-ms-compat โ€” okay so this one is a whole mood ๐ŸŽ€ It tries to use visibility settings to make GCC's C++ linkage model act like Microsoft Visual Studio's, bestie ๐Ÿ’…

It makes these changes to GCC's linkage model:

  1. It sets the default visibility to "hidden", same as -fvisibility=hidden ๐Ÿ™ˆ
  2. Types are not hidden by default, but their members are ๐Ÿ‘€
  3. The One Definition Rule gets relaxed for types with no explicit visibility specification that are defined in more than one shared object. Those declarations are allowed if they'd be allowed without this option โœจ

Honestly, in new code you should use -fvisibility=hidden and export only the classes you actually want visible outside. Unfortunately, code might be relying on the Visual Studio behavior, perhaps by accident ๐Ÿ˜ญ

Some of the fallout: static data members of the same type, with the same name, defined in different shared objects are different, so changing one won't change the other ๐Ÿ’€ Also, pointers to function members defined in different shared objects may not compare equal. And when this flag is given, defining types with the same name in different ways is a violation of the ODR ๐Ÿšจ

  • -fno-weak โ€” no weak symbol support, even if your linker provides it ๐Ÿ™…โ€โ™€๏ธ By default, G++ uses weak symbols when they're available. This one only exists for testing and should not be used by end-users. It gives you inferior code and has no benefits, so don't. It may be removed in a future release of G++ ๐Ÿฅ€
  • -fext-numeric-literals (C++ and Objective-C++ only) โ€” accepts imaginary, fixed-point, or machine-defined literal number suffixes as GNU extensions โœจ When this option is turned off, those suffixes are treated as C++11 user-defined literal numeric suffixes instead. It's on by default for all pre-C++11 dialects and all GNU dialects: -std=c++98, -std=gnu++98, -std=gnu++11, -std=gnu++14. It's off by default for ISO C++11 onwards (-std=c++11, ...) ๐ŸŒธ
  • -nostdinc++ โ€” skips the standard directories that are specific to C++ when searching for header files, but still searches the other standard directories ๐Ÿงš (This one is used when building the C++ library, so it's giving behind-the-scenes energy ๐Ÿ’œ)
  • -flang-info-include-translate, -flang-info-include-translate-not, -flang-info-include-translate=header โ€” these tell you about include translation events ๐Ÿ‘€ The first notes accepted include translations, the second notes declined ones. The header form informs you about include translations for that specific header. If header is of the form "user" or "<system>", it gets resolved to a specific user or system header using the include path ๐Ÿซถ
  • -flang-info-module-cmi, -flang-info-module-cmi=module โ€” informs you about Compiled Module Interface pathnames ๐Ÿฆ‹ The first one notes all read CMI pathnames. The module form does not read a specific module's CMI, which is the vibe to remember here ๐Ÿ˜… module may be a named module or a header-unit. A header-unit is indicated by either a pathname containing directory separators, or by being enclosed in "<>" or "".
  • -stdlib=libstdc++,libc++ โ€” when G++ is configured to support this option, you can pick an alternate C++ runtime library ๐ŸŒˆ There are two options: libstdc++ (the default, native C++ runtime for G++) and libc++, which is the C++ runtime installed on some operating systems (for example, Darwin versions from Darwin11 onwards). This switches G++ to use the headers from the library you pick, and emits "-lstdc++" or "-lc++" respectively when a C++ runtime is needed for linking ๐Ÿ”ฅ

In addition, these warning options only have meanings for C++ programs, so here's the C++-only tea โ˜•

  • -Wabi-tag (C++ and Objective-C++ only) โ€” warns when a type with an ABI tag is used in a context that doesn't have that ABI tag ๐Ÿท๏ธ Check out C++ Attributes for more info on ABI tags โœจ
  • -Wcomma-subscript (C++ and Objective-C++ only) โ€” warns about uses of a comma expression inside a subscripting expression ๐Ÿ™ˆ This was deprecated in C++20 and is going to be removed in C++23. A comma expression wrapped in "( )" is not deprecated, though, so you're safe there ๐Ÿ’– Example:
void f(int *a, int b, int c) {
    a[b,c];     // deprecated in C++20, invalid in C++23
    a[(b,c)];   // OK
}

In C++23 it's valid to have comma separated expressions in a subscript when an overloaded subscript operator is found and supports the right number and types of arguments ๐Ÿฆ„ G++ will accept the formerly valid syntax for code that's not valid in C++23, but used to be valid (though deprecated) in C++20, with a pedantic warning. You can disable that with -Wno-comma-subscript ๐Ÿ’…

It's enabled by default with -std=c++20 unless you use -Wno-deprecated, and after -std=c++23 regardless of -Wno-deprecated. Before -std=c++20, it's enabled with an explicit -Wdeprecated ๐ŸŒธ

This warning gets upgraded to an error by -pedantic-errors in C++23 mode or later ๐Ÿ˜ญ

  • -Wctad-maybe-unsupported (C++ and Objective-C++ only) โ€” warns when you do class template argument deduction (CTAD) on a type with no explicitly written deduction guides ๐Ÿ‘€ This points out cases where CTAD only succeeded because the compiler synthesized the implicit deduction guides, which might not be what the programmer meant. Some style guides only allow CTAD on types that specifically "opt-in", meaning types designed to support CTAD ๐Ÿง You can suppress this warning with the following pattern:
struct allow_ctad_t; // any name works
template <typename T> struct S {
  S(T) { }
};
// Guide with incomplete parameter type will never be considered.
S(allow_ctad_t) -> S<void>;
  • -Wctor-dtor-privacy (C++ and Objective-C++ only) โ€” warns when a class seems unusable because all its constructors or destructors are private, and it has neither friends nor public static member functions ๐Ÿ”’ It also warns if there are no non-private methods, and there's at least one private member function that isn't a constructor or destructor.
  • -Wdangling-reference (C++ and Objective-C++ only) โ€” warns when a reference is bound to a temporary whose lifetime has already ended ๐Ÿ’€ For example:
int n = 1;
const int& r = std::max(n - 1, n + 1); // r is dangling

In that example, two temporaries are created, one for each argument, and a reference to one of them is returned. Both temporaries are destroyed at the end of the full expression, so the reference "r" is dangling ๐Ÿ˜ฑ This warning also catches dangling references in member initializer lists:

const int& f(const int& i) { return i; }
struct S {
  const int &r; // r is dangling
  S() : r(f(10)) { }
};

Member functions get checked too, but only their object argument:

struct S {
   const S& self () { return *this; }
};
const S& s = S().self(); // s is dangling

Some functions are safe in this respect. For example, "std::use_facet" takes and returns a reference, but it doesn't return one of its arguments, which can fool the warning ๐Ÿซ  You can exclude functions like that by wrapping them in a "#pragma":

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
const T& foo (const T&) { ... }
#pragma GCC diagnostic pop

The "#pragma" can also surround the class. In that case, the warning is disabled for all the member functions โœจ

-Wdangling-reference also warns about code like this:

auto p = std::minmax(1, 2);

where "std::minmax" returns "std::pair<const int&, const int&>", and both references dangle after the end of the full expression that contains the call to "std::minmax" ๐Ÿฅ€

The warning doesn't fire for "std::span"-like classes ๐Ÿงš We consider classes of this form:

template<typename T>
struct Span {
  T* data_;
  std::size len_;
};

as "std::span"-like. That means the class is a non-union class that has a pointer data member and a trivial destructor.

You can disable the warning by using the "gnu::no_dangling" attribute ๐Ÿ™…โ€โ™€๏ธ

This warning is enabled by -Wextra ๐Ÿ”ฅ

  • -Wdelete-non-virtual-dtor (C++ and Objective-C++ only) โ€” warns when "delete" is used to destroy an instance of a class that has virtual functions and a non-virtual destructor ๐Ÿšจ It's unsafe to delete an instance of a derived class through a pointer to a base class if the base class doesn't have a virtual destructor. This warning is enabled by -Wall ๐Ÿ’–
  • -Wdeprecated-copy (C++ and Objective-C++ only) โ€” warns that the implicit declaration of a copy constructor or copy assignment operator is deprecated if the class has a user-provided copy constructor or copy assignment operator, in C++11 and up ๐ŸŒท This warning is enabled by -Wextra. With -Wdeprecated-copy-dtor, it also deprecates the implicit declaration if the class has a user-provided destructor ๐Ÿซถ
  • -Wno-deprecated-enum-enum-conversion (C++ and Objective-C++ only) โ€” disables the warning about when the usual arithmetic conversions are applied to operands where one is an enumeration type and the other is a different enumeration type ๐Ÿ™ƒ This conversion was deprecated in C++20. For example:
enum E1 { e };
enum E2 { f };
int k = f - e;

-Wdeprecated-enum-enum-conversion is enabled by default with -std=c++20. In pre-C++20 dialects, you can enable this warning with -Wenum-conversion or -Wdeprecated ๐ŸŒˆ

  • -Wno-deprecated-enum-float-conversion (C++ and Objective-C++ only) โ€” disables the warning about when the usual arithmetic conversions are applied to operands where one is an enumeration type and the other is a floating-point type ๐Ÿซง This conversion was deprecated in C++20. For example:
enum E1 { e };
enum E2 { f };
bool b = e <= 3.7;

-Wdeprecated-enum-float-conversion is enabled by default with -std=c++20. In pre-C++20 dialects, you can enable this warning with -Wenum-conversion or -Wdeprecated โœจ

  • -Wdeprecated-literal-operator (C++ and Objective-C++ only) โ€” okay so this one warns you that declaring a user-defined literal operator with a space before the suffix is deprecated ๐Ÿ™…โ€โ™€๏ธ. It's on by default in C++23, or whenever you explicitly turn on -Wdeprecated. ๐Ÿ’…
string operator "" _i18n(const char*, std::size_t); // deprecated
string operator ""_i18n(const char*, std::size_t); // preferred
  • -Wdeprecated-variadic-comma-omission (C++ and Objective-C++ only) โ€” leaving out the comma before the varargs ... at the end of a parameter list is deprecated, and this warning tells you so ๐ŸŽ€. It's enabled by default in C++26, or with an explicit -Wdeprecated. โœจ
void f1(int...); // deprecated
void f1(int, ...); // preferred
template <typename ...T>
void f2(T...); // ok
template <typename ...T>
void f3(T......); // deprecated
  • -Wno-elaborated-enum-base โ€” for C++11 and up, this warns if an (invalid) extra enum-base shows up in an elaborated-type-specifier ๐Ÿ™ˆ. Basically that's an enum with a given underlying type and no enumerator list, used somewhere other than a standalone declaration of the enum. It's enabled by default, and -pedantic-errors upgrades it into a full error. ๐Ÿ˜ญ
  • -Wno-init-list-lifetime (C++ and Objective-C++ only) โ€” this turns off warnings for std::initializer_list uses that are likely to leave you with dangling pointers ๐Ÿ’€. The underlying array of an initializer_list gets treated like a normal C++ temporary, so it's super easy to accidentally keep a pointer to that array after its lifetime is over. Some classic ways that happens:
    • If a function returns a temporary initializer_list, or a local initializer_list variable, the array's lifetime ends at the end of the return statement, so the returned value has a dangling pointer.
    • If a new-expression creates an initializer_list, the array only lives until the end of the enclosing full-expression, so the initializer_list on the heap has a dangling pointer.
    • When an initializer_list variable is assigned from a brace-enclosed initializer list, the temporary array made for the right side of the assignment only lives until the end of the full-expression. So by the next statement, that variable has a dangling pointer.
    • When a list constructor stores the begin pointer from the initializer_list argument, that doesn't extend the array's lifetime. So if a class variable is constructed from a temporary initializer_list, the pointer is left dangling by the end of the variable declaration statement.
// li's initial underlying array lives as long as li
std::initializer_list<int> li = { 1,2,3 };
// assignment changes li to point to a temporary array
li = { 4, 5 };
// now the temporary is gone and li has a dangling pointer
int i = li.begin()[0] // undefined behavior
  • -Winvalid-constexpr โ€” this warns when a function never produces a constant expression ๐Ÿง. In C++20 and earlier, every constexpr function and function template needs at least one set of arguments in at least one instantiation where calling it could be an evaluated subexpression of a core constant expression. C++23 dropped that rule, so you can now have a constexpr function or function template where no call ever meets the requirements of a core constant expression.

This warning is enabled as a pedantic warning by default in C++20 and earlier. In C++23, you can turn -Winvalid-constexpr on, and then it becomes an ordinary warning. For example:

void f (int& i);
constexpr void
g (int& i)
{
  // Warns by default in C++20, in C++23 only with -Winvalid-constexpr.
  f(i);
}
  • -Winvalid-imported-macros โ€” this checks that all imported macro definitions are valid at the end of compilation ๐Ÿฆ‹. It's not on by default, because it needs extra processing to figure out. It can be really handy when you're preparing sets of header-units and want your macros to stay consistent. ๐Ÿซถ
  • -Wno-literal-suffix (C++ and Objective-C++ only) โ€” this stops the warning when a string or character literal is followed by a ud-suffix that doesn't start with an underscore ๐ŸŒธ. GCC treats those suffixes as separate preprocessing tokens, which is a conforming extension that keeps old code using formatting macros from <inttypes.h> working. For example:
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdio.h>

int main() {
  int64_t i64 = 123;
  printf("My int64: %" PRId64"\n", i64);
}

In this case, PRId64 is treated as a separate preprocessing token.

This option also controls the warnings you get when a user-defined literal operator is declared with a literal suffix identifier that doesn't start with an underscore. Those suffixes are reserved for future standardization, so GCC warns you about them ๐Ÿ‘€. These warnings are enabled by default.

  • -Wno-narrowing (C++ and Objective-C++ only) โ€” for C++11 and later, narrowing conversions get diagnosed by default, just like the standard requires ๐Ÿšซ. A narrowing conversion from a constant is an error, and a narrowing conversion from a non-constant is a warning. -Wno-narrowing silences that diagnostic. Heads up: this doesn't change what well-formed code means, since narrowing conversions are still ill-formed in SFINAE contexts. ๐Ÿ™…โ€โ™€๏ธ

With -Wnarrowing in C++98, you get a warning when a narrowing conversion that C++11 prohibits happens inside { }, like this:

int i = { 2.2 }; // error: narrowing from double to int

This flag is included in -Wall and -Wc++11-compat. ๐Ÿ’–

  • -Wnoexcept (C++ and Objective-C++ only) โ€” warns when a noexcept-expression evaluates to false because of a call to a function that doesn't have a non-throwing exception specification (so no throw() or noexcept), even though the compiler knows it never actually throws an exception ๐Ÿค”.
  • -Wnoexcept-type (C++ and Objective-C++ only) โ€” this warns if the C++17 feature that makes noexcept part of a function type changes the mangled name of a symbol compared to C++14 ๐ŸŒˆ. It's enabled by -Wabi and -Wc++17-compat. Here's the example:
template <class T> void f(T t) { t(); };
void g() noexcept;
void h() { f(g); }

In C++14, f calls f<void(*)()>, but in C++17 it calls f<void(*)()noexcept>. ๐Ÿงš

  • -Wclass-memaccess (C++ and Objective-C++ only) โ€” this warns when the destination of a call to a raw memory function, like memset or memcpy, is an object of class type ๐Ÿ™ˆ. It also warns when writing into that object might sidestep a non-trivial or deleted constructor or copy assignment, break const-correctness or encapsulation, or corrupt virtual table pointers. Messing with the representation of those objects can break invariants that the class's member functions rely on. For example, the memset call below is undefined because it modifies a non-trivial class object, so it gets flagged:
std::string str = "abc";
memset (&str, 0, sizeof str);

The safe move is to initialize or clear those objects with the appropriate constructor or assignment operator, if one exists โœจ. -Wclass-memaccess is enabled by -Wall. Explicitly casting the pointer to the class object to void *, or to a type that the raw memory function can safely access, suppresses the warning.

  • -Wnon-virtual-dtor (C++ and Objective-C++ only) โ€” this warns when a class has virtual functions and an accessible non-virtual destructor, either on itself or on an accessible polymorphic base class ๐Ÿ˜ฌ. That combo makes it possible (but unsafe) to delete a derived-class instance through a pointer to the class or base class. This warning gets turned on automatically when you specify -Weffc++. Honestly, -Wdelete-non-virtual-dtor (enabled by -Wall) is the better pick, because it warns about the unsafe cases without false positives. ๐Ÿ‘‘
  • -Wregister (C++ and Objective-C++ only) โ€” warns on uses of the register storage class specifier, unless it's part of the GNU Explicit Register Variables extension ๐Ÿ—‘๏ธ. register was deprecated in C++11 and removed in C++17. It's enabled by default with -std=c++17.
  • -Wreorder (C++ and Objective-C++ only) โ€” warns when the order of member initializers in your code doesn't match the order they actually have to run in ๐Ÿ”. For instance:
struct A {
  int i;
  int j;
  A(): j (0), i (1) { }
};

The compiler rearranges the member initializers for i and j to match the declaration order of the members, and it emits a warning about that. -Wreorder is enabled by -Wall.

  • -Wno-pessimizing-move (C++ and Objective-C++ only) โ€” this warns when a call to std::move stops copy elision from happening ๐Ÿ™…โ€โ™€๏ธ. A typical case where copy elision can kick in is when you return from a function with a class return type, the expression being returned is the name of a non-volatile automatic object (and not a function parameter), and it has the same type as the function's return type. Check this one out:
struct T {
...
};
T fn()
{
  T t;
  ...
  return std::move (t);
}

In this example, the std::move call prevents copy elision, so you get the warning. This warning is enabled by -Wall. ๐Ÿ’…

  • -Wno-redundant-move (C++ and Objective-C++ only) โ€” this warns about redundant calls to std::move, meaning a move operation would have happened even without the std::move call ๐Ÿซ . That happens because the compiler is forced to treat the object as an rvalue in certain situations, like returning a local variable where copy elision doesn't apply. Consider:
                   struct T {
                   ...
                   };
                   T fn(T t)
                   {
                     ...
                     return std::move (t);
                   }

okay so here the std::move call is redundant ๐Ÿ™„ and bestie, G++ implements Core Issue 1579, so there's another example that's lowkey sneaky:

                   struct T { // convertible to U
                   ...
                   };
                   struct U {
                   ...
                   };
                   U fn()
                   {
                     T t;
                     ...
                     return std::move (t);
                   }

in this one, copy elision isn't applicable because the type of the expression being returned and the function return type differ ๐Ÿ’€ but G++ still treats the return value as if it were designated by an rvalue. it's giving confusing, fr fr ๐ŸŒธ

This warning is enabled by -Wextra โœจ


  • -Wrange-loop-construct (C++ and Objective-C++ only) โ€” the main character of this warning is an unnecessary copy inside a C++ range-based for-loop ๐Ÿฆ‹ it happens when the range declaration isn't a reference, but it probably should be. for example:
                   struct S { char arr[128]; };
                   void fn () {
                     S arr[5];
                     for (const auto x : arr) { ... }
                   }

it does not warn when the type being copied is a trivially-copyable type whose size is less than 64 bytes, so no need to panic over tiny stuff ๐Ÿซถ

this warning also catches a loop variable in a range-based for-loop that gets initialized with a value of a different type, which results in a copy. like:

                   void fn() {
                     int arr[10];
                     for (const double &x : arr) { ... }
                   }

in the example above, every single iteration makes a temporary value of type double that gets created and destroyed, and the reference const double & is bound to it. not it ๐Ÿ˜ญ

This warning is enabled by -Wall ๐Ÿ’œ

  • -Wredundant-tags (C++ and Objective-C++ only) โ€” warns about redundant class-key and enum-key in references to class types and enumerated types, in contexts where the key can be dropped without causing an ambiguity โœจ for example:
                   struct foo;
                   struct foo *p;   // warn that keyword struct can be eliminated

but on the other hand, in this example there is no warning, because the keyword is actually necessary:

                   struct foo;
                   void foo ();   // "hides" struct foo
                   void bar (struct foo&);  // no warning, keyword struct is necessary
  • -Wno-subobject-linkage (C++ and Objective-C++ only) โ€” turns off the warning for when a class type has a base or a field whose type uses the anonymous namespace or depends on a type with no linkage ๐ŸŽ€ if a type A depends on a type B with no or internal linkage, defining it in multiple translation units would be an ODR violation, because the meaning of B is different in each translation unit. if A only shows up in a single translation unit, the best way to silence the warning is to give it internal linkage by putting it in an anonymous namespace as well. the compiler doesn't give this warning for types defined in the main .C file, since those are unlikely to have multiple definitions. -Wsubobject-linkage is enabled by default ๐Ÿงš
  • -Weffc++ (C++ and Objective-C++ only) โ€” warns about violations of the following style guidelines from Scott Meyers' Effective C++ series of books ๐Ÿ“š
    • Define a copy constructor and an assignment operator for classes with dynamically-allocated memory.
    • Prefer initialization to assignment in constructors.
    • Have "operator=" return a reference to *this.
    • Don't try to return a reference when you must return an object.
    • Distinguish between prefix and postfix forms of increment and decrement operators.
    • Never overload "&&", "||", or ",".

this option also enables -Wnon-virtual-dtor, which is also one of the effective C++ recommendations. however, the check is extended to warn about the lack of virtual destructor in accessible non-polymorphic bases classes too ๐Ÿ’…

heads up: when you pick this option, the standard library headers don't obey all of these guidelines, so use grep -v to filter out those warnings ๐Ÿ™ˆ
  • -Wno-exceptions (C++ and Objective-C++ only) โ€” disables the warning for when an exception handler is shadowed by another handler, which can point out a wrong ordering of exception handlers ๐ŸŒˆ
  • -Wstrict-null-sentinel (C++ and Objective-C++ only) โ€” warns about the use of an uncasted "NULL" as sentinel. when compiling only with GCC this is a valid sentinel, since "NULL" is defined to "__null". it's a null pointer constant rather than a null pointer, but it's guaranteed to be the same size as a pointer. BUT this use is not portable across different compilers, so be careful bestie โญ
  • -Wno-non-template-friend (C++ and Objective-C++ only) โ€” disables warnings when non-template friend functions are declared within a template. in very old versions of GCC that predate the implementation of the ISO standard, declarations such as friend int foo(int), where the name of the friend is an unqualified-id, could be interpreted as a particular specialization of a template function. the warning exists to diagnose compatibility problems, and it's enabled by default ๐Ÿ“
  • -Wold-style-cast (C++ and Objective-C++ only) โ€” warns if an old-style (C-style) cast to a non-void type is used within a C++ program ๐Ÿ‘‘ the new-style casts ("dynamic_cast", "static_cast", "reinterpret_cast", and "const_cast") are less vulnerable to unintended effects and way easier to search for, so ngl, use those instead ๐Ÿ’–
  • -Woverloaded-virtual (C++ and Objective-C++ only)
  • -Woverloaded-virtual=n โ€” warns when a function declaration hides virtual functions from a base class ๐Ÿฆ‹ for example, in:
                   struct A {
                     virtual void f();
                   };

                   struct B: public A {
                     void f(int); // does not override
                   };

the "A" class version of "f" is hidden in "B", and code like this:

                   B* b;
                   b->f();

fails to compile ๐Ÿ’€

in cases where the different signatures are not an accident, the simplest solution is to add a using-declaration to the derived class to un-hide the base function, e.g. add "using A::f;" to "B" ๐Ÿซถ

the optional level suffix controls the behavior when all the declarations in the derived class override virtual functions in the base class, even if not all of the base functions are overridden. for example:

                   struct C {
                     virtual void f();
                     virtual void f(int);
                   };

                   struct D: public C {
                     void f(int); // does override
                   }

this pattern is less likely to be a mistake. if D is only used virtually, the user might have decided that the base class semantics for some of the overloads are fine.

at level 1, this case does not warn; at level 2, it does. -Woverloaded-virtual by itself selects level 2. Level 1 is included in -Wall โœจ

  • -Wno-pmf-conversions (C++ and Objective-C++ only) โ€” disables the diagnostic for converting a bound pointer to member function to a plain pointer ๐ŸŒธ
  • -Wsign-promo (C++ and Objective-C++ only) โ€” warns when overload resolution chooses a promotion from an unsigned or enumerated type to a signed type, over a conversion to an unsigned type of the same size. previous versions of G++ tried to preserve unsignedness, but the standard mandates the current behavior ๐Ÿ“œ
  • -Wtemplates (C++ and Objective-C++ only) โ€” warns when a primary template declaration is encountered. some coding rules disallow templates, and this can be used to enforce that rule. the warning is inactive inside a system header file, such as the STL, so you can still use the STL ๐Ÿงš one may also instantiate or specialize templates, no warning there ๐Ÿ’œ
  • -Wmismatched-new-delete (C++ and Objective-C++ only) โ€” warns for mismatches between calls to "operator new" or "operator delete" and the corresponding call to the allocation or deallocation function ๐Ÿ˜ญ this includes invocations of C++ "operator delete" with pointers returned from either mismatched forms of "operator new", or from other functions that allocate objects for which the "operator delete" isn't a suitable deallocator, as well as calls to other deallocation functions with pointers returned from "operator new" for which the deallocation function isn't suitable.

for example, the "delete" expression in the function below gets diagnosed, because it doesn't match the array form of the "new" expression the pointer argument was returned from. similarly, the call to "free" is also diagnosed:

                   void f ()
                   {
                     int *a = new int[n];
                     delete a;   // warning: mismatch in array forms of expressions

                     char *p = new char[n];
                     free (p);   // warning: mismatch between new and free
                   }

the related option -Wmismatched-dealloc diagnoses mismatches involving allocation and deallocation functions other than "operator new" and "operator delete" ๐ŸŽ€

-Wmismatched-new-delete is included in -Wall ๐Ÿ”ฅ

  • -Wmismatched-tags (C++ and Objective-C++ only) โ€” warns for declarations of structs, classes, and class templates and their specializations with a class-key that doesn't match either the definition or the first declaration if no definition is provided ๐Ÿ’…

for example, the declaration of "struct Object" in the argument list of "draw" triggers the warning. to avoid it, either remove the redundant class-key "struct" or replace it with "class" to match its definition:

                   class Object {
                   public:
                     virtual ~Object () = 0;
                   };
                   void draw (struct Object*);

it's not wrong to declare a class with the class-key "struct" as the example above shows ๐Ÿ™ˆ the -Wmismatched-tags option is intended to help achieve a consistent style of class declarations. in code that's meant to be portable to Windows-based compilers, the warning helps prevent unresolved references due to the difference in the mangling of symbols declared with different class-keys ๐ŸŒˆ the option can be used either on its own or in conjunction with -Wredundant-tags ๐Ÿซถ

  • -Wmultiple-inheritance (C++ and Objective-C++ only) โ€” okay so this one warns you when a class is defined with multiple direct base classes ๐Ÿ‘€ Some coding rules ban multiple inheritance, so this is how you enforce that rule ๐Ÿ“ It goes quiet inside system header files like the STL, so you can still use the STL freely ๐Ÿ’– You're also still allowed to define classes that indirectly use multiple inheritance โœจ
  • -Wvirtual-inheritance โ€” this warns when a class is defined with a virtual direct base class ๐Ÿฆ‹ Same deal as above: some coding rules disallow multiple inheritance, and this helps enforce that ๐Ÿ’… It's also silent inside system headers like the STL, and you can still define classes that indirectly use virtual inheritance ๐ŸŒธ
  • -Wno-virtual-move-assign โ€” this one suppresses warnings about inheriting from a virtual base that has a non-trivial C++11 move assignment operator ๐Ÿ™ˆ Heads up, this is a bit of a danger zone ๐Ÿ’€ If the virtual base is reachable along more than one path, it gets moved multiple times, which can leave both objects in the moved-from state ๐Ÿ˜ญ If your move assignment operator is written so it never moves from a moved-from object, you're safe to turn this warning off ๐Ÿซถ
  • -Wnamespaces โ€” warns when a namespace definition is opened ๐Ÿงš Some coding rules say no namespaces, and this is the enforcer ๐Ÿ‘‘ Like the others, it's inactive inside system header files such as the STL, so the STL is still yours ๐Ÿ’œ You can also still use using directives and qualified names ๐ŸŽ€
  • -Wno-template-body (C++ and Objective-C++ only) โ€” this disables diagnosing errors while parsing a template, and instead hits you with an error only when the template actually gets instantiated ๐Ÿซ  It can also downgrade those errors into warnings with Wno-error= or -fpermissive โœจ
  • -Wno-template-id-cdtor (C++ and Objective-C++ only) โ€” this turns off the warning about using a simple-template-id as the declarator-id of a constructor or destructor ๐Ÿ’… That stopped being valid in C++20 thanks to DR 2237, so here's what that looks like:
        template<typename T> struct S {
          S<T>(); // should be S();
          ~S<T>();  // should be ~S();
        };

-Wtemplate-id-cdtor is enabled by default with -std=c++20, and it's also turned on by -Wc++20-compat ๐ŸŒˆ

  • -Wtemplate-names-tu-local โ€” this warns when a template body hides an exposure of a translation-unit-local entity ๐Ÿ” Usually, referring to a translation-unit-local entity (like an internal linkage declaration) inside something emitted into a module's CMI is an error ๐Ÿšจ BUT inside a variable's initializer, or in the body of a non-inline function, it isn't an exposure, so no error gets emitted ๐Ÿ™ƒ

The tricky part: variable or function templates can accidentally become unusable if they reference one of these entities, because other translation units that import the template will never be able to instantiate it ๐Ÿ˜ญ This warning tries to catch cases where that might happen ๐Ÿ•ต๏ธโ€โ™€๏ธ An explicit instantiation silences the warning ๐Ÿคซ

This flag is enabled by -Wextra ๐Ÿ’–

  • -Wno-terminate (C++ and Objective-C++ only) โ€” disables the warning about a throw-expression that will immediately result in a call to "terminate" ๐Ÿ’ฅ
  • -Wno-vexing-parse (C++ and Objective-C++ only) โ€” this warns about the most vexing parse syntactic ambiguity ๐Ÿคฏ It flags declarations that look like variable definitions, but C++ says they have to be read as function declarations ๐Ÿ™ˆ For instance:
        void f(double a) {
          int i();        // extern int i (void);
          int n(int(a));  // extern int n (int);
        }

Another example:

        struct S { S(int); };
        void f(double a) {
          S x(int(a));   // extern struct S x (int);
          S y(int());    // extern struct S y (int (*) (void));
          S z();         // extern struct S z (void);
        }

The warning will suggest ways to deal with the ambiguity, e.g. removing the parentheses or using braces instead โœจ

This warning is enabled by default ๐ŸŒธ

  • -Wno-class-conversion (C++ and Objective-C++ only) โ€” don't warn when a conversion function converts an object to the same type, to a base class of that type, or to void ๐Ÿซง Such a conversion function will never be called, so the warning is kind of pointless ๐Ÿ™„
  • -Wvolatile (C++ and Objective-C++ only) โ€” warns about deprecated uses of the "volatile" qualifier ๐Ÿ“› That includes:
    • postfix and prefix "++" and "--" expressions of "volatile"-qualified types
    • simple assignments where the left operand is a "volatile"-qualified non-class type, using its value
    • compound assignments where the left operand is a "volatile"-qualified non-class type
    • "volatile"-qualified function return types
    • "volatile"-qualified parameter types
    • structured bindings of a "volatile"-qualified type

This usage was deprecated in C++20 ๐Ÿฅฒ

It's enabled by default with -std=c++20. Before -std=c++20, it's enabled with an explicit -Wdeprecated ๐Ÿ’œ

  • -Waligned-new โ€” warns about a new-expression of a type that needs greater alignment than alignof(std::max_align_t) but uses an allocation function without an explicit alignment parameter ๐Ÿ“ This option is enabled by -Wall ๐ŸŽ€

Normally this only warns about global allocation functions, but -Waligned-new=all also warns about class member allocation functions ๐Ÿ‘€

  • -Wno-placement-new and -Wplacement-new=n โ€” warns about placement new expressions with undefined behavior, like constructing an object in a buffer that's smaller than the object's type ๐Ÿ˜ฑ For example, this placement new gets diagnosed because it tries to construct an array of 64 integers in a buffer that's only 64 bytes:
        char buf [64];
        new (buf) int[64];

This warning is enabled by default ๐Ÿซถ

  • -Wplacement-new=1 โ€” this is the default warning level for -Wplacement-new ๐Ÿ’… At this level, the warning isn't issued for some strictly undefined constructs that GCC allows as extensions for compatibility with legacy code ๐Ÿซ  For example, the following "new" expression isn't diagnosed at this level, even though the C++ standard says it's undefined behavior, because it writes past the end of the one-element array:
            struct S { int n, a[1]; };
            S *s = (S *)malloc (sizeof *s + 31 * sizeof s->a[0]);
            new (s->a)int [32]();
  • -Wplacement-new=2 โ€” at this level, GCC diagnoses everything level 1 does, plus placement new expressions that construct an object in the last member of a structure, where that member is an array of a single element and the size is less than the size of the object being constructed ๐Ÿ”ฅ The previous example would be diagnosed here, but the following construct uses the flexible member array extension to dodge the warning at level 2 ๐Ÿฆ‹
            struct S { int n, a[]; };
            S *s = (S *)malloc (sizeof *s + 32 * sizeof s->a[0]);
            new (s->a)int [32]();
  • -Wcatch-value and -Wcatch-value=n (C++ and Objective-C++ only) โ€” warns about catch handlers that don't catch via reference ๐Ÿ™…โ€โ™€๏ธ
    • -Wcatch-value=1 (or just -Wcatch-value for short) warns about polymorphic class types that are caught by value ๐Ÿง
    • -Wcatch-value=2 warns about all class types that are caught by value ๐Ÿ’ซ
    • -Wcatch-value=3 warns about all types that are not caught by reference ๐ŸŒˆ

-Wcatch-value is enabled by -Wall โœจ

  • -Wconditionally-supported (C++ and Objective-C++ only) โ€” warns for conditionally-supported constructs (C++11 [intro.defs]) ๐Ÿงฉ
  • -Wno-defaulted-function-deleted (C++ and Objective-C++ only) โ€” warns when an explicitly defaulted function gets deleted by the compiler ๐Ÿ—‘๏ธ That can happen when the function's declared type doesn't match the type of the function that would have been implicitly declared ๐Ÿ™ˆ This warning is enabled by default ๐ŸŒธ
  • -Wno-delete-incomplete (C++ and Objective-C++ only) โ€” don't warn when deleting a pointer to an incomplete type, which may cause undefined behavior at runtime ๐Ÿ˜ฌ This warning is enabled by default ๐Ÿ’œ
  • -Wextra-semi (C++, Objective-C++ only) โ€” warns about redundant semicolons ๐Ÿ™„ Extra semicolons can pop up in a few different places:
    • a semicolon after in-class function definitions, which is valid in all C++ dialects and is never a pedwarn:
        struct S {
          void foo () {};
        };
  • an extra semicolon at namespace scope, which has been allowed since C++11 (so it's a pedwarn in C++98):
        struct S {
        };
        ;
  • an extra semicolon in class definitions, which has also been allowed since C++11 (so it's a pedwarn in C++98):
        struct S {
          int a;
          ;
        };
  • -Wno-global-module (C++ and Objective-C++ only) โ€” disables the diagnostic for when the global module fragment of a module unit doesn't consist only of preprocessor directives ๐Ÿงš
  • -Wno-inaccessible-base (C++, Objective-C++ only) โ€” this controls warnings when a base class is inaccessible in a class derived from it because of ambiguity ๐Ÿ”Ž The warning is enabled by default โœจ Note that the warning for ambiguous virtual bases is enabled by the -Wextra option ๐Ÿ’…
        struct A { int a; };

        struct B : A { };

        struct C : B, A { };
  • -Wno-inherited-variadic-ctor โ€” suppresses warnings about using C++11 inheriting constructors when the base class being inherited from has a C variadic constructor ๐Ÿ™ƒ The warning is on by default, because the ellipsis is not inherited ๐Ÿซ 
  • -Wno-invalid-offsetof (C++ and Objective-C++ only) โ€” suppresses warnings from applying the "offsetof" macro to a non-POD type ๐Ÿ™ˆ According to the 2014 ISO C++ standard, applying "offsetof" to a non-standard-layout type is undefined ๐Ÿ˜ณ In existing C++ implementations, though, "offsetof" typically gives meaningful results ๐Ÿคทโ€โ™€๏ธ This flag is for users who know they're writing nonportable code and have deliberately chosen to ignore the warning ๐Ÿ’–

The restrictions on "offsetof" may be relaxed in a future version of the C++ standard ๐ŸŒธ

  • -Wsized-deallocation (C++ and Objective-C++ only) โ€” bestie, this one warns when you define an unsized deallocation function but there's no matching sized one (or the other way around) ๐Ÿงนโœจ The unsized version looks like this:
void operator delete (void *) noexcept;
void operator delete[] (void *) noexcept;

and the sized version looks like this:

void operator delete (void *, std::size_t) noexcept;
void operator delete[] (void *, std::size_t) noexcept;

It's enabled by -Wextra along with -fsized-deallocation ๐ŸŽ€

  • -Wsuggest-final-types โ€” this one is giving code-review energy ๐Ÿง It warns about types with virtual methods where your code would be cleaner if the type were declared with the C++11 final specifier or, if possible, declared in an anonymous namespace. That lets GCC devirtualize the polymorphic calls more aggressively, which is lowkey iconic ๐Ÿ’… This warning hits harder with link-time optimization, because the class hierarchy graph is more complete then ๐ŸŒธ
  • -Wsuggest-final-methods โ€” same vibe, but at the method level ๐Ÿฆ‹ It warns about virtual methods that would be better off declared with the C++11 final specifier or, if possible, with their type declared in an anonymous namespace or with the final specifier. Again, this is more effective with link-time optimization, when the class hierarchy graph is more complete ๐Ÿ‘‘ Pro tip from the manual (not me, the manual ๐Ÿ™ˆ): look at the -Wsuggest-final-types suggestions first, then rebuild with the new annotations โœจ
  • -Wsuggest-override โ€” warns about overriding virtual functions that aren't marked with the override keyword. No sneaky overrides allowed, periodt ๐Ÿ’œ
  • -Wno-conversion-null (C++ and Objective-C++ only) โ€” this one turns off warnings for conversions between NULL and non-pointer types ๐Ÿ™…โ€โ™€๏ธ Heads up, -Wconversion-null is enabled by default, so you'd have to ask for this one specifically ๐Ÿฅบ

โœจ Options Controlling Objective-C and Objective-C++ Dialects โœจ

(Quick note first, okay? ๐Ÿ“ This manual does not describe the Objective-C and Objective-C++ languages themselves. This section is only about the command-line options that are meaningful for Objective-C and Objective-C++ programs. You can also use most of the language-independent GNU compiler options. For example, you might compile a file some_class.m like this:)

gcc -g -fgnu-runtime -O -c some_class.m

In this example, -fgnu-runtime is an option meant only for Objective-C and Objective-C++ programs. You can use the other options with any language GCC supports ๐ŸŒˆ

Since Objective-C is an extension of the C language, Objective-C compilations may also use options specific to the C front-end (e.g., -Wtraditional). Similarly, Objective-C++ compilations may use C++-specific options (e.g., -Wabi) ๐Ÿซถ

Here's the list of options that are only for compiling Objective-C and Objective-C++ programs ๐Ÿ’–

  • -fconstant-string-class=class-name โ€” tells GCC which class to instantiate for each literal string written with the @"..." syntax ๐Ÿง Use class-name as that class. The default is NXConstantString if the GNU runtime is being used, and NSConstantString if the NeXT runtime is being used (see below). On Darwin / macOS platforms, the -fconstant-cfstrings option, if it's also present, overrides the -fconstant-string-class setting and causes @"..." literals to be laid out as constant CoreFoundation strings. Also, -fconstant-cfstrings is an alias for the target-specific -mconstant-cfstrings equivalent ๐Ÿ’…
  • -fgnu-runtime โ€” generates object code compatible with the standard GNU Objective-C runtime. This is the default on most types of systems ๐Ÿฃ
  • -fnext-runtime โ€” generates output compatible with the NeXT runtime. This is the default on NeXT-based systems, including Darwin / macOS ๐ŸŽ The macro __NEXT_RUNTIME__ is predefined if, and only if, this option is used โœจ
  • -fno-nil-receivers โ€” assumes that all Objective-C message dispatches ([receiver message:arg]) in this translation unit make sure the receiver is not nil. That lets the runtime use more efficient entry points ๐Ÿƒโ€โ™€๏ธ This option is only available together with the NeXT runtime and ABI version 0 or 1.
  • -fobjc-abi-version=n โ€” uses version n of the Objective-C ABI for the selected runtime ๐ŸŽ€ Right now this is only supported for the NeXT runtime. In that case, version 0 is the traditional (32-bit) ABI without support for properties and other Objective-C 2.0 additions. Version 1 is the traditional (32-bit) ABI with support for properties and other Objective-C 2.0 additions. Version 2 is the modern (64-bit) ABI. If you don't specify anything, the default is version 0 on 32-bit target machines, and version 2 on 64-bit target machines ๐Ÿ’œ
  • -fobjc-call-cxx-cdtors โ€” for each Objective-C class, GCC checks whether any of its instance variables is a C++ object with a non-trivial default constructor. If so, it synthesizes a special - (id) .cxx_construct instance method, which runs those non-trivial default constructors on the instance variables, in order, and then returns self. It also checks whether any instance variable is a C++ object with a non-trivial destructor, and if so, synthesizes a special - (void) .cxx_destruct method, which runs all those default destructors in reverse order ๐Ÿงจ

The - (id) .cxx_construct and - (void) .cxx_destruct methods generated this way only operate on instance variables declared in the current Objective-C class, not the ones inherited from superclasses. It's the Objective-C runtime's responsibility to invoke all of these methods across an object's inheritance hierarchy. The - (id) .cxx_construct methods get invoked by the runtime right after a new object instance is allocated, and the - (void) .cxx_destruct methods get invoked right before the runtime deallocates an object instance ๐Ÿซง

As of this writing, only the NeXT runtime on Mac OS X 10.4 and later can invoke the - (id) .cxx_construct and - (void) .cxx_destruct methods ๐Ÿ

  • -fobjc-direct-dispatch โ€” allows fast jumps to the message dispatcher. On Darwin, this is done via the comm page ๐Ÿš€
  • -fobjc-exceptions โ€” enables syntactic support for structured exception handling in Objective-C, similar to what C++ offers ๐Ÿ™Œ This option is required to use the Objective-C keywords @try, @throw, @catch, @finally and @synchronized. It's available with both the GNU runtime and the NeXT runtime, but it's not available together with the NeXT runtime on Mac OS X 10.2 and earlier ๐Ÿ’€
  • -fobjc-gc โ€” enables garbage collection (GC) in Objective-C and Objective-C++ programs ๐Ÿ—‘๏ธ This is only available with the NeXT runtime. The GNU runtime has a different garbage collection implementation that doesn't need any special compiler flags ๐Ÿงน
  • -fobjc-nilcheck โ€” for the NeXT runtime with version 2 of the ABI, checks for a nil receiver in method invocations before doing the actual method call ๐Ÿ” This is the default, and you can turn it off with -fno-objc-nilcheck. Class methods and super calls are never checked for nil this way, no matter what this flag is set to. Right now this flag does nothing when the GNU runtime, or an older version of the NeXT runtime ABI, is used ๐Ÿ™ƒ
  • -fobjc-std=objc1 โ€” conforms to the language syntax of Objective-C 1.0, the language recognized by GCC 4.0 ๐Ÿ“œ This only affects the Objective-C additions to the C/C++ language. It doesn't change conformance to C/C++ standards, which is controlled by the separate C/C++ dialect option flags. When you use this option with the Objective-C or Objective-C++ compiler, any Objective-C syntax that GCC 4.0 doesn't recognize gets rejected. It's useful if you need to make sure your Objective-C code can be compiled with older versions of GCC ๐Ÿ•ฐ๏ธ
  • -freplace-objc-classes โ€” emits a special marker that tells ld(1) not to statically link the resulting object file, and lets dyld(1) load it at run time instead ๐Ÿ” This is used with the Fix-and-Continue debugging mode, where the object file may be recompiled and dynamically reloaded during program execution, without restarting the program itself. Right now, Fix-and-Continue is only available together with the NeXT runtime on Mac OS X 10.3 and later โœจ
  • -fzero-link โ€” when compiling for the NeXT runtime, the compiler normally replaces calls to objc_getClass("...") (when the class name is known at compile time) with static class references that get initialized at load time, which is better for run-time performance ๐ŸŽ๏ธ Specifying -fzero-link suppresses that behavior and keeps the calls to objc_getClass("..."). This is useful in Zero-Link debugging mode, since it lets you modify individual class implementations during program execution. The GNU runtime currently always keeps calls to objc_get_class("...") regardless of command-line options ๐Ÿซ 
  • -fno-local-ivars โ€” by default, instance variables in Objective-C can be accessed as if they were local variables from within the methods of the class they're declared in. That can cause shadowing between instance variables and other variables declared locally inside a class method, or globally, with the same name ๐Ÿ‘€ Specifying -fno-local-ivars disables this behavior, which avoids those variable shadowing issues ๐ŸŒท
  • -fivar-visibility=[public|protected|private|package] โ€” sets the default instance variable visibility to the option you choose, so that instance variables declared outside the scope of any access modifier directive default to that visibility ๐Ÿ”’
  • -gen-decls โ€” dumps interface declarations for all classes seen in the source file to a file named sourcename.decl ๐Ÿ“‚
  • -Wassign-intercept (Objective-C and Objective-C++ only) โ€” warns whenever an Objective-C assignment is being intercepted by the garbage collector ๐Ÿšจ
  • -Wno-property-assign-default (Objective-C and Objective-C++ only) โ€” doesn't warn if a property for an Objective-C object has no assign semantics specified ๐Ÿคซ

Okay so bestie, we're picking up right where the Objective-C warnings get spicy ๐Ÿ™ˆ

  • -Wno-protocol (Objective-C and Objective-C++ only) โ€” okay so if a class says it implements a protocol, GCC yells at you for every protocol method the class doesn't implement ๐Ÿ˜ค By default it even yells about methods the class inherits from its superclass without explicitly implementing them. But if you use -Wno-protocol, inherited methods count as implemented and GCC stays quiet about them ๐Ÿซถ
  • -Wobjc-root-class (Objective-C and Objective-C++ only) โ€” warns if a class interface has no superclass. Most classes inherit from "NSObject" (or "Object"), for example ๐Ÿ’… If you're making a root class on purpose, you can shut the warning up by marking its interface with "__attribute__((objc_root_class))" โœจ
  • -Wselector (Objective-C and Objective-C++ only) โ€” warns if you end up with multiple methods of different types for the same selector during compilation ๐Ÿ‘€ It checks the method list at the very final stage of compilation, and it also checks each selector used in a @selector(...) expression that has a matching method somewhere in the compilation. Because all of this happens only at the end, these warnings won't show up if the final stage never gets reached, like when there's an error earlier on or when you're using -fsyntax-only ๐Ÿ’€
  • -Wstrict-selector-match (Objective-C and Objective-C++ only) โ€” warns if a selector has multiple methods with different argument and/or return types when you're sending a message to a receiver of type "id" or "Class" ๐Ÿ”ฅ This is off by default, and when it's off GCC skips the warning if the differences are only between types that share the same size and alignment.
  • -Wundeclared-selector (Objective-C and Objective-C++ only) โ€” warns if you use a @selector(...) expression for a selector that hasn't been declared yet ๐Ÿ™…โ€โ™€๏ธ A selector counts as undeclared if no method with that name was declared before the @selector(...) expression, either explicitly in an @interface or @protocol declaration, or implicitly in an @implementation section. This one checks the moment it spots a @selector(...) expression, while -Wselector waits for the final stage. It also enforces the coding style rule that methods and selectors must be declared before you use them ๐Ÿงš
  • -print-objc-runtime-info โ€” generates a C header describing the largest structure that gets passed by value, if there is one ๐ŸŒธ

๐ŸŒˆ Options Controlling OpenMP and OpenACC ๐ŸŒˆ

okay so GCC supports OpenMP extensions for C, C++, and Fortran with -fopenmp โœจ And OpenACC extensions work in all three languages with -fopenacc ๐Ÿ’–

  • -foffload=disable, -foffload=default, -foffload=target-list โ€” this is where you pick which OpenMP and OpenACC offload targets get code generated for them ๐ŸŽฏ The default, which is the same as -foffload=default, generates code for all supported offload targets. -foffload=disable only generates code for the host fallback, and -foffload=target-list only generates code for the comma-separated list of offload targets you name ๐Ÿ’…

Offload targets are written in GCC's internal target-triplet format ๐Ÿงš If you want to see the list of configured offload targets, run the compiler with -v and look for "OFFLOAD_TARGET_NAMES" ๐Ÿ‘€

  • -foffload-options=options, -foffload-options=target-triplet-list=options โ€” with -foffload-options=options, GCC passes your options to the compilers for every enabled offloading target ๐Ÿซถ If you only want options for specific targets, use the -foffload-options=target-list=options form instead. The target-list is a comma-separated list in the same format as the -foffload= option.

Typical command lines look like:

-foffload-options='-fno-math-errno -ffinite-math-only' \
        -foffload-options=nvptx-none=-latomic
-foffload-options=amdgcn-amdhsa=-march=gfx906
  • -fopenacc โ€” enables handling of OpenACC directives: #pragma acc in C/C++, and !$acc in free-form Fortran, plus !$acc, c$acc, and *$acc in fixed-form Fortran ๐Ÿ”ฅ Heads up: this option implies -pthread, so it's only supported on targets that support -pthread โš ๏ธ
  • -fopenacc-dim=geom โ€” sets default compute dimensions for parallel offload regions that don't specify their own ๐Ÿ’œ The geom value is a triple of sizes separated by colons, in the order gang, worker, and vector. You can leave a size out to use a target-specific default value โœจ
  • -fopenmp โ€” enables handling of OpenMP directives in C/C++: #pragma omp, [[omp::directive(...)]], [[omp::decl(...)]], and [[omp::sequence(...)]] ๐ŸŽ€ In Fortran it turns on !$omp and the conditional compilation sentinel !$. In fixed source form Fortran, the sentinels can also start with c or *.

This option implies -pthread, so it's only supported on targets that support -pthread. Also, -fopenmp implies -fopenmp-simd ๐Ÿฆ‹

  • -fopenmp-simd โ€” enables handling of OpenMP's "simd", "declare simd", "declare reduction", "assume", "ordered", "scan", and "loop" directives, plus combined or composite directives that have "simd" as a constituent, using #pragma omp, [[omp::directive(...)]], [[omp::sequence(...)]], and [[omp::decl(...)]] in C/C++, and !$omp in Fortran ๐ŸŒธ It also turns on the conditional compilation sentinel !$ in Fortran, and in fixed source form Fortran the sentinels can start with c or * too. Other OpenMP directives get ignored ๐Ÿ™ˆ

Unless you also specify -fopenmp, the "loop" region binds to the current task region, no matter what the "bind" clause says ๐Ÿงš

  • -fopenmp-target-simd-clone, -fopenmp-target-simd-clone=device-type โ€” besides making SIMD clones for functions marked with "declare simd", GCC also makes clones for functions marked with the OpenMP "declare target" directive that are good candidates for vectorization while this option is active ๐Ÿ“ The device-type can be "none", "host", "nohost", or "any". These match the keywords for the "device_type" clause of the "declare target" directive, and clones get generated for the intersection of the devices you specify. -fopenmp-target-simd-clone is the same as -fopenmp-target-simd-clone=any, and -fno-openmp-target-simd-clone is the same as -fopenmp-target-simd-clone=none ๐Ÿ’œ

At -O2 and higher (but not -Os or -Og), this optimization defaults to -fopenmp-target-simd-clone=nohost. Otherwise it's disabled by default ๐Ÿ˜ญ

๐Ÿ’– Options to Control Diagnostic Messages Formatting ๐Ÿ’–

Traditionally, diagnostic messages got formatted without caring what the output device looked like (its width, and so on) ๐Ÿ™„ The options below let you control the formatting algorithm, like how many characters go on each line and how often source location info gets reported. Heads up that some language front ends might not honor these options ๐Ÿซถ

  • -fmessage-length=n โ€” tries to format error messages so they fit on lines about n characters long โœจ If n is zero, no line-wrapping happens and each error message sits on a single line. This is the default for all front ends.

Note: this option also affects the display of the #error and #warning pre-processor directives, and the deprecated function/type/variable attribute. It does not affect the pragma GCC warning and pragma GCC error pragmas ๐Ÿ™…โ€โ™€๏ธ

  • -fdiagnostics-plain-output โ€” requests that diagnostic output look as plain as possible ๐Ÿค This can be handy when you're running dejagnu or other utilities that parse diagnostics output and need it to stay stable over time. -fdiagnostics-plain-output is currently equivalent to all of these:
    • -fno-diagnostics-show-caret
    • -fno-diagnostics-show-line-numbers
    • -fdiagnostics-color=never
    • -fdiagnostics-urls=never
    • -fdiagnostics-path-format=separate-events
    • -fdiagnostics-text-art-charset=none
    • -fno-diagnostics-show-event-links

If GCC ever changes the default look of its diagnostics in the future, the matching option to turn off the new behavior will get added to this list ๐Ÿ”ฎ

  • -fdiagnostics-show-location=once โ€” only matters in line-wrapping mode ๐ŸŒท It tells the diagnostic reporter to emit source location info once. So if a message is too long for one physical line and has to wrap, the location won't get repeated as a prefix on every continuation line. This is the default behavior โœจ
  • -fdiagnostics-show-location=every-line โ€” also only matters in line-wrapping mode ๐ŸŒˆ This one tells the reporter to emit the same source location info (as a prefix) for every physical line that comes out of breaking up a message that's too long for one line ๐Ÿงต
  • -fdiagnostics-color[=WHEN], -fno-diagnostics-color โ€” uses color in diagnostics ๐ŸŽจ WHEN is never, always, or auto. The default depends on how the compiler was configured. It can be any of those WHEN options, or never if the GCC_COLORS environment variable isn't present, and auto otherwise. auto makes GCC use color only when standard error is a terminal and it's not running inside an emacs shell ๐Ÿ’… The forms -fdiagnostics-color and -fno-diagnostics-color are aliases for -fdiagnostics-color=always and -fdiagnostics-color=never, respectively ๐Ÿ’œ

Okay so the colors are controlled by the environment variable GCC_COLORS ๐ŸŒˆ Its value is a colon-separated list of capabilities and Select Graphic Rendition (SGR) substrings ๐Ÿ’… SGR commands get interpreted by your terminal or terminal emulator, so if you want the permitted values and what they mean as character attributes, check the section in your text terminal's documentation ๐Ÿค“ These substring values are integers written in decimal, and you can stack them together with semicolons โœจ

Common values to stack include 1 for bold, 4 for underline, 5 for blink, 7 for inverse, 39 for default foreground color, 30 to 37 for foreground colors, 90 to 97 for 16-color mode foreground colors, 38;5;0 to 38;5;255 for 88-color and 256-color mode foreground colors, 49 for default background color, 40 to 47 for background colors, 100 to 107 for 16-color mode background colors, and 48;5;0 to 48;5;255 for 88-color and 256-color mode background colors ๐ŸŽ€

The default GCC_COLORS is this bad boy ๐Ÿ‘‘

error=01;31:warning=01;35:note=01;36:range1=32:range2=34:locus=01:\
quote=01:path=01;36:fixit-insert=32:fixit-delete=31:\
diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
type-diff=01;32:fnname=01;32:targs=35:valid=01;31:invalid=01;32\
highlight-a=01;32:highlight-b=01;34

So 01;31 is bold red ๐Ÿ”ฅ, 01;35 is bold magenta ๐Ÿ’œ, 01;36 is bold cyan ๐Ÿฉต, 32 is green ๐Ÿ€, 34 is blue ๐Ÿฆ‹, 01 is bold, and 31 is red. Setting GCC_COLORS to the empty string turns colors off completely ๐Ÿ™ˆ These are the capabilities you can set, bestie ๐Ÿซถ

  • error= โ€” the SGR substring for error: markers ๐Ÿ’€
  • warning= โ€” the SGR substring for warning: markers โš ๏ธ
  • note= โ€” the SGR substring for note: markers ๐Ÿ“
  • path= โ€” the SGR substring for coloring paths of control-flow events, the ones printed via -fdiagnostics-path-format=. This covers the identifiers of individual events and the lines showing interprocedural calls and returns ๐Ÿ›ค๏ธ
  • range1= โ€” the SGR substring for the first additional range ๐ŸŽˆ
  • range2= โ€” the SGR substring for the second additional range ๐ŸŽˆ
  • locus= โ€” the SGR substring for location info, like file:line or file:line:column etc ๐Ÿ“
  • quote= โ€” the SGR substring for anything printed inside quotes ๐Ÿ’ฌ
  • fnname= โ€” the SGR substring for names of C++ functions ๐Ÿงš
  • targs= โ€” the SGR substring for C++ function template parameter bindings ๐ŸŒธ
  • fixit-insert= โ€” the SGR substring for fix-it hints that suggest text to be inserted or replaced โž•
  • fixit-delete= โ€” the SGR substring for fix-it hints that suggest text to be deleted โž–
  • diff-filename= โ€” the SGR substring for filename headers inside generated patches ๐Ÿ“„
  • diff-hunk= โ€” the SGR substring for the starts of hunks inside generated patches ๐Ÿงฉ
  • diff-delete= โ€” the SGR substring for deleted lines inside generated patches ๐Ÿ—‘๏ธ
  • diff-insert= โ€” the SGR substring for inserted lines inside generated patches ๐ŸŒฑ
  • type-diff= โ€” the SGR substring for highlighting mismatching types within template arguments in the C++ frontend ๐Ÿ™…โ€โ™€๏ธ
  • valid= โ€” the SGR substring for highlighting valid elements within text art diagrams โœ…
  • invalid= โ€” the SGR substring for highlighting invalid elements within text art diagrams โŒ
  • highlight-a=, highlight-b= โ€” the SGR substrings for contrasting two different things inside diagnostics, like a pair of mismatching types. Check out -fdiagnostics-show-highlight-colors for more ๐ŸŒˆ

โœจ -fdiagnostics-urls[=WHEN] โœจ

Uses escape sequences to embed URLs in diagnostics ๐Ÿ”— For example, when -fdiagnostics-show-option prints the command-line option controlling a diagnostic, it can embed a URL pointing at the docs for that option ๐Ÿ“š

WHEN can be never, always, or auto ๐Ÿ’– auto makes GCC use URL escape sequences only when standard error is a terminal, and only when it's not running in an emacs shell or any graphical terminal known to be incompatible with this feature (more on that below) ๐Ÿ™ˆ

The default depends on how the compiler was configured ๐Ÿ› ๏ธ It can be any of the above WHEN options ๐Ÿ’…

GCC can also be configured (through the --with-diagnostics-urls=auto-if-env configure-time option) so the default depends on environment variables ๐Ÿ‘€ In that setup, GCC defaults to auto if either the GCC_URLS or TERM_URLS environment variables are present and non-empty in the compiler's environment, and to never if neither one is ๐ŸŒธ

Even with -fdiagnostics-urls=always, the behavior still depends on those environment variables, though ๐Ÿ˜ญ If GCC_URLS is set to empty or no, URLs are not embedded in diagnostics. If it's set to st, URLs use ST escape sequences. If it's set to bel, which is the default, URLs use BEL escape sequences. Any other non-empty value enables the feature ๐ŸŽ‰ If GCC_URLS isn't set, TERM_URLS is used as a fallback ๐Ÿ” Quick note: ST is an ANSI escape sequence whose string terminator is ESC \, and BEL is an ASCII character (CTRL-G) that usually sounds like a beep ๐Ÿ””

Right now GCC also tries to detect a few terminals that don't implement the URL feature and that have bugs, or at least had bugs in versions still in use. With those, URL escapes are likely to misbehave and print garbage on the screen ๐Ÿ—‘๏ธ That list is currently xfce4-terminal, certain known-buggy gnome-terminal versions, the linux console, and mingw ๐Ÿ’€ You can skip this check with -fdiagnostics-urls=always ๐Ÿซก

โœจ -fno-diagnostics-show-option โœจ

By default, each diagnostic includes text naming the command-line option that directly controls it (if that option is known to the diagnostic machinery) ๐Ÿ“ฃ Passing -fno-diagnostics-show-option suppresses that behavior ๐Ÿ™Š

โœจ -fno-diagnostics-show-caret โœจ

By default, each diagnostic includes the original source line and a caret ^ pointing at the column ๐ŸŽฏ This option suppresses that info. If -fmessage-length=n is given, the source line gets truncated to n characters โœ‚๏ธ When the output goes to a terminal, the width is limited to whatever the COLUMNS environment variable says or, if it isn't set, to the terminal width ๐Ÿ“

โœจ -fno-diagnostics-show-labels โœจ

By default, when printing source code (via -fdiagnostics-show-caret), diagnostics can label ranges of source with pertinent info, like the types of expressions ๐Ÿท๏ธ

            printf ("foo %s bar", long_i + long_j);
                         ~^       ~~~~~~~~~~~~~~~
                          |              |
                          char *         long int

This option stops those labels from printing (in the example above, that's the vertical bars and the "char *" and "long int" text) ๐Ÿ™…โ€โ™€๏ธ

By default, when printing execution paths (via -fdiagnostics-path-format=inline-events), GCC prints lines connecting related events, like the line connecting events 1 and 2 in this example ๐Ÿ›ค๏ธ

            3 |   if (p)
              |       ^
              |       |
              |       (1) following `false' branch (when `p' is NULL)... ->-+
              |                                                             |
              |                                                             |
              |+------------------------------------------------------------+
            4 ||    return 0;
            5 ||  return *p;
              ||         ~
              ||         |
              |+-------->(2) ...to here
              |          (3) dereference of NULL `p'

This option suppresses those connector lines ๐Ÿ™ˆ

โœจ -fno-diagnostics-show-cwe โœจ

Diagnostic messages can optionally carry an associated CWE ("https://cwe.mitre.org/index.html") identifier ๐Ÿชฒ GCC itself only provides this metadata for some of the -fanalyzer diagnostics, and GCC plugins may also provide diagnostics with it ๐Ÿ”Œ By default, if the info is present, it gets printed along with the diagnostic. This option suppresses printing of this metadata ๐Ÿซถ

โœจ -fno-diagnostics-show-rules โœจ

Diagnostic messages can optionally have rules attached, like from a coding standard or a specification ๐Ÿ“œ GCC itself doesn't do this for any of its diagnostics, but plugins may ๐Ÿงฉ By default, if the info is present, it gets printed with the diagnostic. This option suppresses printing of this metadata ๐Ÿ’…

โœจ -fno-diagnostics-show-highlight-colors โœจ

GCC can use color for emphasis and contrast when printing diagnostic messages and quoting your source ๐ŸŒˆ

For example, in this one:

demo.c: In function `test_bad_format_string_args':
../../src/demo.c:25:18: warning: format `%i' expects argument of type `int', but argument 2 has type `const char *' [-Wformat=]
   25 |   printf("hello %i", msg);
      |                 ~^   ~~~
      |                  |   |
      |                  int const char *
      |                 %s
  • the %i and "int" in the message, and the "int" in the quoted source, are colored using highlight-a (bold green by default), and
  • the "const char *" in the message and in the quoted source are both colored using highlight-b (bold blue by default).

The whole point is to draw your eyes to how the different parts of the message relate to the source ๐Ÿ‘€ Color groups related stuff together and makes mismatched things stand out ๐Ÿ’–

This extra colorization is turned on by default whenever color printing is enabled (as per -fdiagnostics-color=), but you can turn it off separately with -fno-diagnostics-show-highlight-colors ๐Ÿซถ

โœจ -fno-diagnostics-show-line-numbers โœจ

By default, when printing source code (via -fdiagnostics-show-caret), a left margin showing line numbers gets printed ๐Ÿ”ข This option suppresses that left margin ๐Ÿ™ˆ

โœจ -fdiagnostics-minimum-margin-width=width โœจ

This option controls the minimum width of the left margin printed by -fdiagnostics-show-line-numbers ๐Ÿ“ It defaults to 6 ๐Ÿ“

  • -fdiagnostics-parseable-fixits โ€” okay so this one is for the IDEs ๐Ÿ’ปโœจ It spits out fix-it hints in a format a machine can actually read. After each relevant diagnostic you get a line that starts with fix-it:, like this:
fix-it:"test.c":{45:3-45:21}:"gtk_widget_show_all"

The location is a half-open range counted in bytes, and the column count starts at byte 1. So in that example, bytes 3 through 20 of line 45 in "test.c" get swapped out for the replacement string. Picture it like this ๐Ÿ‘€

00000000011111111112222222222
12345678901234567890123456789
  gtk_widget_showall (dlg);
  ^^^^^^^^^^^^^^^^^^
  gtk_widget_show_all

The filename and replacement string escape backslash as "\\", tab as "\t", newline as "\n", and double quotes as "\"". Anything non-printable gets written in octal, so vertical tab becomes "\013" ๐Ÿซถ

An empty replacement string means "delete this range." An empty range, like "45:3-45:3", means "insert the string right here." ๐ŸŒธ

  • -fdiagnostics-generate-patch โ€” this one prints the fix-it hints to stderr as a unified diff, after all the diagnostics have been printed ๐ŸŽ€ Something like this:
--- test.c
+++ test.c
@ -42,5 +42,5 @

 void show_cb(GtkDialog *dlg)
 {
-  gtk_widget_showall(dlg);
+  gtk_widget_show_all(dlg);
 }

The diff may or may not be colorized. It follows the same rules as diagnostics, so go check -fdiagnostics-color if you want the details ๐ŸŒˆ

  • -fdiagnostics-show-template-tree โ€” in the C++ frontend, when you get those mismatching template type errors, this flag turns on a tree view that shows which parts match and which parts don't ๐ŸŒณ Without it, you get something like:
could not convert 'std::map<int, std::vector<double> >()'
  from 'map<[...],vector<double>>' to 'map<[...],vector<float>>

With it, you get a tree-like structure showing the common and differing parts, like:

map<
  [...],
  vector<
    [double != float]>>

The parts that differ get highlighted with color, so in this case "double" and "float" pop out ๐Ÿ’…

  • -fno-elide-type โ€” okay so by default, when the C++ frontend shows mismatching template types, it hides the shared bits and prints them as "[...]" to keep the message short ๐Ÿ™ˆ Here's the default:
could not convert 'std::map<int, std::vector<double> >()'
  from 'map<[...],vector<double>>' to 'map<[...],vector<float>>

If you add -fno-elide-type, GCC stops doing that. This flag also changes what -fdiagnostics-show-template-tree shows, so they're connected ๐Ÿ”—

  • -fdiagnostics-path-format=KIND โ€” this tells GCC how to print the control-flow paths attached to a diagnostic ๐Ÿ›ค๏ธ KIND can be none, separate-events, or inline-events, and inline-events is the default โœจ

none means don't print diagnostic paths at all. Bye bye, paths ๐Ÿ‘‹

separate-events prints a separate note diagnostic for each event in the path. It looks like this:

test.c:29:5: error: passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter
test.c:25:10: note: (1) when 'PyList_New' fails, returning NULL
test.c:27:3: note: (2) when 'i < count'
test.c:29:5: note: (3) when calling 'PyList_Append', passing NULL from (1) as argument 1

inline-events prints the events right inside the source code. It tries to group events that are close together into runs and shows them as labelled ranges, which is so cute ๐Ÿฆ‹ The same events as above would look like:

  'test': events 1-3
   25 |   list = PyList_New(0);
      |          ^~~~~~~~~~~~~
      |          |
      |          (1) when 'PyList_New' fails, returning NULL
   26 |
   27 |   for (i = 0; i < count; i++) {
      |   ~~~
      |   |
      |   (2) when 'i < count'
   28 |     item = PyLong_FromLong(random());
   29 |     PyList_Append(list, item);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~
      |     |
      |     (3) when calling 'PyList_Append', passing NULL from (1) as argument 1

Interprocedural control flow (events that jump between functions) gets grouped by stack frame, and indentation shows how the frames are nested, pushed, and popped ๐Ÿช† For example:

  'test': events 1-2
    |
    |  133 | {
    |      | ^
    |      | |
    |      | (1) entering 'test'
    |  134 |   boxed_int *obj = make_boxed_int (i);
    |      |                    ~~~~~~~~~~~~~~~~~~
    |      |                    |
    |      |                    (2) calling 'make_boxed_int'
    |
    +--> 'make_boxed_int': events 3-4
           |
           |  120 | {
           |      | ^
           |      | |
           |      | (3) entering 'make_boxed_int'
           |  121 |   boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
           |      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |                                    |
           |      |                                    (4) calling 'wrapped_malloc'
           |
           +--> 'wrapped_malloc': events 5-6
                  |
                  |    7 | {
                  |      | ^
                  |      | |
                  |      | (5) entering 'wrapped_malloc'
                  |    8 |   return malloc (size);
                  |      |          ~~~~~~~~~~~~~
                  |      |          |
                  |      |          (6) calling 'malloc'
                  |
    <-------------+
    |
  'test': event 7
    |
    |  138 |   free_boxed_int (obj);
    |      |   ^~~~~~~~~~~~~~~~~~~~
    |      |   |
    |      |   (7) calling 'free_boxed_int'
    |
  (etc)
  • -fdiagnostics-show-path-depths โ€” this one adds extra info when printing control-flow paths for a diagnostic ๐Ÿ” If you turn it on, the stack depth gets printed for each run of events within -fdiagnostics-path-format=inline-events. If you use it together with -fdiagnostics-path-format=separate-events, then the stack depth and function declaration get tacked onto each event instead.

This one is mainly for GCC developers and plugin developers who are debugging diagnostics that report interprocedural control flow. Not really for the rest of us, bestie ๐Ÿงš

  • -fno-show-column โ€” do not print column numbers in diagnostics ๐Ÿ™…โ€โ™€๏ธ This might be necessary if the diagnostics are being scanned by a program that doesn't understand column numbers, like dejagnu.
  • -fdiagnostics-column-unit=UNIT โ€” this picks the units used for column numbers ๐Ÿ“ It affects the traditional diagnostics (as long as you haven't used -fno-show-column) and also the JSON format diagnostics, if you asked for those.

The default UNIT is display, which counts the number of display columns each character takes up. That can be bigger than the byte count, like with tab characters, or smaller, like with multibyte characters. For example, "GREEK SMALL LETTER PI (U+03C0)" takes up one display column, but its UTF-8 encoding is two bytes. "SLIGHTLY SMILING FACE (U+1F642)" takes up two display columns, and its UTF-8 encoding is four bytes ๐Ÿ™‚

If you set UNIT to byte, the column number becomes the raw byte count in every case. That's how GCC did it before version 11.1.0, so it's the old-school vibe ๐Ÿ“œ

  • -fdiagnostics-column-origin=ORIGIN โ€” this chooses the origin for column numbers, meaning which number the very first column gets ๐Ÿ”ข The default of 1 matches traditional GCC behavior and the GNU style guide. Some utilities might work better with an origin of 0, and any non-negative value is allowed.
  • -fdiagnostics-escape-format=FORMAT โ€” okay so when GCC prints the relevant source lines for a diagnostic, it normally tries to print the source bytes straight up ๐Ÿ–จ๏ธ But some diagnostics are about encoding problems in the source file, like malformed UTF-8 or Unicode normalization issues. Those get flagged so GCC escapes any bytes that aren't printable ASCII when it prints those lines.

This option controls how those bytes get escaped.

The default FORMAT is unicode. It shows non-printable-ASCII Unicode characters in the form <U+XXXX>. Bytes that don't correspond to a Unicode character validly encoded in UTF-8 get shown as hexadecimal in the form <XX> ๐ŸŒธ

For example, say a source line has the string before, followed by the Unicode character U+03C0 ("GREEK SMALL LETTER PI", with UTF-8 encoding 0xCF 0x80), followed by the byte 0xBF (a stray UTF-8 trailing byte), followed by the string after. For that diagnostic, it gets printed as:

before<U+03C0><BF>after

If you set FORMAT to bytes, every non-printable-ASCII byte shows up as <XX>. This lets you see the underlying encoding of non-ASCII Unicode characters. For the same example, you'd get:

before<CF><80><BF>after
  • -fdiagnostics-text-art-charset=CHARSET โ€” some diagnostics include "text art" diagrams, which are pictures made out of text meant to be viewed in a monospaced font ๐ŸŽจ

This option picks which characters get used to draw those diagrams, if any. CHARSET can be none, ascii, unicode, or emoji ๐ŸŒˆ

The none value turns those little diagrams off completely ๐Ÿ™ˆ. ascii makes sure every diagram is pure ASCII, aka "ASCII art" ๐ŸŽจ. unicode lets it use a conservative amount of unicode drawing characters, like box-drawing characters ๐Ÿ’…. And emoji goes full glam and adds emoji to the output โœจ, such as printing U+26A0 WARNING SIGN followed by U+FE0F VARIATION SELECTOR-16 to pick the emoji version of the character ๐Ÿซถ.

The default is emoji ๐ŸŒˆ, except when the environment variable LANG is set to C. Then it's ascii instead, because that's the safe choice ๐Ÿซถ.

-fdiagnostics-format=FORMAT

Okay so this one lets you pick a different format for printing diagnostics ๐Ÿ’–. FORMAT can be text, sarif-stderr, sarif-file, json, json-stderr, or json-file ๐Ÿฆ‹.

Using this option replaces any extra "output sinks" added by -fdiagnostics-add-output=, or the ones set by -fdiagnostics-set-output= ๐Ÿ’….

The default is text ๐ŸŒธ.

The sarif-stderr and sarif-file formats both emit diagnostics in SARIF Version 2.1.0 format. sarif-stderr sends them to stderr, and sarif-file writes them to a file named source.sarif โœจ.

The various json, json-stderr, and json-file values are deprecated ๐Ÿ’€. They refer to a legacy JSON-based output format. json is just a synonym for json-stderr ๐Ÿงš. The json-stderr and json-file formats are identical, apart from where the JSON gets emitted to. With json-stderr, the JSON goes to stderr, whereas with json-file it's written to source.gcc.json ๐Ÿ“.

-fdiagnostics-add-output=DIAGNOSTICS-OUTPUT-SPEC

Add an additional "output sink" for emitting diagnostics ๐ŸŽ€.

DIAGNOSTICS-OUTPUT-SPEC should specify a scheme, optionally followed by : and one or more KEY=VALUE pairs, in this form:

    <SCHEME>
    <SCHEME>:<KEY>=<VALUE>
    <SCHEME>:<KEY>=<VALUE>,<KEY2>=<VALUE2>

etc. ๐Ÿซ 

Schemes, keys, or values with a name prefixed "experimental" may change or be removed without notice ๐Ÿ™ˆ. Be careful with those ones, bestie!

SCHEME can be:

  • text: emits diagnostics to stderr using GCC's classic text output format ๐Ÿ“œ.

Supported keys are:

  • color=[yes|no]: overrides the colorization settings from -fdiagnostics-color for this text output ๐ŸŒˆ.
  • experimental-nesting=[yes|no]: enables an experimental mode that emphasizes hierarchical relationships within diagnostics messages, showing location information on separate lines ๐Ÿงฉ.
  • experimental-nesting-show-locations=[yes|no]: if experimental-nesting=yes, then locations are shown by default. Set this key to no to stop printing them. This exists for GCC developers writing DejaGnu test cases ๐Ÿงช.
  • experimental-nesting-show-levels=[yes|no]: a debugging option for use with experimental-nesting=yes. Set it to yes to print explicit nesting levels in the output. This also exists for GCC developers ๐Ÿ› ๏ธ.
  • sarif: emits diagnostics to a file in SARIF format ๐Ÿ“.

Supported keys are:

  • file=FILENAME: the filename to write the SARIF output to, which can have a leading absolute or relative path. If not specified, it defaults to source.sarif ๐Ÿ’Œ.
  • version=[2.1|2.2-prerelease]: the SARIF version to use for the output. If not specified, it defaults to 2.1. "2.2-prerelease" uses an unofficial draft of the future SARIF 2.2 specification and should only be used for experimentation in this release ๐Ÿงชโš ๏ธ.

For example,

        -fdiagnostics-add-output=sarif:version=2.1,file=foo.2.1.sarif
        -fdiagnostics-add-output=sarif:version=2.2-prerelease,file=foo.2.2.sarif

would add a pair of outputs, each writing to a different file, using versions 2.1 and 2.2 of the SARIF standard respectively ๐Ÿ‘ฏโ€โ™€๏ธ.

In EBNF:

        <diagnostics-output-specifier> = <diagnostics-output-name>
                                           | <diagnostics-output-name>, ":", <key-value-pairs>;

        <diagnostics-output-name> = "text" | "sarif";

        <key-value-pairs> = <key-value-pair>
                              | <key-value-pair> "," <key-value-pairs>;

        <key-value-pair> = <key> "=" <value>;

        <key> = ? string without a '=' ? ;
        <value> = ? string without a ',' ? ;

-fdiagnostics-set-output=DIAGNOSTICS-OUTPUT-SPEC

This works like -fdiagnostics-add-output=, except instead of adding an extra "output sink," it replaces all existing output sinks ๐Ÿงน. That includes ones from -fdiagnostics-format=, -fdiagnostics-add-output=, or a prior call to -fdiagnostics-set-output= ๐Ÿ’….

-fno-diagnostics-json-formatting

By default, when JSON gets emitted for diagnostics (via -fdiagnostics-format=sarif-stderr, -fdiagnostics-format=sarif-file, -fdiagnostics-format=json, -fdiagnostics-format=json-stderr, or -fdiagnostics-format=json-file), GCC adds newlines and indentation to visually show the hierarchical structure of the JSON ๐ŸŒธ.

Use -fno-diagnostics-json-formatting to suppress that whitespace ๐Ÿซง. It must be passed before the option it is meant to affect โš ๏ธ.

This is meant for compatibility with tools that don't expect newlines in the output, like the kind emitted by older GCC releases ๐Ÿฆ–.


๐ŸŽ€ Options to Request or Suppress Warnings ๐ŸŽ€

Warnings are diagnostic messages that report constructions that aren't inherently erroneous, but are risky or suggest there may have been an error ๐Ÿ‘€.

The following language-independent options don't enable specific warnings. Instead, they control the kinds of diagnostics GCC produces ๐Ÿง .

-fsyntax-only

Checks the code for syntax errors, but doesn't do anything beyond that ๐Ÿ“.

-fmax-errors=n

Limits the maximum number of error messages to n. Once that number is hit, GCC bails out instead of trying to keep processing the source code ๐Ÿ›‘. If n is 0 (the default), there's no limit on the number of error messages produced ๐ŸŒˆ. If -Wfatal-errors is also specified, then -Wfatal-errors takes precedence over this option ๐Ÿ‘‘.

  • -w: inhibits all warning messages ๐Ÿคซ.
  • -Werror: turns all warnings into errors ๐Ÿ’€.

-Werror=

Turns the specified warning into an error ๐Ÿšจ. You append the specifier for a warning to it. For example, -Werror=switch turns the warnings controlled by -Wswitch into errors ๐Ÿ“. This switch also has a negative form, which negates -Werror for specific warnings. For example, -Wno-error=switch makes -Wswitch warnings not be errors, even when -Werror is in effect ๐Ÿฆ‹.

The warning message for each controllable warning includes the option that controls it. You can then use that option with -Werror= and -Wno-error= as described above ๐Ÿ’œ. (Printing of the option in the warning message can be disabled with the -fno-diagnostics-show-option flag ๐Ÿ™ˆ.)

Note that specifying -Werror=foo automatically implies -Wfoo. However, -Wno-error=foo doesn't imply anything at all ๐Ÿซ .

-Wfatal-errors

Causes the compiler to abort compilation on the first error that occurred, instead of trying to keep going and printing further error messages ๐Ÿ˜ญ.

You can request many specific warnings with options beginning with -W. For example, -Wunused-variable requests warnings on declarations of variables that are never used ๐Ÿ”. Each of these specific warning options also has a negative form beginning with -Wno- to turn warnings off. For example, -Wno-unused-variable ๐ŸŒ™. This manual only lists one of the two forms, whichever one isn't the default ๐Ÿ“–. For further language-specific options, also refer to C++ Dialect Options and Objective-C and Objective-C++ Dialect Options ๐ŸŒ. Additional warnings can be produced by enabling the static analyzer;

Some options, such as -Wall and -Wextra, turn on other options, such as -Wunused, which may turn on further options, such as -Wunused-variable ๐Ÿ”—. The combined effect of positive and negative forms is that more specific options have priority over less specific ones, no matter their position on the command line ๐Ÿ‘‘. For options of the same specificity, the last one takes effect ๐Ÿ. Options enabled or disabled via pragmas take effect as if they appeared at the end of the command line ๐Ÿ“Œ.

When an unrecognized warning option is requested (e.g., -Wunknown-warning), GCC gives an error saying the option isn't recognized ๐Ÿ™…โ€โ™€๏ธ. The -Wno- form behaves a little differently, though: no diagnostic is produced for -Wno-unknown-warning unless other diagnostics are being produced ๐Ÿคทโ€โ™€๏ธ. This lets you use new -Wno- options with old compilers. But if something goes wrong, the compiler warns that an unrecognized option is present ๐Ÿซถ.

The effectiveness of some warnings depends on optimizations also being enabled. For example, -Wsuggest-final-types is more effective with link-time optimization โšก. Some other warnings may not be issued at all unless optimization is enabled ๐Ÿ˜ด. While optimization in general improves the efficacy of warnings about control and data-flow problems, in some cases it may also cause false positives ๐Ÿ™ƒ.

-Wpedantic, -pedantic

Issues all the warnings demanded by strict ISO C and ISO C++. It diagnoses all programs that use forbidden extensions, and some other programs that don't follow ISO C and ISO C++ ๐Ÿ“. This follows the version of the ISO C or C++ standard specified by any -std option used ๐Ÿ“š.

Valid ISO C and ISO C++ programs should compile properly with or without this option (though a rare few require -ansi or a -std option specifying the version of the standard) โœ…. Without this option, though, certain GNU extensions and traditional C and C++ features are supported too ๐Ÿง. With this option, they get diagnosed (or rejected with -pedantic-errors) ๐Ÿšซ.

-Wpedantic doesn't cause warning messages for use of the alternate keywords whose names begin and end with __. This alternate format can also be used to disable warnings for non-ISO __intN types, i.e. __intN__ ๐Ÿช„. Pedantic warnings are also disabled in the expression that follows "__extension__". However, only system header files should use these escape routes. Application programs should avoid them ๐Ÿ™…โ€โ™€๏ธโš ๏ธ.

Some warnings about non-conforming programs are controlled by options other than -Wpedantic. In many cases they're implied by -Wpedantic, but they can be disabled separately by their specific option, e.g. -Wpedantic -Wno-pointer-sign ๐Ÿ’–.

Where the standard named by -std is a GNU extended dialect like gnu90 or gnu99, there's a matching base standard, which is the version of ISO C that the GNU dialect is built on, bestie ๐ŸŽ€. Warnings from -Wpedantic show up wherever the base standard requires them. It wouldn't make sense for them to only cover features outside the GNU dialect you picked, because by definition the GNU dialects already include everything the compiler supports with that option, so there'd be nothing left to warn about ๐Ÿ™ˆ

-pedantic-errors

Okay so this one is giving strict ๐Ÿ’…. It makes an error out of anything the base standard requires a diagnostic for, including some cases with undefined behavior at compile time and some cases that don't stop a program that's valid under the standard from compiling. Don't mix this up with -Werror=pedantic, though. That one is basically useless in comparison, because it only upgrades the diagnostics controlled by -Wpedantic. -pedantic-errors also hits required diagnostics that are always on or controlled by other options ๐Ÿ˜ญ

If you want the required diagnostics that are warnings by default to become errors, but you don't want the -Wpedantic diagnostics turned on, you can use -pedantic-errors -Wno-pedantic. Or, if you want them enabled but only as warnings, go with -pedantic-errors -Wno-error=pedantic โœจ

Some required diagnostics are errors by default, but you can knock them down to warnings with -fpermissive or with their own specific warning option, like -Wno-error=narrowing. ๐Ÿฆ‹

Some diagnostics for non-ISO practices are controlled by their own warning options instead of -Wpedantic, but -pedantic-errors turns those into errors too. For example:

  • -Wattributes (for standard attributes)
  • -Wchanges-meaning (C++)
  • -Wcomma-subscript (C++23 or later)
  • -Wdeclaration-after-statement (C90 or earlier)
  • -Welaborated-enum-base (C++11 or later)
  • -Wimplicit-int (C99 or later)
  • -Wimplicit-function-declaration (C99 or later)
  • -Wincompatible-pointer-types
  • -Wint-conversion
  • -Wlong-long (C90 or earlier)
  • -Wmain
  • -Wnarrowing (C++11 or later)
  • -Wpointer-arith
  • -Wpointer-sign
  • -Wincompatible-pointer-types
  • -Wregister (C++17 or later)
  • -Wvla (C90 or earlier)
  • -Wwrite-strings (C++11 or later)

-fpermissive

This one is the chill option ๐ŸŒธ It downgrades some required diagnostics about nonconformant code from errors to warnings, so some nonconforming code can still compile. A few C++ diagnostics are controlled only by this flag. It also downgrades some C and C++ diagnostics that have their own flag:

  • -Wdeclaration-missing-parameter-type (C and Objective-C only)
  • -Wimplicit-function-declaration (C and Objective-C only)
  • -Wimplicit-int (C and Objective-C only)
  • -Wincompatible-pointer-types (C and Objective-C only)
  • -Wint-conversion (C and Objective-C only)
  • -Wnarrowing (C++ and Objective-C++ only)
  • -Wreturn-mismatch (C and Objective-C only)
  • -Wtemplate-body (C++ and Objective-C++ only)

-fpermissive is the default for the historic C language modes: -std=c89, -std=gnu89, -std=c90, and -std=gnu90 ๐Ÿ•ฐ๏ธ

-Wall

This turns on all the warnings about constructions that some folks find questionable and that are easy to avoid (or tweak so the warning goes away), even when macros are involved. It also turns on some language-specific warnings described in the C++ Dialect Options and Objective-C and Objective-C++ Dialect Options sections ๐Ÿ’–

-Wall turns on the following warning flags:

  • -Waddress
  • -Waligned-new (C++ and Objective-C++ only)
  • -Warray-bounds=1 (only with -O2)
  • -Warray-compare
  • -Warray-parameter=2
  • -Wbool-compare
  • -Wbool-operation
  • -Wc++11-compat
  • -Wc++14-compat
  • -Wc++17compat
  • -Wc++20compat
  • -Wcatch-value (C++ and Objective-C++ only)
  • -Wchar-subscripts
  • -Wclass-memaccess (C++ and Objective-C++ only)
  • -Wcomment
  • -Wdangling-else
  • -Wdangling-pointer=2
  • -Wdelete-non-virtual-dtor (C++ and Objective-C++ only)
  • -Wduplicate-decl-specifier (C and Objective-C only)
  • -Wenum-compare (in C/ObjC; this is on by default in C++)
  • -Wenum-int-mismatch (C and Objective-C only)
  • -Wformat=1
  • -Wformat-contains-nul
  • -Wformat-diag
  • -Wformat-extra-args
  • -Wformat-overflow=1
  • -Wformat-truncation=1
  • -Wformat-zero-length
  • -Wframe-address
  • -Wimplicit (C and Objective-C only)
  • -Wimplicit-function-declaration (C and Objective-C only)
  • -Wimplicit-int (C and Objective-C only)
  • -Winfinite-recursion
  • -Winit-self (C++ and Objective-C++ only)
  • -Wint-in-bool-context
  • -Wlogical-not-parentheses
  • -Wmain (only for C/ObjC and unless -ffreestanding)
  • -Wmaybe-uninitialized
  • -Wmemset-elt-size
  • -Wmemset-transposed-args
  • -Wmisleading-indentation (only for C/C++)
  • -Wmismatched-dealloc
  • -Wmismatched-new-delete (C++ and Objective-C++ only)
  • -Wmissing-attributes
  • -Wmissing-braces (only for C/ObjC)
  • -Wmultistatement-macros
  • -Wnarrowing (C++ and Objective-C++ only)
  • -Wnonnull
  • -Wnonnull-compare
  • -Wopenmp-simd (C and C++ only)
  • -Woverloaded-virtual=1 (C++ and Objective-C++ only)
  • -Wpacked-not-aligned
  • -Wparentheses
  • -Wpessimizing-move (C++ and Objective-C++ only)
  • -Wpointer-sign (only for C/ObjC)
  • -Wrange-loop-construct (C++ and Objective-C++ only)
  • -Wreorder (C++ and Objective-C++ only)
  • -Wrestrict
  • -Wreturn-type
  • -Wself-move (C++ and Objective-C++ only)
  • -Wsequence-point
  • -Wsign-compare (C++ and Objective-C++ only)
  • -Wsizeof-array-div
  • -Wsizeof-pointer-div
  • -Wsizeof-pointer-memaccess
  • -Wstrict-aliasing
  • -Wstrict-overflow=1
  • -Wswitch
  • -Wtautological-compare
  • -Wtrigraphs
  • -Wuninitialized
  • -Wunknown-pragmas
  • -Wunused
  • -Wunused-but-set-variable
  • -Wunused-const-variable=1 (only for C/ObjC)
  • -Wunused-function
  • -Wunused-label
  • -Wunused-local-typedefs
  • -Wunused-value
  • -Wunused-variable
  • -Wuse-after-free=2
  • -Wvla-parameter
  • -Wvolatile-register-var
  • -Wzero-length-bounds

Heads up ๐Ÿ‘€ some warning flags are not implied by -Wall. Some of them flag constructions users don't usually see as questionable but that you might want to check for now and then. Others flag constructions that are necessary or hard to avoid in some cases, with no simple code change to silence them. Some of those are enabled by -Wextra, but a lot of them have to be turned on one by one ๐Ÿงš

-Wextra

This turns on some extra warning flags that -Wall doesn't. (This option used to be called -W. The older name still works, but -Wextra is more descriptive, so use that one โœจ)

  • -Wabsolute-value (only for C/ObjC)
  • -Walloc-size
  • -Wcalloc-transposed-args
  • -Wcast-function-type
  • -Wclobbered
  • -Wdangling-reference (C++ only)
  • -Wdeprecated-copy (C++ and Objective-C++ only)
  • -Wempty-body
  • -Wenum-conversion (only for C/ObjC)
  • -Wexpansion-to-defined
  • -Wignored-qualifiers (only for C/C++)
  • -Wimplicit-fallthrough=3
  • -Wmaybe-uninitialized
  • -Wmissing-field-initializers
  • -Wmissing-parameter-name (C/ObjC only)
  • -Wmissing-parameter-type (C/ObjC only)
  • -Wold-style-declaration (C/ObjC only)
  • -Woverride-init (C/ObjC only)
  • -Wredundant-move (C++ and Objective-C++ only)
  • -Wshift-negative-value (in C++11 to C++17 and in C99 and newer)
  • -Wsign-compare (C++ and Objective-C++ only)
  • -Wsized-deallocation (C++ and Objective-C++ only)
  • -Wstring-compare
  • -Wtype-limits
  • -Wuninitialized
  • -Wunterminated-string-initialization
  • -Wunused-parameter (only with -Wunused or -Wall)
  • -Wunused-but-set-parameter (only with -Wunused or -Wall)

-Wextra also prints warnings for these cases too ๐Ÿซถ

  • A pointer gets compared against integer zero with <, <=, >, or >=.
  • (C++ only) An enumerator and a non-enumerator both show up in a conditional expression.
  • (C++ only) Ambiguous virtual bases.
  • (C++ only) Subscripting an array that has been declared register.
  • (C++ only) Taking the address of a variable that has been declared register.
  • (C++ only) A base class isn't initialized in the copy constructor of a derived class.

-Wabi (C, Objective-C, C++ and Objective-C++ only)

This warns about code affected by ABI changes ๐Ÿ”ฅ That includes code that might not be compatible with the vendor-neutral C++ ABI, plus the psABI for the particular target. The psABI warnings are also controlled separately by -Wpsabi, which -Wabi implies.

Since G++ now defaults to updating the ABI with each major release, -Wabi normally only warns about C++ ABI compatibility problems when a check was added later in a release series for an ABI issue found after the initial release. -Wabi warns about more things if an older ABI version is selected with -fabi-version=n ๐Ÿ‘‘

You can also give -Wabi an explicit version number to check C++ ABI compatibility against a particular -fabi-version level. For example, -Wabi=2 warns about changes relative to -fabi-version=2.

If you give an explicit version number and -fabi-compat-version isn't specified, the version number from this option is used for compatibility aliases. If you don't give a version number with this option but do specify -fabi-compat-version, that version number is used for C++ ABI warnings instead.

An effort has been made to warn about all of these cases, but there are probably some that go unwarned even though G++ is generating incompatible code. There may also be cases where warnings get emitted even though the generated code is actually compatible ๐Ÿ’€

If you're worried that code generated by G++ might not be binary compatible with code from other compilers, rewrite your code to dodge these warnings ๐ŸŒˆ

Known incompatibilities in -fabi-version=2 (the default from GCC 3.4 to 4.9) include:

  • A template with a non-type template parameter of reference type got mangled incorrectly:
extern int N;
template <int &> struct S {};
void n (S<N>) {2}

This was fixed in -fabi-version=3.

  • SIMD vector types declared using __attribute ((vector_size)) were mangled in a non-standard way that didn't allow overloading of functions taking vectors of different sizes.

The mangling changed in -fabi-version=4.

  • __attribute ((const)) and noreturn were mangled as type qualifiers, and decltype of a plain declaration got folded away.

These mangling issues were fixed in -fabi-version=5.

Okay so the ABI saga keeps going, bestie ๐Ÿ’…

  • Scoped enumerators passed to a variadic function get promoted like unscoped ones, which makes va_arg throw a fit ๐Ÿ™ˆ On most targets this doesn't actually change the parameter passing ABI, because there's no way to pass anything smaller than int. โœจ Also, the ABI mangling changed for template argument packs, const_cast, static_cast, prefix increment/decrement, and a class scope function used as a template argument. Those got fixed in -fabi-version=6 ๐ŸŽ€
  • Lambdas in default argument scope were mangled incorrectly, and the ABI changed the mangling of nullptr_t ๐Ÿ˜ญ Both got fixed in -fabi-version=7 ๐Ÿซถ
  • When mangling a function type with function-cv-qualifiers, the un-qualified function type was wrongly treated as a substitution candidate ๐Ÿ’€ That got fixed in -fabi-version=8, which is the default for GCC 5.1 ๐Ÿ‘‘
  • decltype(nullptr) had an alignment of 1, so you could get unaligned accesses, yikes ๐Ÿ™ˆ Tea: this didn't affect the ABI of a function with a nullptr_t parameter, because parameters have a minimum alignment. It got fixed in -fabi-version=9, the default for GCC 5.2 ๐ŸŒธ
  • Target-specific attributes that change a type's identity, like ia32 calling conventions on a function type (stdcall, regparm, etc.), didn't affect the mangled name ๐Ÿ˜ญ That led to name collisions whenever function pointers were used as template arguments. It got fixed in -fabi-version=10, the default for GCC 6.1 ๐Ÿ’œ
  • -Wpsabi (C, Objective-C, C++ and Objective-C++ only) โ€” this one is all about processor-specific ABI changes, like shifts in alignment requirements or how function arguments get passed ๐Ÿฆ‹ On a bunch of targets, including AArch64, ARM, x86, MIPS, RS6000/PowerPC, and S/390, these details changed between GCC versions and/or C or C++ standard versions, and that can mess with binary compatibility. With -Wpsabi, GCC can sniff out potentially incompatible usages and warn you โœจ It's enabled by default, and -Wabi implies it too.
  • -Wno-changes-meaning (C++ and Objective-C++ only) โ€” okay, C++ wants unqualified uses of a name inside a class to mean the same thing across the whole class scope, so declaring the name after you've already used it is ill-formed ๐Ÿ™…โ€โ™€๏ธ
struct A;
struct B1 { A a; typedef A A; }; // warning, 'A' changes meaning
struct B2 { A a; struct A { }; }; // error, 'A' changes meaning

By default the B1 case is only a warning, because both declarations have the same type. The B2 case is an error ๐Ÿ’€ You can turn off both diagnostics with -Wno-changes-meaning. If you want the error case downgraded to a warning instead, use -Wno-error=changes-meaning or -fpermissive. Both diagnostics also get suppressed by -fms-extensions ๐ŸŽ€

  • -Wchar-subscripts โ€” warns if an array subscript has type char. This is a super common bug, because people forget that char is signed on some machines ๐Ÿ™ˆ Enabled by -Wall.
  • -Wno-coverage-mismatch โ€” warns if feedback profiles don't match when you use -fprofile-use. If the source file changes between compiling with -fprofile-generate and -fprofile-use, the profile feedback files can fail to match, and GCC can't use that feedback info ๐Ÿ˜ญ By default this warning is on and treated as an error. You can use -Wno-coverage-mismatch to silence the warning, or -Wno-error=coverage-mismatch to silence the error. Skipping the error can leave you with poorly optimized code, so it's only really useful for very minor stuff like bug fixes in an existing codebase. Completely disabling the warning is not recommended ๐Ÿซถ
  • -Wno-coverage-too-many-conditions โ€” warns if -fcondition-coverage is used and an expression has too many terms, so GCC gives up on coverage. Coverage gets abandoned when the conditional has more terms than there are bits in a gcov_type_unsigned. This warning is enabled by default โœจ
  • -Wno-coverage-too-many-paths โ€” warns if -fpath-coverage is used and a function has too many paths, so GCC gives up. Giving up is controlled by -fpath-coverage-limit. This warning is enabled by default ๐ŸŒˆ
  • -Wno-coverage-invalid-line-number โ€” warns in case a function ends earlier than it begins because of invalid linenum macros ๐Ÿ’€ The warning only shows up with --coverage enabled. By default it's on and treated as an error. -Wno-coverage-invalid-line-number disables the warning, and -Wno-error=coverage-invalid-line-number disables the error.
  • -Wno-cpp (C, Objective-C, C++, Objective-C++ and Fortran only) โ€” suppresses the warning messages that #warning directives emit ๐ŸŒธ
  • -Wdouble-promotion (C, C++, Objective-C and Objective-C++ only) โ€” gives a warning when a float value gets implicitly promoted to double ๐Ÿ‘€ CPUs with a 32-bit single-precision floating-point unit implement float in hardware but emulate double in software. On a machine like that, doing math with double is way more expensive because of the software emulation overhead ๐Ÿ˜ญ

It's super easy to accidentally do double math, because floating-point literals are implicitly double. For example:

float area(float radius)
{
   return 3.14159 * radius * radius;
}

The compiler does the entire computation in double, because the floating-point literal is a double ๐Ÿ™ˆ

  • -Wduplicate-decl-specifier (C and Objective-C only) โ€” warns if a declaration has a duplicate const, volatile, restrict or _Atomic specifier. Enabled by -Wall ๐Ÿ’…
  • -Wformat, -Wformat=n โ€” this is the big format-string checker ๐Ÿ”ฅ It checks calls to printf, scanf, etc., to make sure the arguments have types that match the format string, and that the conversions in the format string actually make sense โœจ That covers standard functions, plus any others specified by format attributes, in the printf, scanf, strftime and strfmon families (strfmon is an X/Open extension, not in the C standard), or other target-specific families. Which functions get checked without format attributes depends on the standard version you picked, and those checks are turned off by -ffreestanding or -fno-builtin.

The formats get checked against the format features supported by GNU libc version 2.2. That includes all ISO C90 and C99 features, plus features from the Single Unix Specification and some BSD and GNU extensions ๐Ÿฆ‹ Other library implementations might not support all of these, and GCC doesn't warn about features that go beyond a particular library's limits. Still, if you use -Wpedantic with -Wformat, you'll get warnings about format features that aren't in the selected standard version (but not for strfmon formats, since those aren't in any version of the C standard).

  • -Wformat=1, -Wformat โ€” -Wformat is the same as -Wformat=1, and -Wno-format is the same as -Wformat=0 ๐ŸŽ€ Since -Wformat also checks for null format arguments in several functions, it implies -Wnonnull too. A few parts of this level can be turned off with -Wno-format-contains-nul, -Wno-format-extra-args, and -Wno-format-zero-length. -Wformat is enabled by -Wall.
  • -Wformat=2 โ€” turns on -Wformat plus extra format checks ๐Ÿ‘‘ Right now that's equivalent to -Wformat -Wformat-nonliteral -Wformat-security -Wformat-y2k.
  • -Wno-format-contains-nul โ€” if -Wformat is specified, this stops GCC from warning about format strings that contain NUL bytes ๐ŸŒธ
  • -Wno-format-extra-args โ€” if -Wformat is specified, this stops GCC from warning about excess arguments to a printf or scanf format function. The C standard says those extra args get ignored ๐Ÿ’– If the unused arguments sit between used arguments that use $ operand number specifications, GCC still warns by default, because it can't know what type to hand to va_arg to skip the unused ones. But for scanf formats, this option suppresses the warning when the unused arguments are all pointers, since the Single Unix Specification says those are allowed ๐Ÿงš
  • -Wformat-overflow, -Wformat-overflow=level โ€” warns about calls to formatted output functions like sprintf and vsprintf that might overflow the destination buffer ๐Ÿ˜ญ When the exact number of bytes written by a format directive can't be figured out at compile time, GCC estimates it using heuristics that depend on the level argument and on optimization. Turning on optimization usually makes the warning more accurate, but it can also cause false positives ๐Ÿ”
    • -Wformat-overflow, -Wformat-overflow=1 โ€” level 1 is enabled by -Wformat and takes a conservative approach, only warning about calls that most likely overflow the buffer ๐Ÿซถ At this level, numeric arguments to format directives with unknown values are assumed to be one, and strings of unknown length are assumed to be empty. Numeric arguments known to be bounded to a subrange of their type, or string arguments whose output is bounded either by their directive's precision or by a finite set of string literals, are assumed to take the value in that range that produces the most bytes on output. For example, the sprintf call below gets diagnosed, because even with both a and b equal to zero, the terminating NUL character ('\0') that the function appends to the destination buffer gets written past its end ๐Ÿ’€ Making the buffer one byte bigger is enough to avoid the warning, though it might not be enough to avoid the overflow itself ๐ŸŒˆ

โœจ OPTIONS โœจ (continued)

  • -Wformat-overflow=2 โ€” okay bestie, level 2 is where it gets spicy ๐Ÿ”ฅ. It also flags calls that might overflow the destination buffer if an argument is big enough or large enough in magnitude. At this level, unknown numeric arguments get assumed to be the minimum value for signed types with a precision greater than 1, and the maximum value for everything else. Unknown string arguments that can't be proven bounded (not by the directive's precision, not by a finite set of string literals they could be, and not by the character array they point to) get assumed to be 1 character long ๐Ÿงš

At level 2, GCC diagnoses the example above again, but this time the reason is that with a equal to a 32-bit INT_MIN, the first %i directive writes some of its digits past the end of the destination buffer ๐Ÿ˜ญ To make the call safe no matter what the two variables hold, the buffer needs to be at least 34 bytes. GCC even includes the minimum buffer size in an informational note right after the warning ๐Ÿ’…

void f (int a, int b)
{
  char buf [13];
  sprintf (buf, "a = %i, b = %i\n", a, b);
}

Instead of making the buffer bigger, you can also put a leash on the values. You can bound the length of string arguments by giving the format directive a precision. If numeric arguments can be assumed to fit in less than the precision of their type, picking a suitable length modifier in the format specifier shrinks the buffer you need. So if a and b in the example above are guaranteed to fit in a short int, then using either the %hi directive or casting the argument to short brings the maximum required size down to 24 bytes ๐Ÿฆ‹

void f (int a, int b)
{
  char buf [23];
  sprintf (buf, "a = %hi, b = %i\n", a, (short)b);
}
  • -Wno-format-zero-length โ€” if you've got -Wformat on, this tells GCC to stop warning about zero-length formats. Totally allowed, the C standard says so ๐ŸŒธ
  • -Wformat-nonliteral โ€” with -Wformat on, this also warns when the format string isn't a string literal, since GCC can't check it. The exception is when the format function takes its arguments as a va_list ๐ŸŽ€
  • -Wformat-security โ€” with -Wformat on, this one is about possible security problems ๐Ÿ™ˆ Right now it warns about printf and scanf calls where the format string isn't a string literal and there are no format arguments, like printf (foo);. That can be a real security hole if the format string came from untrusted input and contains %n. (Fun fact: this is currently a subset of what -Wformat-nonliteral catches, but future warnings may be added to -Wformat-security that aren't in -Wformat-nonliteral.) ๐Ÿ’œ
  • -Wformat-signedness โ€” with -Wformat on, this warns if the format string wants an unsigned argument but you pass a signed one, and vice versa ๐ŸŒˆ
  • -Wformat-truncation, -Wformat-truncation=level โ€” this one warns about formatted output functions like snprintf and vsnprintf that might truncate their output โœ‚๏ธ If GCC can't figure out the exact number of bytes a format directive writes at compile time, it guesses using heuristics that depend on the level and on optimization. Turning optimization on usually makes the warning more accurate, but it can also cause false positives. Unless noted otherwise, this option uses the same logic as -Wformat-overflow.
    • -Wformat-truncation, -Wformat-truncation=1 โ€” level 1 is enabled by -Wformat and is pretty chill ๐Ÿ˜Œ It only warns about calls to bounded functions whose return value is unused and that will most likely truncate the output.
    • -Wformat-truncation=2 โ€” level 2 goes further and also warns about bounded calls whose return value is used, if they might truncate given an argument of sufficient length or magnitude ๐Ÿ‘‘
  • -Wformat-y2k โ€” with -Wformat on, this warns about strftime formats that might only give you a two-digit year ๐Ÿ“…
  • -Wnonnull โ€” warns when you pass a null pointer to an argument that the nonnull function attribute says must be non-null ๐Ÿšซ It's included in -Wall and -Wformat, and you can turn it off with -Wno-nonnull.
  • -Wnonnull-compare โ€” warns when you compare an argument marked with the nonnull function attribute against null inside the function ๐Ÿ™ƒ It's included in -Wall, and you can disable it with -Wno-nonnull-compare.
  • -Wnull-dereference โ€” warns if the compiler finds paths that lead to erroneous or undefined behavior from dereferencing a null pointer ๐Ÿ’€ This only works when -fdelete-null-pointer-checks is active, which optimizations turn on for most targets. How precise the warnings are depends on which optimization options you use.
  • -Wno-musttail-local-addr โ€” stops warnings about passing a pointer (or, in C++, a reference) to a local variable or label as an argument to a musttail call. Those variables go out of scope before the tail call instruction, so no drama โœจ
  • -Wmaybe-musttail-local-addr โ€” warns when the address of a local variable can escape into a musttail call, unless that variable already went out of scope before the musttail call ๐Ÿฆ‹
int foo (int *);

int
bar (int *x)
{
  if (x[0] == 1)
    {
      int a = 42;
      foo (&a);
      /* Without the C<musttail> attribute this call would not
         be tail called, because address of the C<a> variable escapes
         and the second foo call could dereference it.  With the attribute
         the local variables are assumed to go out of scope immediately
         before the tail call instruction and the compiler warns about
         this.  */
      [[gnu::musttail]] return foo (nullptr);
    }
  else
    {
      {
        int a = 42;
        foo (&a);
      }
      /* The C<a> variable isn't already in scope, so even when it
         escaped, even without C<musttail> attribute it would be
         undefined behavior to dereference it and the compiler could
         turn this into a tail call.  No warning is diagnosed here.  */
      [[gnu::musttail]] return foo (nullptr);
    }
}

This warning is enabled by -Wextra. ๐Ÿง

  • -Wnrvo (C++ and Objective-C++ only) โ€” warns if GCC doesn't elide the copy from a local variable to the return value, even though [class.copy.elision] allows it ๐ŸคŒ This elision is commonly called the Named Return Value Optimization. In the example below, GCC can't elide the copies from both v1 and v2, so it elides neither โœจ
std::vector<int> f()
{
  std::vector<int> v1, v2;
  // ...
  if (cond) return v1;
  else return v2; // warning: not eliding copy
}
  • -Winfinite-recursion โ€” warns about infinitely recursive calls ๐Ÿ” It works at all optimization levels, but you need optimization on to catch infinite recursion across two or more functions. It's included in -Wall.

Compare this with -Wanalyzer-infinite-recursion, which gives a similar diagnostic but is implemented differently (as part of -fanalyzer).

  • -Winit-self (C, C++, Objective-C and Objective-C++ only) โ€” warns about uninitialized variables that get initialized with themselves ๐Ÿ™ˆ Heads up: this option only works together with -Wuninitialized.

For example, GCC warns about i being uninitialized in the following snippet only when -Winit-self is specified:

int f()
{
  int i = i;
  return i;
}

In C++, this warning is enabled by -Wall.

  • -Wno-implicit-int (C and Objective-C only) โ€” controls warnings for declarations that don't specify a type ๐Ÿ™ƒ This warning is on by default as an error in C99 and later dialects, and it's also turned on by -Wall. You can downgrade the error to a warning with -fpermissive (along with some other errors), or just for this error with -Wno-error=implicit-int.

-pedantic-errors upgrades this warning to an error.

  • -Wno-implicit-function-declaration (C and Objective-C only) โ€” controls warnings for functions that get used before they're declared ๐Ÿ“ฃ Like implicit-int, this is on by default as an error in C99 and later, and -Wall turns it on too. You can downgrade it with -fpermissive (along with certain other errors), or for this error alone with -Wno-error=implicit-function-declaration.

-pedantic-errors upgrades this warning to an error.

  • -Wimplicit (C and Objective-C only) โ€” same as -Wimplicit-int plus -Wimplicit-function-declaration ๐ŸŽ€ It's enabled by -Wall.
  • -Whardened โ€” warns when -fhardened didn't enable an option from its set (see -fhardened for the full list) ๐Ÿ›ก๏ธ For example, if you use -fhardened and -fstack-protector together on the command line, -Whardened will warn, because -fstack-protector-strong won't be enabled by -fhardened.

This warning is on by default, but it only has an effect when -fhardened is enabled.

  • -Wimplicit-fallthrough โ€” this is the same as -Wimplicit-fallthrough=3, and -Wno-implicit-fallthrough is the same as -Wimplicit-fallthrough=0 ๐ŸŒท
  • -Wimplicit-fallthrough=n โ€” warns when a switch case falls through into the next one without a break. Here's an example:

โœจ OPTIONS โœจ (continued, bestie) ๐Ÿ’…

okay so the -Wimplicit-fallthrough warning is lowkey the best one for catching sneaky bugs ๐Ÿ™ˆ

switch (cond)
  {
  case 1:
    a = 1;
    break;
  case 2:
    a = 2;
  case 3:
    a = 3;
    break;
  }

This warning does not warn when the last statement of a case cannot fall through, e.g. when there is a return statement or a call to function declared with the noreturn attribute. It's giving "I know what I'm doing" energy ๐Ÿ‘‘

-Wimplicit-fallthrough= also takes into account control flow statements, such as ifs, and only warns when appropriate. E.g.

switch (cond)
  {
  case 1:
    if (i > 3) {
      bar (5);
      break;
    } else if (i < 1) {
      bar (0);
    } else
      return;
  default:
    ...
  }

Since there are occasions where a switch case fall through is desirable, GCC provides an attribute, __attribute__ ((fallthrough)), that is to be used along with a null statement to suppress this warning that would normally occur ๐Ÿซถ

switch (cond)
  {
  case 1:
    bar (0);
    __attribute__ ((fallthrough));
  default:
    ...
  }

C++17 gives you a standard way to suppress the -Wimplicit-fallthrough warning using [[fallthrough]]; instead of the GNU attribute โœจ. In C++11 or C++14 you can use [[gnu::fallthrough]];, which is a GNU extension. Instead of these attributes, it is also possible to add a fallthrough comment to silence the warning ๐Ÿ’œ. The whole body of the C or C++ style comment should match the given regular expressions listed below. The option argument n specifies what kind of comments are accepted:

  • -Wimplicit-fallthrough=0 disables the warning altogether.
  • -Wimplicit-fallthrough=1 matches ".*" regular expression, any comment is used as fallthrough comment.
  • -Wimplicit-fallthrough=2 case insensitively matches ".*falls?[ \t-]*thr(ough|u).*" regular expression.
  • -Wimplicit-fallthrough=3 case sensitively matches one of the following regular expressions:
    • "-fallthrough"
    • "@fallthrough@"
    • "lint -fallthrough[ \t]*"
    • "[ \t.!]*(ELSE,? |INTENTIONAL(LY)? )?FALL(S | |-)?THR(OUGH|U)[ \t.!]*(-[^\n\r]*)?"
    • "[ \t.!]*(Else,? |Intentional(ly)? )?Fall((s | |-)[Tt]|t)hr(ough|u)[ \t.!]*(-[^\n\r]*)?"
    • "[ \t.!]*([Ee]lse,? |[Ii]ntentional(ly)? )?fall(s | |-)?thr(ough|u)[ \t.!]*(-[^\n\r]*)?"
  • -Wimplicit-fallthrough=4 case sensitively matches one of the following regular expressions:
    • "-fallthrough"
    • "@fallthrough@"
    • "lint -fallthrough[ \t]*"
    • "[ \t]*FALLTHR(OUGH|U)[ \t]*"
  • -Wimplicit-fallthrough=5 doesn't recognize any comments as fallthrough comments, only attributes disable the warning.

The comment needs to be followed after optional whitespace and other comments by case or default keywords or by a user label that precedes some case or default label ๐ŸŒธ

switch (cond)
  {
  case 1:
    bar (0);
    /* FALLTHRU */
  default:
    ...
  }

The -Wimplicit-fallthrough=3 warning is enabled by -Wextra ๐ŸŽ€


  • -Wno-if-not-aligned (C, C++, Objective-C and Objective-C++ only) โ€” okay so this one controls whether warnings triggered by the warn_if_not_aligned attribute get issued. These warnings are enabled by default ๐Ÿฆ‹
  • -Wignored-qualifiers (C and C++ only) โ€” warns if the return type of a function has a type qualifier such as const. For ISO C such a type qualifier has no effect, since the value returned by a function is not an lvalue. For C++, the warning is only emitted for scalar types or void. ISO C prohibits qualified void return types on function definitions, so such return types always receive a warning even without this option. This warning is also enabled by -Wextra ๐Ÿงš
  • -Wno-ignored-attributes (C and C++ only) โ€” this option controls warnings when an attribute is ignored. This is different from the -Wattributes option in that it warns whenever the compiler decides to drop an attribute, not that the attribute is either unknown, used in a wrong place, etc. This warning is enabled by default ๐Ÿ’–
  • -Wmain โ€” warns if the type of main is suspicious. main should be a function with external linkage, returning int, taking either zero arguments, two, or three arguments of appropriate types. This warning is enabled by default in C++ and is enabled by either -Wall or -Wpedantic. This warning is upgraded to an error by -pedantic-errors ๐Ÿ‘‘
  • -Wmisleading-indentation (C and C++ only) โ€” warns when the indentation of the code does not reflect the block structure. Specifically, a warning is issued for if, else, while, and for clauses with a guarded statement that does not use braces, followed by an unguarded statement with the same indentation. In the following example, the call to bar is misleadingly indented as if it were guarded by the if conditional ๐Ÿ™ˆ
if (some_condition ())
  foo ();
  bar ();  /* Gotcha: this is not guarded by the "if".  */

In the case of mixed tabs and spaces, the warning uses the -ftabstop= option to determine if the statements line up (defaulting to 8). The warning is not issued for code involving multiline preprocessor logic such as the following example.

if (flagA)
  foo (0);
#if SOME_CONDITION_THAT_DOES_NOT_HOLD
  if (flagB)
#endif
  foo (1);

The warning is not issued after a #line directive, since this typically indicates autogenerated code, and no assumptions can be made about the layout of the file that the directive references. This warning is enabled by -Wall in C and C++ โœจ

  • -Wmissing-attributes โ€” warns when a declaration of a function is missing one or more attributes that a related function is declared with and whose absence may adversely affect the correctness or efficiency of generated code. For example, the warning is issued for declarations of aliases that use attributes to specify less restrictive requirements than those of their targets. This typically represents a potential optimization opportunity. By contrast, the -Wattribute-alias=2 option controls warnings issued when the alias is more restrictive than the target, which could lead to incorrect code generation. Attributes considered include alloc_align, alloc_size, cold, const, hot, leaf, malloc, nonnull, noreturn, nothrow, pure, returns_nonnull, and returns_twice. In C++, the warning is issued when an explicit specialization of a primary template declared with attribute alloc_align, alloc_size, assume_aligned, format, format_arg, malloc, or nonnull is declared without it. Attributes deprecated, error, and warning suppress the warning.. You can use the copy attribute to apply the same set of attributes to a declaration as that on another declaration without explicitly enumerating the attributes. This attribute can be applied to declarations of functions, variables, or types. -Wmissing-attributes is enabled by -Wall ๐Ÿฆ‹

For example, since the declaration of the primary function template below makes use of both attribute malloc and alloc_size the declaration of the explicit specialization of the template is diagnosed because it is missing one of the attributes ๐ŸŒˆ

template <class T>
T* __attribute__ ((malloc, alloc_size (1)))
allocate (size_t);

template <>
void* __attribute__ ((malloc))   // missing alloc_size
allocate<void> (size_t);
  • -Wmissing-braces โ€” warns if an aggregate or union initializer is not fully bracketed. In the following example, the initializer for a is not fully bracketed, but that for b is fully bracketed. This warning is enabled by -Wall ๐Ÿ“
int a[2][2] = { 0, 1, 2, 3 };
int b[2][2] = { { 0, 1 }, { 2, 3 } };
  • -Wmissing-include-dirs (C, C++, Objective-C, Objective-C++ and Fortran only) โ€” warns if a user-supplied include directory does not exist. This option is disabled by default for C, C++, Objective-C and Objective-C++. For Fortran, it is partially enabled by default by warning for -I and -J, only. ๐ŸŒธ
  • -Wno-missing-profile โ€” this option controls warnings if feedback profiles are missing when using the -fprofile-use option. This option diagnoses those cases where a new function or a new file is added between compiling with -fprofile-generate and with -fprofile-use, without regenerating the profiles. In these cases, the profile feedback data files do not contain any profile feedback information for the newly added function or file respectively. Also, in the case when profile count data (.gcda) files are removed, GCC cannot use any profile feedback information. In all these cases, warnings are issued to inform you that a profile generation step is due. Ignoring the warning can result in poorly optimized code. -Wno-missing-profile can be used to disable the warning, but this is not recommended and should be done only when non-existent profile data is justified. > ๐Ÿ˜ญ Ignoring this one can give you poorly optimized code, so don't just silence it because it's annoying!
  • -Wmismatched-dealloc โ€” warns for calls to deallocation functions with pointer arguments returned from allocation functions for which the former isn't a suitable deallocator. A pair of functions can be associated as matching allocators and deallocators by use of attribute malloc. Unless disabled by the -fno-builtin option the standard functions calloc, malloc, realloc, and free, as well as the corresponding forms of C++ operator new and operator delete are implicitly associated as matching allocators and deallocators. In the following example mydealloc is the deallocator for pointers returned from myalloc ๐Ÿงš
void mydealloc (void*);
__attribute__ ((malloc (mydealloc, 1))) void*
myalloc (size_t);

void f (void)
{
  void *p = myalloc (32);
  // ...use p...
  free (p);   // warning: not a matching deallocator for myalloc
  mydealloc (p);   // ok
}

okay so in C++, the related option -Wmismatched-new-delete catches mismatches involving either "operator new" or "operator delete" ๐Ÿ”ฅ

Option -Wmismatched-dealloc is included in -Wall โœจ

  • -Wmultistatement-macros ๐ŸŽ€ โ€” Warns about unsafe multi-statement macros that look like they're guarded by an "if", "else", "for", "switch", or "while" clause, but where only the first statement is actually guarded once the macro expands. ๐Ÿ’€

For example:

#define DOIT x++; y++
if (c)
  DOIT;

will increment "y" unconditionally, not just when "c" holds. The can usually be fixed by wrapping the macro in a do-while loop:

#define DOIT do { x++; y++; } while (0)
if (c)
  DOIT;

This warning is enabled by -Wall in C and C++.

  • -Wparentheses ๐ŸŒธ โ€” Warns when parentheses are left out in certain spots, like an assignment where a truth value is expected, or nested operators whose precedence people constantly mix up ๐Ÿ™ˆ

It also warns if you write a comparison like "x<=y<=z". That's equivalent to "(x<=y ? 1 : 0) <= z", which is NOT what regular math notation means, so it's a total trap ๐Ÿ˜ญ

It also warns about dangerous uses of the GNU extension to "?:" with an omitted middle operand. When the condition in the "?:" operator is a boolean expression, the omitted value is always 1. Programmers often expect it to be a value computed inside the conditional expression instead ๐Ÿ’…

For C++, this also warns about some cases of unnecessary parentheses in declarations, which can mean you meant a function call instead of a declaration:

{
  // Declares a local variable called mymutex.
  std::unique_lock<std::mutex> (mymutex);
  // User meant std::unique_lock<std::mutex> lock (mymutex);
}

This warning is enabled by -Wall.

  • -Wno-self-move (C++ and Objective-C++ only) ๐Ÿฆ‹ โ€” This one warns when a value gets moved to itself with "std::move". That kind of "std::move" usually does absolutely nothing, so it's giving pointless โœจ
struct T {
...
};
void fn()
{
  T t;
  ...
  t = std::move (t);
}

This warning is enabled by -Wall.

  • -Wsequence-point ๐Ÿงš โ€” Warns about code that may have undefined semantics because it breaks the sequence point rules in the C and C++ standards. Lowkey the most chaotic one on this list ๐Ÿ˜ญ

The C and C++ standards define the order that expressions get evaluated in, using sequence points. Think of them as a partial ordering between parts of the program: the stuff that runs before the sequence point, and the stuff that runs after it. They show up after a full expression is evaluated (one that isn't part of a bigger expression), after the first operand of a "&&", "||", "? :" or "," (comma) operator is evaluated, before a function gets called (but after its arguments and the expression naming the called function are evaluated), and in a few other places. Apart from what the sequence point rules say, the order of evaluating subexpressions is not specified. These rules only give a partial order, not a total one. For example, if two functions get called inside one expression with no sequence point between them, the order they run in is not specified. Still, the standards committee has ruled that function calls do not overlap.

It's not specified when, between sequence points, changes to object values take effect. Programs whose behavior depends on this have undefined behavior. The C and C++ standards say that "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.". If a program breaks these rules, the results on any particular implementation are totally unpredictable ๐Ÿ’€

Examples of code with undefined behavior are a = a++;, a[n] = b[n++] and a[i++] = i;. Some more complicated cases aren't caught by this option, and it may give the occasional false positive. But overall it's been found pretty effective at catching this sort of problem ๐Ÿ‘‘

The C++17 standard will define the order of evaluation for operands in more cases. In particular, it requires that the right-hand side of an assignment gets evaluated before the left-hand side, so the examples above are no longer undefined. But this option will still warn about them, to help people avoid writing code that's undefined in C and in earlier revisions of C++ ๐Ÿซถ

The standard is worded confusingly, so there's some debate over the precise meaning of the sequence point rules in subtle cases. Links to discussions of the problem, including proposed formal definitions, can be found on the GCC readings page, at https://gcc.gnu.org/readings.html.

This warning is enabled by -Wall for C and C++.

  • -Wno-return-local-addr ๐ŸŒˆ โ€” Don't warn about returning a pointer (or, in C++, a reference) to a variable that goes out of scope after the function returns.
  • -Wreturn-mismatch ๐Ÿ“ โ€” Warns about return statements with no expression in functions that don't return "void". It also warns about a "return" statement with an expression in a function whose return type is "void", unless the expression's type is also "void". As a GNU extension, that second case is accepted without a warning unless -Wpedantic is used.

Trying to use the return value of a non-"void" function other than "main" that flows off the end by reaching the closing curly brace is undefined. Yikes ๐Ÿ˜ญ

This warning is specific to C and enabled by default. In C99 and later language dialects, it's treated as an error. You can downgrade it to a warning with -fpermissive (along with other warnings), or for just this warning, with -Wno-error=return-mismatch.

  • -Wreturn-type ๐Ÿ‘‘ โ€” Warns whenever a function is defined with a return type that defaults to "int" (unless -Wimplicit-int is active, which takes precedence). It also warns if execution may reach the end of the function body, or if the function doesn't contain any return statement at all.

Again, trying to use the return value of a non-"void" function other than "main" that flows off the end by reaching the closing curly brace is undefined.

Unlike C, in C++, flowing off the end of a non-"void" function other than "main" results in undefined behavior even when the function's value isn't used. Not it ๐Ÿ™ˆ

This warning is enabled by default in C++ and by -Wall otherwise.

  • -Wno-shift-count-negative โœจ โ€” Controls warnings if a shift count is negative. This warning is enabled by default.
  • -Wno-shift-count-overflow ๐Ÿ’œ โ€” Controls warnings if a shift count is greater than or equal to the bit width of the type. This warning is enabled by default.
  • -Wshift-negative-value ๐Ÿ”ฅ โ€” Warns if you left-shift a negative value. This warning is enabled by -Wextra in C99 (and newer) and in C++11 to C++17 modes.
  • -Wno-shift-overflow, -Wshift-overflow=n ๐ŸŒธ โ€” These options control warnings about left shift overflows.
    • -Wshift-overflow=1 ๐Ÿฆ‹ โ€” This is the warning level of -Wshift-overflow and it's enabled by default in C99 and C++11 modes (and newer). This level doesn't warn about left-shifting 1 into the sign bit. (However, in C, such an overflow is still rejected in contexts where an integer constant expression is required.) No warning is emitted in C++20 mode (and newer), since signed left shifts always wrap now ๐Ÿงš
    • -Wshift-overflow=2 ๐Ÿ’… โ€” This level also warns about left-shifting 1 into the sign bit, unless C++14 mode (or newer) is active.
  • -Wswitch ๐ŸŽ€ โ€” Warns whenever a "switch" statement has an index of enumerated type and is missing a "case" for one or more of the named codes of that enumeration. (A "default" label stops this warning from happening.) "case" labels that don't match any enumerator also trigger warnings when this option is used, unless the enumeration is marked with the "flag_enum" attribute. This warning is enabled by -Wall.
  • -Wswitch-default ๐Ÿซถ โ€” Warns whenever a "switch" statement doesn't have a "default" case.
  • -Wswitch-enum ๐Ÿ‘‘ โ€” Warns whenever a "switch" statement has an index of enumerated type and is missing a "case" for one or more of the named codes of that enumeration. "case" labels that don't correspond to enumerators also trigger warnings when this option is used, unless the enumeration is marked with the "flag_enum" attribute. The only difference between -Wswitch and this option is that this one warns about an omitted enumeration code even if there's a "default" label. Very thorough, very iconic โœจ
  • -Wno-switch-bool ๐ŸŒˆ โ€” Don't warn when a "switch" statement has an index of boolean type and the case values are outside the range of a boolean type. You can suppress this warning by casting the controlling expression to a type other than "bool". For example:
switch ((int) (a == 4))
  {
  ...
  }

This warning is enabled by default for C and C++ programs.

  • -Wno-switch-outside-range ๐Ÿ“ โ€” This option controls warnings when a "switch" case has a value that's outside of its respective type range. This warning is enabled by default for C and C++ programs.
  • -Wno-switch-unreachable ๐Ÿ™ˆ โ€” Don't warn when a "switch" statement contains statements between the controlling expression and the first case label, since those will never be executed. For example:
switch (cond)
  {
   i = 15;
  ...
   case 5:
  ...
  }

The -Wswitch-unreachable vibe check: it does not warn if the only thing sitting between the controlling expression and the first case label is a plain declaration. Like this ๐Ÿ’…

switch (cond)
  {
   int i;
  ...
   case 5:
   i = 5;
  ...
  }

This one is enabled by default for C and C++ programs โœจ

  • -Wsync-nand (C and C++ only) ๐ŸŒธ โ€” okay so this one yells when you use the __sync_fetch_and_nand and __sync_nand_and_fetch built-ins. Those two changed their semantics in GCC 4.4, so it's giving "heads up, bestie" ๐Ÿซถ
  • -Wtrivial-auto-var-init ๐Ÿฆ‹ โ€” warns when -ftrivial-auto-var-init can't initialize an automatic variable. The most common way this happens is when an automatic variable is declared between the controlling expression and the first case label of a switch statement. Not it ๐Ÿ’€
  • -Wunused-but-set-parameter ๐ŸŽ€ โ€” warns whenever a function parameter gets assigned to but is otherwise unused, aside from its declaration. To shut this up, use the unused attribute. It's also turned on by -Wunused together with -Wextra ๐Ÿงš
  • -Wunused-but-set-variable ๐Ÿ’œ โ€” same energy, but for local variables: warns whenever one gets assigned to and then never used (aside from its declaration). It's enabled by -Wall โœจ To suppress it, use the unused attribute. It's also enabled by -Wunused, which -Wall turns on ๐Ÿ‘‘
  • -Wunused-function ๐Ÿ“ โ€” warns whenever a static function is declared but not defined, or when a non-inline static function is unused. Enabled by -Wall ๐Ÿ’…
  • -Wunused-label ๐ŸŒˆ โ€” warns whenever a label is declared but never used. Enabled by -Wall. Silence it with the unused attribute ๐Ÿ™ˆ
  • -Wunused-local-typedefs (C, Objective-C, C++ and Objective-C++ only) โญ โ€” warns when a typedef defined locally inside a function never gets used. Enabled by -Wall โœจ
  • -Wunused-parameter ๐Ÿซถ โ€” warns whenever a function parameter is unused, aside from its declaration. This one is not turned on by -Wunused unless you also specify -Wextra. To suppress it, use the unused attribute ๐Ÿ”ฅ
  • -Wno-unused-result ๐Ÿงš โ€” don't warn if a caller of a function marked with the attribute warn_unused_result ignores its return value. The default is -Wunused-result ๐Ÿ’–
  • -Wunused-variable ๐Ÿ’… โ€” warns whenever a local or static variable is unused, aside from its declaration. This option implies -Wunused-const-variable=1 for C, but not for C++. Enabled by -Wall. To suppress it, use the unused attribute โœจ
  • -Wunused-const-variable, -Wunused-const-variable=n ๐ŸŒธ โ€” warns whenever a constant static variable is unused, aside from its declaration. Silence it with the unused attribute ๐Ÿฆ‹
    • -Wunused-const-variable=1 ๐Ÿ’œ โ€” warns about unused static const variables defined in the main compilation unit, but not about static const variables declared in any header that gets included. This level is enabled by either -Wunused-variable or -Wunused for C, but not for C++. In C this declares variable storage, but in C++ it's not an error, since const variables take the place of #defines ๐Ÿซถ
    • -Wunused-const-variable=2 ๐Ÿ”ฅ โ€” this level also warns about unused constant static variables in headers (excluding system headers). It's equivalent to the short form -Wunused-const-variable. You have to request this level explicitly in both C and C++, because it might be hard to clean up all the headers you've included ๐Ÿ’€
  • -Wunused-value ๐Ÿ“ โ€” warns whenever a statement computes a result that is explicitly not used. To suppress it, cast the unused expression to void. This covers an expression-statement or the left-hand side of a comma expression that has no side effects. For example, x[i,j] causes a warning, while x[(void)i,j] does not. Enabled by -Wall ๐Ÿ‘‘
  • -Wunused ๐ŸŒˆ โ€” all of the above -Wunused options combined, except the ones documented as needing to be specified explicitly. To get a warning about an unused function parameter, you must either specify -Wextra -Wunused (note that -Wall implies -Wunused), or separately specify -Wunused-parameter and/or -Wunused-but-set-parameter. -Wunused enables only -Wunused-const-variable=1 rather than -Wunused-const-variable, and only for C, not C++ โœจ
  • -Wuse-after-free (C, Objective-C, C++ and Objective-C++ only), -Wuse-after-free=n โญ โ€” warns about uses of pointers to dynamically allocated objects that were made indeterminate by a call to a deallocation function. This warning is enabled at all optimization levels, but it may give different results with optimization than without ๐Ÿ”ฅ
    • -Wuse-after-free=1 ๐Ÿฆ‹ โ€” at level 1 the warning only tries to catch unconditional uses of pointers made indeterminate by a deallocation call, or by a successful call to realloc, no matter whether that call actually reallocated memory. This includes double-free calls as well as uses in arithmetic and relational expressions. Uses of indeterminate pointers in equality (or inequality) expressions are undefined, but they are not diagnosed at this level ๐Ÿ™ˆ
    • -Wuse-after-free=2 ๐Ÿ’– โ€” at level 2, on top of unconditional uses, the warning also diagnoses conditional uses of pointers made indeterminate by a deallocation call. As at level 2, uses in equality (or inequality) expressions are not diagnosed. For example, the second call to free in the following function gets diagnosed at this level:
struct A { int refcount; void *data; };

void release (struct A *p)
{
  int refcount = --p->refcount;
  free (p);
  if (refcount == 0)
    free (p->data);   // warning: p may be used after free
}
  • -Wuse-after-free=3 ๐ŸŒˆ โ€” at level 3, the warning also diagnoses uses of indeterminate pointers in equality expressions. All uses of indeterminate pointers are undefined, but equality tests sometimes show up after calls to realloc as an attempt to figure out whether the call moved the object to a different address. These get diagnosed at their own separate level to help gradually move legacy code over to safe alternatives. For example, the equality test in the function below is diagnosed at this level:
void adjust_pointers (int**, int);

void grow (int **p, int n)
{
  int **q = (int**)realloc (p, n *= 2);
  if (q == p)
    return;
  adjust_pointers ((int**)q, n);
}

To avoid the warning at this level, store offsets into allocated memory instead of pointers. That approach means you don't need to adjust the stored pointers after reallocation ๐Ÿงš

  • -Wuse-after-free=2 is included in -Wall ๐Ÿ‘‘
  • -Wuseless-cast (C, Objective-C, C++ and Objective-C++ only) ๐ŸŽ€ โ€” warns when an expression is cast to its own type. This warning does not occur when a class object is converted to a non-reference type, because that's a way to create a temporary. Like this:
struct S { };
void g (S&&);
void f (S&& arg)
{
  g (S(arg)); // make arg prvalue so that it can bind to S&&
}
  • -Wuninitialized ๐Ÿ˜ญ โ€” warns if an object with automatic or allocated storage duration gets used without having been initialized. In C++, it also warns if a non-static reference or a non-static const member shows up in a class without constructors ๐Ÿ’…

Also, passing a pointer (or, in C++, a reference) to an uninitialized object to a const-qualified argument of a built-in function known to read that object is diagnosed by this warning too. (-Wmaybe-uninitialized is issued for ordinary functions.) ๐Ÿซถ

If you want to warn about code that uses the uninitialized value of a variable in its own initializer, use the -Winit-self option โœจ

These warnings show up for individual uninitialized elements of structure, union or array variables, as well as for variables that are uninitialized as a whole. They do not occur for variables or elements declared volatile. Because these warnings depend on optimization, the exact variables or elements that get warned about depend on the precise optimization options and the GCC version you're using ๐Ÿ”ฅ

Also, there may be no warning about a variable that's only used to compute a value that itself is never used, because data flow analysis can delete those computations before the warnings get printed ๐Ÿ™ˆ

In C++, this warning also catches using uninitialized objects in member-initializer-lists. For example, GCC warns about b being uninitialized in the following snippet:

struct A {
  int a;
  int b;
  A() : a(b) { }
};
  • -Wno-invalid-memory-model ๐Ÿฆ‹ โ€” this option controls warnings for invocations of __atomic built-ins, __sync built-ins, and the C11 atomic generic functions whose memory consistency argument is either invalid for the operation or outside the range of values of the memory_order enumeration. For example, since __atomic_store and __atomic_store_n are only defined for the relaxed, release, and sequentially consistent memory orders, the following code gets diagnosed:
void store (int *i)
{
  __atomic_store_n (i, 0, memory_order_consume);
}

-Winvalid-memory-model is enabled by default ๐ŸŒธ

  • -Wmaybe-uninitialized ๐Ÿ’œ โ€” for an object with automatic or allocated storage duration, if there's a path from the function entry to a use of the object where it is initialized, but there are other paths where it isn't, the compiler emits a warning if it can't prove those uninitialized paths never run at runtime. It's giving "maybe, but I'm not sure" ๐Ÿงš

Okay so the uninitialized warning has a sneaky extra trick, bestie ๐Ÿ’…. Passing a pointer (or in C++, a reference) to an uninitialized object into a const-qualified function argument also gets flagged by this warning ๐Ÿ‘€. (-Wuninitialized fires for built-in functions that are known to read the object ๐Ÿซถ.) If you slap the attribute access (none) on the function, you're telling GCC the argument never touches the object, and the warning goes poof โœจ.

These warnings only show up when you're compiling with optimization on ๐Ÿ”ฅ, because otherwise GCC doesn't keep track of what's happening to your variables. It's giving "no tracking, no vibes" ๐Ÿ’€.

These warnings are optional, and that's on purpose ๐Ÿฆ‹. GCC can't always tell when your code is correct even though it looks sus. Here's a classic example ๐Ÿ‘‡

{
  int x;
  switch (y)
    {
    case 1: x = 1;
      break;
    case 2: x = 4;
      break;
    case 3: x = 5;
    }
  foo (x);
}

If y is always 1, 2 or 3, then x is always initialized ๐Ÿฅน, but GCC doesn't know that. To shut the warning up, add a default case with assert(0) or similar code ๐Ÿ™ˆ.

This option also warns when a non-volatile automatic variable might get changed by a call to longjmp ๐Ÿ˜ญ. The compiler only sees the setjmp calls, and it has no idea where longjmp will be called. A signal handler could even call it at any point in the code ๐ŸŒˆ. So you might get a warning even when there's no real problem, because longjmp can't actually be called in the spot that would cause trouble โœจ.

Some spurious warnings can be avoided if you declare every function you use that never returns as noreturn ๐Ÿงš.

This warning is enabled by -Wall or -Wextra ๐ŸŽ€.


  • -Wunknown-pragmas โ€” Warns when a #pragma directive shows up that GCC doesn't understand ๐Ÿ‘€. If you use this command-line option, you'll even get warnings for unknown pragmas in system header files ๐Ÿซฃ. That's not the case if the warnings are only turned on by -Wall ๐Ÿ’….
  • -Wno-pragmas โ€” Silences warnings about pragma misuse, like wrong parameters, invalid syntax, or pragmas clashing with each other ๐Ÿ™ˆ. See also -Wunknown-pragmas ๐Ÿฆ‹.
  • -Wno-pragma-once-outside-header โ€” Stops the warning when #pragma once is used in a file that isn't a header, like your main file ๐ŸŒธ.
  • -Wno-prio-ctor-dtor โ€” Stops the warning when a priority from 0 to 100 is used for a constructor or destructor ๐Ÿ“. The constructor and destructor attributes let you give a priority to control when they run, either before main is called or after it returns โญ. Just know the priority values have to be greater than 100, since GCC keeps 0--100 reserved for its own implementation ๐Ÿ”’.
  • -Wstrict-aliasing โ€” This one only works when -fstrict-aliasing is active ๐Ÿ”ฅ. It warns about code that might break the strict aliasing rules GCC uses for optimization ๐Ÿ’œ. It doesn't catch every case, but it does try to catch the more common pitfalls ๐Ÿซถ. It's included in -Wall โœจ. It's equivalent to -Wstrict-aliasing=3 ๐Ÿ‘‘.
  • -Wstrict-aliasing=n โ€” Also only active when -fstrict-aliasing is active ๐ŸŽ€. It warns about the same kind of code, but higher levels mean higher accuracy (fewer false positives) ๐Ÿ’–. Higher levels also take more effort, kind of like how -O works ๐Ÿงš. -Wstrict-aliasing is equivalent to -Wstrict-aliasing=3 ๐Ÿ’….
    • Level 1 โ€” The most aggressive, quick, and least accurate ๐Ÿ˜ญ. It's possibly useful when the higher levels don't warn but -fstrict-aliasing still breaks your code, since it has very few false negatives ๐Ÿ‘€. The downside is it has many false positives ๐Ÿ’€. It warns for all pointer conversions between possibly incompatible types, even if the pointer is never dereferenced ๐Ÿ™ˆ. It only runs in the front end ๐ŸŒˆ.
    • Level 2 โ€” Aggressive and quick, but not super precise โœจ. It may still have lots of false positives (though not as many as level 1) and few false negatives (possibly more than level 1) ๐Ÿฆ‹. Unlike level 1, it only warns when an address is taken ๐ŸŒธ. It warns about incomplete types ๐Ÿ“. It runs in the front end only ๐Ÿ’œ.
    • Level 3 (the default for -Wstrict-aliasing) โ€” This one should have very few false positives and few false negatives ๐Ÿ‘‘. It's slightly slower than levels 1 or 2 when optimization is enabled ๐Ÿข. It handles the common pun+dereference pattern in the front end, like *(int*)&some_float ๐Ÿซถ. If optimization is enabled, it also runs in the back end, where it deals with multi-statement cases using flow-sensitive points-to information ๐Ÿง . It only warns when the converted pointer is dereferenced ๐Ÿ”ฅ. It does not warn about incomplete types ๐Ÿ™….
  • -Wstrict-overflow, -Wstrict-overflow=n โ€” This only works when signed overflow is undefined ๐ŸŒˆ. It warns about cases where the compiler optimizes based on the assumption that signed overflow never happens โœจ. Heads up: it doesn't warn about every spot where your code might overflow ๐Ÿ‘€. It only warns where the compiler actually applies some optimization, so this warning depends on your optimization level ๐Ÿ’…. An optimization that assumes no signed overflow is totally safe if the values involved never actually overflow ๐Ÿฅน. That means this warning can easily give a false positive, aka a warning about code that isn't actually a problem ๐Ÿ™ˆ. To help you focus on the important stuff, there are several warning levels ๐Ÿ’–. No warnings get issued for undefined signed overflow when GCC is estimating how many iterations a loop needs, especially when figuring out whether a loop runs at all ๐Ÿ”.
    • -Wstrict-overflow=1 โ€” Warns about cases that are both questionable and easy to avoid ๐Ÿฆ‹. For example, the compiler simplifies x + 1 > x to 1 ๐Ÿซฃ. This level is enabled by -Wall; higher levels aren't, so you have to ask for them explicitly ๐Ÿ’….
    • -Wstrict-overflow=2 โ€” Also warns about other cases where a comparison gets simplified to a constant ๐Ÿ‘‘. For example: abs (x) >= 0. This can only be simplified when signed integer overflow is undefined, because abs (INT_MIN) overflows to INT_MIN, which is less than zero ๐Ÿ˜ฑ. -Wstrict-overflow with no level is the same as -Wstrict-overflow=2 ๐ŸŽ€.
    • -Wstrict-overflow=3 โ€” Also warns about other comparisons that get simplified ๐Ÿงš. For example: x + 1 > 1 is simplified to x > 0 ๐Ÿ’œ.
    • -Wstrict-overflow=4 โ€” Also warns about other simplifications not covered by the cases above ๐ŸŒธ. For example: (x * 10) / 5 is simplified to x * 2 โœจ.
    • -Wstrict-overflow=5 โ€” Also warns about cases where the compiler reduces the magnitude of a constant involved in a comparison ๐Ÿ˜ญ. For example: x + 2 > y is simplified to x + 1 >= y ๐Ÿ“. This is only reported at the highest warning level, because this simplification applies to so many comparisons that this level gives a very large number of false positives ๐Ÿ’€.
  • -Wstring-compare โ€” Warns for calls to strcmp and strncmp whose result is determined to be always zero or always non-zero in equality tests ๐Ÿ‘€. That happens because one argument is longer than the array the other argument is stored in (or the bound, for strncmp) ๐Ÿซฃ. These calls could be mistakes ๐Ÿ™ˆ. In the example below, the strcmp call gets diagnosed because its result is necessarily non-zero no matter what's in the array a ๐Ÿ’….
extern char a[4];
void f (char *d)
{
  strcpy (d, "string");
  ...
  if (0 == strcmp (a, d))   // cannot be true
    puts ("a and d are the same");
}

-Wstring-compare is enabled by -Wextra ๐ŸŽ€.

  • -Wno-stringop-overflow, -Wstringop-overflow, -Wstringop-overflow=type โ€” Warns about code that can be statically determined to cause buffer overflows or memory overruns ๐Ÿšจ, like memcpy and strcpy calls that overflow the destination buffer ๐Ÿ’ฅ. The optional argument is one greater than the type of Object Size Checking used to figure out the size of the destination ๐Ÿงฎ. The argument only matters for string functions that work on character arrays ๐Ÿ”ค. Raw memory functions like memcpy always use type-zero Object Size Checking ๐Ÿง . This option also warns for calls that specify a size bigger than the largest possible object, or at most SIZE_MAX / 2 bytes ๐Ÿ“. It gives the best results with optimization enabled ๐Ÿ”ฅ. Even without optimization, it can detect a small subset of simple buffer overflows in calls to GCC built-in functions like __builtin_memcpy that match the standard functions ๐Ÿซถ. Either way, it only warns about a subset of the overflows caught by the matching overflow checking built-ins, like __builtin___memcpy_chk, which can do run-time checking when the access can't be proven safe at compile time ๐Ÿ•ต๏ธโ€โ™€๏ธ. For example, this option issues a warning for the strcpy call below, because it copies at least 5 characters (the string "blue" including the terminating NUL) into a buffer of size 4 ๐Ÿ˜ญ.
enum Color { blue, purple, yellow };
const char* f (enum Color clr)
{
  static char buf [4];
  const char *str;
  switch (clr)
    {
      case blue: str = "blue"; break;
      case purple: str = "purple"; break;
      case yellow: str = "yellow"; break;
    }

  return strcpy (buf, str);   // warning here
}

The effect of this option isn't limited to string or memory functions ๐Ÿ™Œ. In this example, a warning is diagnosed because a 1-element array is passed to a function that needs at least a 4-element array argument ๐Ÿคฏ.

void f (int[static 4]);

void g (void)
{
  int *p = (int *) malloc (1 * sizeof(int));
  f (p);   // warning here
}

Option -Wstringop-overflow=2 is enabled by default ๐Ÿ’–.

  • -Wstringop-overflow, -Wstringop-overflow=1 โ€” okay so this one uses type-zero Object Size Checking ๐ŸŽ€ to figure out how big destination objects are. At this setting it doesn't warn about writes past the end of a subobject inside a bigger object reached through a pointer, unless the size of the biggest surrounding object is known. If the destination could be one of several objects, it assumes the biggest one ๐Ÿ’…. On Linux, with optimization turned on at this level, it warns for the same code that trips up when the _FORTIFY_SOURCE macro is defined to a non-zero value โœจ
  • -Wstringop-overflow=2 โ€” this one switches to type-one Object Size Checking ๐Ÿฆ‹ and warns about overflows when writing to members of the largest complete objects whose exact size is known. It won't flag excessive writes to the same members of unknown objects reached through pointers, since those might point at arrays with an unknown number of elements ๐Ÿคทโ€โ™€๏ธ This is the default setting of the option ๐Ÿ‘‘
  • -Wstringop-overflow=3 โ€” type-two Object Size Checking is in charge here ๐Ÿ’œ It warns about overflowing the smallest object or data member. This is the most restrictive setting that may still warn about perfectly safe code ๐Ÿ™ˆ
  • -Wstringop-overflow=4 โ€” now we're on type-three Object Size Checking ๐Ÿ”ฅ It warns about overflowing any data members, and when the destination is one of several objects it uses the size of the largest one to decide whether to warn. Just like -Wstringop-overflow=3, this setting may warn about benign code ๐Ÿ’€

  • -Wno-stringop-overread โ€” warn for calls to string manipulation functions like memchr or strcpy that are determined to read past the end of the source sequence ๐Ÿ‘€

Option -Wstringop-overread is enabled by default โœ…

  • -Wno-stringop-truncation โ€” no warnings for bounded string functions like strncat, strncpy, and stpncpy that might either truncate the copied string or leave the destination unchanged ๐ŸŒธ

Okay, example time ๐Ÿงš In the snippet below, the call to strncat sets a bound that's smaller than the length of the source string. So the copy gets truncated, and that's why the call is diagnosed. To dodge the warning, use bufsize - strlen (buf) - 1) as the bound ๐Ÿ’–

void append (char *buf, size_t bufsize)
{
  strncat (buf, ".txt", 3);
}

Next up, the strncpy call here copies just the characters before the terminating NUL into d, and it doesn't append the NUL to the end. Assuming the result of strncpy is definitely a NUL-terminated string is a super common mistake, so this call gets diagnosed ๐Ÿ˜ญ If the result isn't supposed to be NUL-terminated, call memcpy instead to avoid the warning ๐Ÿซถ

void copy (char *d, const char *s)
{
  strncpy (d, s, strlen (s));
}

In this one, the strncpy call uses the size of the destination buffer as its bound. If the source string's length is equal to or greater than that size, the copy won't be NUL-terminated, so yep, it gets diagnosed too ๐Ÿ™ƒ To avoid the warning, specify sizeof buf - 1 as the bound and set the last element of the buffer to NUL ๐Ÿ“

void copy (const char *s)
{
  char buf[80];
  strncpy (buf, s, sizeof buf);
  ...
}

Now, if a character array is meant to hold a sequence of bytes with no terminating NUL, you can annotate that array with the nonstring attribute to avoid this warning ๐ŸŒˆ Just know these arrays aren't suitable arguments for functions that expect NUL-terminated strings. To help catch accidental misuses of them, GCC issues warnings unless it can prove the use is safe ๐Ÿ•ต๏ธโ€โ™€๏ธ


  • -Wstrict-flex-arrays (C and C++ only) โ€” warns about improper usages of flexible array members, based on the level of the strict_flex_array (level) attribute attached to the trailing array field of a structure if it's available. Otherwise it goes by the level of the option -fstrict-flex-arrays=level โœจ -Wstrict-flex-arrays only does anything when level is greater than 0 ๐ŸŽ€

At level=1, warnings are issued for a trailing array reference of a structure that has 2 or more elements, if that trailing array is referenced as a flexible array member ๐Ÿฆ‹

At level=2, you get everything from level=1, plus extra warnings for a trailing one-element array reference of a structure if the array is referenced as a flexible array member ๐Ÿ’œ

At level=3, you get everything from level=2, plus extra warnings for a trailing zero-length array reference of a structure if the array is referenced as a flexible array member ๐Ÿ”ฅ

This option is more effective when -ftree-vrp is active (which is the default for -O2 and above), but some warnings may still be diagnosed even without optimization ๐Ÿค“


  • -Wsuggest-attribute=attribute-name โ€” warns for cases where adding an attribute might be beneficial ๐Ÿ’… The attribute names currently supported are:
    • -Wsuggest-attribute=pure
    • -Wsuggest-attribute=const
    • -Wsuggest-attribute=noreturn
    • -Wmissing-noreturn
    • -Wsuggest-attribute=malloc
    • -Wsuggest-attribute=returns_nonnull

These warn about functions that might be good candidates for the pure, const, noreturn, malloc, or returns_nonnull attributes ๐ŸŒŸ The compiler only warns for functions that are visible in other compilation units, or (for pure and const) if it can't prove that the function returns normally. A function returns normally if it doesn't contain an infinite loop and doesn't return abnormally by throwing, calling abort, or trapping ๐Ÿ™Œ

This analysis requires the option -fipa-pure-const, which is enabled by default at -O and higher. Higher optimization levels improve how accurate the analysis is ๐Ÿ“ˆ

  • -Wsuggest-attribute=format, -Wmissing-format-attribute โ€” warns about function pointers that might be candidates for format attributes ๐Ÿงš Just a heads up: these are only possible candidates, not absolute ones ๐Ÿ™ˆ GCC guesses that function pointers with format attributes that get used in assignment, initialization, parameter passing, or return statements should have a matching format attribute in the resulting type. In other words, the left-hand side of the assignment or initialization, the type of the parameter variable, or the return type of the containing function should also have a format attribute to avoid the warning ๐Ÿ’–

GCC also warns about function definitions that might be candidates for format attributes ๐Ÿ‘€ Again, these are only possible candidates. GCC guesses that format attributes might be appropriate for any function that calls a function like vprintf or vscanf, but that might not always be the case, and some functions where format attributes are appropriate may not get detected ๐Ÿซ 

  • -Wsuggest-attribute=cold โ€” warns about functions that might be candidates for the cold attribute โ„๏ธ This is based on static detection, and it generally only warns about functions that always lead to a call to another cold function, like wrappers of C++ throw or fatal error reporting functions that lead to abort ๐Ÿฅถ

  • -Walloc-size โ€” warns about calls to allocation functions decorated with the alloc_size attribute that specify an insufficient size for the target type of the pointer the result is assigned to ๐Ÿฐ This includes calls to the built-in forms of aligned_alloc, alloca, calloc, malloc, and realloc ๐ŸŽ‚
  • -Walloc-zero โ€” warns about calls to allocation functions decorated with the alloc_size attribute that specify zero bytes, including calls to the built-in forms of aligned_alloc, alloca, calloc, malloc, and realloc ๐Ÿ™…โ€โ™€๏ธ Here's the tea: how these functions behave when called with a zero size differs between implementations (and realloc has been deprecated for this), so relying on it can cause sneaky portability bugs and should be avoided ๐Ÿซฃ
  • -Wcalloc-transposed-args โ€” warns about calls to allocation functions decorated with the alloc_size attribute that take two arguments, where the sizeof operator is used in the earlier size argument but not in the later one ๐Ÿ”„ This is a coding style warning โœจ The first argument to calloc is documented as the number of elements in the array, and the second is the size of each element, so calloc (n, sizeof (int)) is preferred over calloc (sizeof (int), n) ๐ŸŒท If having sizeof in the earlier argument and not the later one is intentional, you can suppress the warning by using calloc (sizeof (struct S) + 0, n) or calloc (1 * sizeof (struct S), 4), or by using sizeof in the later argument as well ๐Ÿ™Œ
  • -Walloc-size-larger-than=byte-size โ€” warns about calls to functions decorated with the alloc_size attribute that try to allocate objects larger than the specified number of bytes ๐Ÿ“ It also warns when the result of the size computation, done in an integer type with infinite precision, would exceed the value of PTRDIFF_MAX on the target ๐ŸŽฏ -Walloc-size-larger-than=PTRDIFF_MAX is enabled by default โœ… You can disable warnings from this option either by specifying a byte-size of SIZE_MAX or more, or by using -Wno-alloc-size-larger-than ๐Ÿซถ
  • -Wno-alloc-size-larger-than โ€” disables -Walloc-size-larger-than= warnings ๐Ÿ™…โ€โ™€๏ธ It's the same as -Walloc-size-larger-than=SIZE_MAX or larger ๐Ÿ’…
  • -Walloca โ€” warns on all uses of alloca in the source ๐Ÿšจ No exceptions, bestie ๐Ÿ˜ค
  • -Walloca-larger-than=byte-size ๐ŸŽ€ โ€” okay so this one babysits your alloca calls. It warns when alloca gets an integer argument that's either zero, or isn't bounded by some check that caps it at byte-size bytes. It also yells when the bound is unknown entirely. Heads up: non-integer arguments count as unbounded, even if they look like they're in range. ๐Ÿ’€

A bounded alloca looks like this, bestie:

void func (size_t n)
{
  void *p;
  if (n <= 1000)
    p = alloca (n);
  else
    p = malloc (n);
  f (p);
}

With -Walloca-larger-than=1000, you get no warning, because that alloca is known to be at most 1000 bytes. But if you pass -Walloca-larger-than=500 instead, the compiler will absolutely warn you. ๐Ÿ™ˆ

Unbounded uses are the chaotic ones: alloca with no controlling predicate constraining its integer argument. Like this:

void func ()
{
  void *p = alloca (n);
  f (p);
}

Pass -Walloca-larger-than=500 and this one gets flagged, but this time it's because there's zero bounds checking happening. ๐Ÿ˜ญ

Even code that seems totally correct with signed integers can trigger it, so be careful:

void func (signed int n)
{
  if (n < 500)
    {
      p = alloca (n);
      f (p);
    }
}

Here n could be negative, and then a much larger value gets implicitly cast into the alloca call. Not it. ๐Ÿ’…

This option also warns when alloca shows up inside a loop. ๐Ÿ”

-Walloca-larger-than=PTRDIFF_MAX is on by default, but it's usually only really effective when -ftree-vrp is active, which is the default for -O2 and above. โœจ

See also -Wvla-larger-than=byte-size. ๐Ÿฆ‹

  • -Wno-alloca-larger-than ๐Ÿงš โ€” switches off the -Walloca-larger-than= warnings. It's the same as passing -Walloca-larger-than=SIZE_MAX or something larger. ๐Ÿ’œ
  • -Warith-conversion โœจ โ€” this one is extra. It makes GCC warn about implicit conversions coming out of arithmetic operations, even when converting the operands to the same type couldn't change their values. It affects the warnings from -Wconversion, -Wfloat-conversion, and -Wsign-conversion. Check the vibes:
void f (char c, int i)
{
  c = c + i; // warns with -Wconversion
  c = c + 1; // only warns with -Warith-conversion
}
  • -Warray-bounds, -Warray-bounds=n ๐ŸŒธ โ€” warns about out of bounds subscripts or offsets into arrays. It's enabled by -Wall. It's way more effective when -ftree-vrp is active (the default for -O2 and above), but a subset of cases still gets flagged even without optimization. ๐Ÿ‘‘

By default, the trailing array of a structure gets treated as a flexible array member by -Warray-bounds or -Warray-bounds=n if it's declared as any of these: a flexible array member per the C99 standard onwards ([]), a GCC zero-length array extension ([0]), or a one-element array ([1]). So out of bounds subscripts or offsets into zero-length arrays or one-element arrays are not warned about by default. ๐Ÿ™ƒ

You can add -fstrict-flex-arrays or -fstrict-flex-arrays=level to control how this option treats that trailing array:

  • when level<=1, nothing changes from the default behavior. ๐Ÿคทโ€โ™€๏ธ
  • when level=2, you get additional warnings for out of bounds subscripts or offsets into one-element arrays. ๐Ÿซถ
  • when level=3, you get everything from level 2, plus additional warnings for out of bounds subscripts or offsets into zero-length arrays. ๐Ÿ”ฅ
  • -Warray-bounds=1 ๐Ÿ’– โ€” this is the default warning level of -Warray-bounds, and it's enabled by -Wall. The higher levels aren't, so you have to ask for them explicitly.
  • -Warray-bounds=2 ๐Ÿ“ โ€” this level also warns about the intermediate results of pointer arithmetic that might yield out of bounds values. Heads up, it can give you a bigger pile of false positives, so it's deactivated by default. ๐Ÿ’€
  • -Wunterminated-string-initialization (C and Objective-C only) ๐ŸŽ€ โ€” warns about character arrays initialized as unterminated character sequences with a string literal, unless the declaration being initialized has the nonstring attribute. For example:
char arr[3] = "foo"; /* Warning.  */
char arr2[3] __attribute__((nonstring)) = "bar"; /* No warning.  */

This one is enabled by -Wextra. If -Wc++-compat is enabled, the warning uses slightly different wording and warns even if the declaration has the nonstring attribute, because in C++ those initializations are an error. ๐Ÿ˜ญ

  • -Warray-compare ๐Ÿฆ‹ โ€” warns about equality and relational comparisons between two operands of array type. This comparison was deprecated in C++20. Example:
int arr1[5];
int arr2[5];
bool same = arr1 == arr2;

-Warray-compare is enabled by -Wall. โœจ

  • -Warray-parameter, -Warray-parameter=n ๐Ÿ‘‘ โ€” warns about redeclarations of functions involving parameters of array or pointer types with inconsistent kinds or forms. It also turns on detection of out-of-bounds accesses to those parameters by warnings like -Warray-bounds.

If the first function declaration uses the array form for a parameter, the bound in the array is assumed to be the minimum number of elements expected in calls to the function, and the maximum number of elements it accesses. Passing arguments of insufficient size, or accessing more than the maximum number of elements, may get diagnosed by warnings like -Warray-bounds or -Wstringop-overflow. At level 1, the warning diagnoses inconsistencies involving array parameters declared with the T[static N] form. ๐ŸŒˆ

Here the warning triggers on the second declaration of f. The first one, with the keyword static, says the array argument must have at least four elements, but the second one allows an array of any size to be passed:

void f (int[static 4]);
void f (int[]);           // warning (inconsistent array form)

void g (void)
{
  int *p = (int *)malloc (1 * sizeof (int));
  f (p);                  // warning (array too small)
  ...
}

At level 2, the warning also triggers for redeclarations involving any other inconsistency in array or pointer argument forms that denote array sizes. Pointers and arrays of unspecified bound are considered equivalent, so they don't trigger a warning:

void g (int*);
void g (int[]);     // no warning
void g (int[8]);    // warning (inconsistent array bound)

-Warray-parameter=2 is included in -Wall. The -Wvla-parameter option triggers warnings for similar inconsistencies involving Variable Length Array arguments. ๐Ÿงš

The short form -Warray-parameter is equivalent to -Warray-parameter=2. The negative form -Wno-array-parameter is equivalent to -Warray-parameter=0. ๐Ÿ’œ

  • -Wattribute-alias=n, -Wno-attribute-alias ๐ŸŒธ โ€” warns about declarations using the alias and similar attributes whose target is incompatible with the type of the alias.
  • -Wattribute-alias=1 ๐Ÿ’– โ€” the default warning level diagnoses incompatibilities between the type of the alias declaration and that of its target. These are usually signs of bugs, bestie. ๐Ÿ›
  • -Wattribute-alias=2 ๐Ÿ”ฅ โ€” at this level, it also diagnoses cases where the attributes of the alias declaration are more restrictive than the attributes applied to its target. Those mismatches can potentially lead to incorrect code generation. Other times they're benign and could be fixed just by adding the missing attribute to the target. For comparison, see the -Wmissing-attributes option, which controls diagnostics when the alias declaration is less restrictive than the target, rather than more restrictive.

Attributes considered include alloc_align, alloc_size, cold, const, hot, leaf, malloc, nonnull, noreturn, nothrow, pure, returns_nonnull, and returns_twice. ๐Ÿซถ

-Wattribute-alias is equivalent to -Wattribute-alias=1, and that's the default. You can disable these warnings with either -Wno-attribute-alias or -Wattribute-alias=0. ๐Ÿ™ˆ

  • -Wbidi-chars=[none|unpaired|any|ucn] ๐ŸŒˆ โ€” warns about possibly misleading UTF-8 bidirectional control characters in comments, string literals, character constants, and identifiers. These characters can flip the writing direction from left-to-right into right-to-left (and vice versa), which can cause confusion between logical order and visual order. This is seriously dangerous, because code can look commented out when it actually isn't. ๐Ÿ˜ฑ

GCC supports three levels of this warning. The default is -Wbidi-chars=unpaired, which warns about improperly terminated bidi contexts. -Wbidi-chars=none turns the warning off. -Wbidi-chars=any warns about any use of bidirectional control characters. โœจ

By default, this warning doesn't check UCNs. You can turn that checking on with -Wbidi-chars=unpaired,ucn or -Wbidi-chars=any,ucn. Using -Wbidi-chars=ucn is valid too, and it's equivalent to -Wbidi-chars=unpaired,ucn, as long as no previous -Wbidi-chars=any was specified. ๐Ÿ”

NOTE: In Ubuntu 25.10 and later versions, -Wbidi-chars is enabled by default for C, C++, ObjC, ObjC++, unless -Wno-bidi-chars or -Wbidi-chars=none is found. ๐Ÿพ
  • -Wbool-compare ๐Ÿ’… โ€” warns about a boolean expression being compared with an integer value different from "true"/"false". For instance, the following comparison is always false:
int n = 5;
...
if ((n > 1) == 2) { ... }

This warning is enabled by -Wall. ๐Ÿ‘‘

๐Ÿ’– okay so the warnings lineup continues, bestie, and it's giving a lot of good stuff ๐ŸŽ€

  • -Wbool-operation โ€” ๐Ÿ” Warns about sus operations on boolean expressions. Bitwise negation of a boolean is very likely a bug in your program, no cap ๐Ÿ’€ For C, it also warns when you increment or decrement a boolean, which rarely makes sense. (In C++, decrementing a boolean is always invalid. Incrementing one is invalid in C++17 and deprecated in older versions.) This one is turned on by -Wall โœจ
  • -Wduplicated-branches โ€” ๐Ÿชž Warns when an if-else has identical branches. Like this:
if (p != NULL)
  return 0;
else
  return 0;

It won't warn if both branches contain just a null statement. It also warns for conditional operators, so this one's flagged too:

int i = x ? *p : *p;
  • -Wduplicated-cond โ€” ๐Ÿ‘ฏ Warns about duplicated conditions in an if-else-if chain. For example, this gets called out:
if (p->q != NULL) { ... }
else if (p->q != NULL) { ... }
  • -Wframe-address โ€” ๐Ÿšจ Warns when __builtin_frame_address or __builtin_return_address gets called with an argument greater than 0. These calls may return indeterminate values or crash your program, so yeah, be careful bestie ๐Ÿ™ˆ This one is included in -Wall.
  • -Wno-discarded-qualifiers (C and Objective-C only) โ€” ๐Ÿซ  Don't warn if type qualifiers on pointers are getting discarded. Usually the compiler complains if a const char * variable gets passed to a function that wants a char * parameter. This option silences that.
  • -Wno-discarded-array-qualifiers (C and Objective-C only) โ€” ๐Ÿงบ Don't warn if type qualifiers on arrays that are pointer targets get discarded. Typically the compiler warns when a const int (*)[] variable is passed to a function taking an int (*)[] parameter. This option suppresses that warning.
  • -Wno-incompatible-pointer-types (C and Objective-C only) โ€” ๐Ÿ™…โ€โ™€๏ธ Don't warn when there's a conversion between pointers with incompatible types. This covers cases not handled by -Wno-pointer-sign, which warns about pointer argument passing or assignment with different signedness.

By default, in C99 and later dialects of C, GCC treats this as an error ๐Ÿ˜ค You can downgrade it to a warning with -fpermissive (along with certain other errors), or just this one error with -Wno-error=incompatible-pointer-types.

This warning gets upgraded to an error by -pedantic-errors.

  • -Wno-int-conversion (C and Objective-C only) โ€” ๐Ÿ”ข Don't warn about incompatible integer-to-pointer and pointer-to-integer conversions. This one is about implicit conversions. For explicit conversions, you can use -Wno-int-to-pointer-cast and -Wno-pointer-to-int-cast instead.

By default, in C99 and later dialects of C, GCC treats this as an error ๐Ÿ˜ค You can downgrade it to a warning with -fpermissive (along with certain other errors), or just this one with -Wno-error=int-conversion.

This warning gets upgraded to an error by -pedantic-errors.

  • -Wzero-as-null-pointer-constant โ€” 0๏ธโƒฃ Warns when a literal 0 gets used as a null pointer constant.
  • -Wzero-length-bounds โ€” ๐Ÿ“ Warns about accesses to elements of zero-length array members that might overlap other members of the same object. Interior zero-length arrays are discouraged, because accessing them is undefined ๐Ÿซฃ

Check this example. The first two stores in function bad get flagged because those array elements overlap the members b and c that come after them. The third store is caught by -Warray-bounds instead, since it's past the end of the enclosing object:

struct X { int a[0]; int b, c; };
struct X x;

void bad (void)
{
  x.a[0] = 0;   // -Wzero-length-bounds
  x.a[1] = 1;   // -Wzero-length-bounds
  x.a[2] = 2;   // -Warray-bounds
}

Option -Wzero-length-bounds is enabled by -Warray-bounds.

  • -Wno-div-by-zero โ€” โž— Don't warn about compile-time integer division by zero. Floating-point division by zero doesn't get warned about either, because it can be a legit way to get infinities and NaNs ๐Ÿฆ‹
  • -Wsystem-headers โ€” ๐Ÿ›๏ธ Prints warning messages for constructs found in system header files. Warnings from system headers are normally hushed, on the assumption that they usually aren't real problems and would just make the compiler output harder to read. This option tells GCC to emit those warnings as if they came from user code.

Heads up though, bestie ๐Ÿ‘€ Using -Wall together with this option does not warn about unknown pragmas in system headers. For that, you also need -Wunknown-pragmas.

  • -Wtautological-compare โ€” ๐Ÿ” Warns if a self-comparison always evaluates to true or false. This catches mistakes like:
int i = 1;
...
if (i > i) { ... }

It also warns about bitwise comparisons that always evaluate to true or false. For instance, this one will always be false:

if ((a & 16) == 10) { ... }

This warning is enabled by -Wall.

  • -Wtrailing-whitespace, -Wtrailing-whitespace=kind โ€” ๐Ÿซง Warns about trailing whitespace at the end of lines, including inside comments, but not trailing whitespace in raw string literals. -Wtrailing-whitespace is the same as -Wtrailing-whitespace=blanks, which warns about trailing spaces and horizontal tabs. -Wtrailing-whitespace=any warns about those plus trailing form feed or vertical tab characters. -Wno-trailing-whitespace or -Wtrailing-whitespace=none turns it off, and off is the default. This is a coding style warning, so it's a vibe check thing ๐Ÿ’…
  • -Wleading-whitespace=kind โ€” ๐Ÿชถ Warns about style issues in leading whitespace, but not about how much indentation you have. Some projects indent with only spaces, some with only tabs, and some with zero or more tabs (for multiples of -ftabstop=n) followed by zero or fewer than n spaces. No warning is emitted on lines that contain only whitespace (though a -Wtrailing-whitespace= warning might still show up), and no warnings are emitted inside raw string literals. Warnings are also emitted for leading whitespace inside multi-line comments.
    • -Wleading-whitespace=spaces warns about leading whitespace other than spaces, for projects that want to indent with just spaces ๐ŸŒธ
    • -Wleading-whitespace=tabs warns about leading whitespace other than horizontal tabs, for projects that want to indent with just horizontal tabs ๐Ÿ“
    • -Wleading-whitespace=blanks warns about leading whitespace other than spaces and horizontal tabs. It also warns about a horizontal tab after a space in the leading whitespace, or about n or more consecutive spaces in leading whitespace, where n is the argument of -ftabstop=n (8 by default) ๐Ÿงš
    • -Wleading-whitespace=none turns the warning off, which is the default.

This is a coding style warning too ๐Ÿ’œ

  • -Wtrampolines โ€” ๐ŸŽช Warns about trampolines generated for pointers to nested functions. A trampoline is a small piece of data or code created at run time on the stack when the address of a nested function is taken, and it's used to call the nested function indirectly. For some targets, it's made up of data only, so it needs no special treatment. But for most targets, it's made up of code, which means the stack has to be made executable for the program to work properly ๐Ÿ˜ญ
  • -Wfloat-equal โ€” ๐Ÿงฎ Warns if floating-point values are used in equality comparisons.

The idea is that sometimes it's convenient for the programmer to treat floating-point values as approximations of infinitely precise real numbers. If you're doing that, you need to figure out (by analyzing the code, or some other way) the maximum or likely maximum error the computation introduces, and allow for it when doing comparisons (and when producing output, but that's a different problem). Instead of testing for equality, you should check whether the two values have ranges that overlap. That's done with the relational operators, so equality comparisons are probably a mistake โœจ

  • -Wtraditional (C and Objective-C only) โ€” ๐Ÿ•ฐ๏ธ Warns about certain constructs that behave differently in traditional C and ISO C. It also warns about ISO C constructs that have no traditional C equivalent, and about problematic constructs you should avoid. Here's the list of what gets flagged:
    • Macro parameters that appear within string literals in the macro body. In traditional C, macro replacement happens inside string literals, but in ISO C it doesn't ๐Ÿงต
    • In traditional C, some preprocessor directives didn't exist. Traditional preprocessors only considered a line to be a directive if the # appeared in column 1. So -Wtraditional warns about directives that traditional C understands but ignores because the # isn't the first character on the line. It also suggests hiding directives like #pragma, which traditional C doesn't understand, by indenting them. Some traditional implementations don't recognize #elif, so this option suggests avoiding it altogether ๐Ÿ™ˆ
    • A function-like macro that appears without arguments ๐Ÿซฅ
    • The unary plus operator โž•
    • The U integer constant suffix, or the F or L floating-point constant suffixes. (Traditional C does support the L suffix on integer constants.) These suffixes show up in macros defined in the system headers of most modern systems, e.g. the _MIN/_MAX macros in <limits.h>. Using these macros in user code might normally lead to spurious warnings, but GCC's integrated preprocessor has enough context to avoid warning in these cases ๐ŸŒˆ
    • A function declared external in one block and then used after the end of that block ๐Ÿ“ฆ
    • A switch statement with an operand of type long ๐ŸŽ€
    • A non-static function declaration that follows a static one. Some traditional C compilers don't accept this construct ๐Ÿซถ
  • The ISO type of an integer constant has a different width or signedness than its traditional type. This warning only fires for base-ten constants, so hexadecimal and octal values (which are usually bit patterns ๐Ÿงš) get a free pass ๐ŸŒธ
  • Usage of ISO string concatenation gets flagged ๐Ÿ‘€
  • Initialization of automatic aggregates gets flagged too ๐Ÿ’…
  • Identifiers that clash with labels. Traditional C has no separate namespace for labels, so this is a whole vibe-check ๐Ÿซถ
  • Initialization of unions. If the initializer is zero, the warning gets skipped ๐Ÿฆ‹ The logic is that zero initializers in user code are usually wrapped in something like "__STDC__" so missing-initializer warnings don't pop up, and traditional C relies on default initialization to zero anyway โœจ
  • Conversions by prototypes between fixed-point and floating-point values, and the other way around. Missing these prototypes when compiling with traditional C causes serious problems ๐Ÿ˜ญ This is only a subset of the possible conversion warnings. For the full set, use -Wtraditional-conversion.
  • Use of ISO C style function definitions. This one is intentionally silent for prototype declarations and variadic functions, because those ISO features sneak into your code when you use libiberty's traditional C compatibility macros, "PARAMS" and "VPARAMS" ๐ŸŒˆ It's also bypassed for nested functions, since that's already a GCC extension and not really relevant to traditional C compatibility ๐Ÿ’œ
  • -Wtraditional-conversion (C and Objective-C only) โ€” warns if a prototype causes a type conversion that's different from what would happen to the same argument without a prototype ๐ŸŽ€ This covers fixed-point to floating-point and back, plus conversions that change the width or signedness of a fixed-point argument, unless it's the same as the default promotion.
  • -Wdeclaration-after-statement (C and Objective-C only) โ€” warns when a declaration shows up after a statement in a block ๐Ÿ“ This one is borrowed from C++, came in with ISO C99, and is allowed by default in GCC. ISO C90 does not support it.

This warning gets upgraded to an error by -pedantic-errors ๐Ÿšจ

  • -Wshadow โ€” warns whenever a local variable or type declaration shadows another variable, parameter, type, class member (in C++), or instance variable (in Objective-C), or whenever a built-in function gets shadowed ๐Ÿ‘‘ In C++, the compiler warns if a local variable shadows an explicit typedef, but not if it shadows a struct, class, or enum. Turn this on and you also get every instance of local shadowing. That means -Wno-shadow=local and -Wno-shadow=compatible-local get ignored whenever -Wshadow is in use. This is the same as -Wshadow=global.
  • -Wno-shadow-ivar (Objective-C only) โ€” skips the warning when a local variable shadows an instance variable inside an Objective-C method ๐Ÿ™ˆ
  • -Wshadow=global โ€” warns for any shadowing. Same as -Wshadow ๐Ÿ’–
  • -Wshadow=local โ€” warns when a local variable shadows another local variable or parameter โœจ
  • -Wshadow=compatible-local โ€” warns when a local variable shadows another local variable or parameter whose type is compatible with the shadowing variable's type. In C++, "compatible" means the shadowing variable's type can be converted to the shadowed variable's type. This flag exists (on top of -Wshadow=local) because when a local variable shadows one of an incompatible type, it's most likely on purpose, not a bug or typo ๐Ÿง Here's a classic example:
for (SomeIterator i = SomeObj.begin(); i != SomeObj.end(); ++i)
{
  for (int i = 0; i < N; ++i)
  {
    ...
  }
  ...
}

Since the two i variables in that example have incompatible types, turning on only -Wshadow=compatible-local won't emit a warning ๐Ÿ’… And that's by design: if a programmer accidentally uses one in place of the other, type checking should catch it and emit an error or warning. Using this flag instead of -Wshadow=local can cut down on warnings triggered by intentional shadowing. Also note that shadowing const char *i with char *i does not emit a warning.

This warning is also enabled by -Wshadow=local ๐ŸŒธ

  • -Wlarger-than=byte-size โ€” warns whenever an object is defined whose size exceeds byte-size ๐Ÿฃ -Wlarger-than=PTRDIFF_MAX is on by default. You can disable these warnings either by specifying a byte-size of SIZE_MAX or more, or with -Wno-larger-than.

It also warns for calls to bounded functions like "memchr" or "strnlen" that specify a bound bigger than the largest possible object, which is PTRDIFF_MAX bytes by default. These warnings can only be disabled with -Wno-larger-than ๐Ÿ”ฅ

  • -Wno-larger-than โ€” disables the -Wlarger-than= warnings. It's equivalent to -Wlarger-than=SIZE_MAX or larger ๐Ÿ™Œ
  • -Wframe-larger-than=byte-size โ€” warns if the size of a function frame exceeds byte-size ๐Ÿฆ‹ The math used to figure out the stack frame size is approximate and not conservative, so the actual requirements might be somewhat bigger than byte-size even if you don't get a warning ๐Ÿ˜ฌ Also, any space allocated via "alloca", variable-length arrays, or related constructs is not counted when the compiler decides whether to warn.

-Wframe-larger-than=PTRDIFF_MAX is on by default. You can disable these warnings by specifying a byte-size of SIZE_MAX or more, or with -Wno-frame-larger-than.

  • -Wno-frame-larger-than โ€” disables the -Wframe-larger-than= warnings. It's equivalent to -Wframe-larger-than=SIZE_MAX or larger ๐Ÿ’œ
  • -Wfree-nonheap-object โ€” warns when you try to deallocate an object that either wasn't allocated on the heap, or was reached through a pointer that wasn't returned by a prior call to the matching allocation function ๐Ÿงน For example, stpcpy returns a pointer to the terminating nul character and not the beginning of the object, so the free call below gets diagnosed:
void f (char *p)
{
  p = stpcpy (p, "abc");
  // ...
  free (p);   // warning
}

-Wfree-nonheap-object is included in -Wall โœจ

  • -Wstack-usage=byte-size โ€” warns if a function's stack usage might exceed byte-size ๐Ÿ“š This calculation is conservative, and any space allocated via "alloca", variable-length arrays, or related constructs is counted when the compiler decides whether to warn. The message matches the output of -fstack-usage:
    • If the stack usage is fully static but goes over the limit, you'll see: warning: stack usage is 1120 bytes
    • If the stack usage is (partly) dynamic but bounded, you'll see: warning: stack usage might be 1648 bytes
    • If the stack usage is (partly) dynamic and unbounded, you'll see: warning: stack usage might be unbounded

-Wstack-usage=PTRDIFF_MAX is on by default. Disable these warnings by specifying a byte-size of SIZE_MAX or more, or with -Wno-stack-usage ๐ŸŽ€

  • -Wno-stack-usage โ€” disables the -Wstack-usage= warnings. It's equivalent to -Wstack-usage=SIZE_MAX or larger ๐ŸŒˆ
  • -Wunsafe-loop-optimizations โ€” warns if a loop can't be optimized because the compiler can't assume anything about the bounds of the loop indices ๐Ÿค” With -funsafe-loop-optimizations, it warns if the compiler does make those assumptions.
  • -Wno-pedantic-ms-format (MinGW targets only) โ€” when used with -Wformat and -pedantic without GNU extensions, this turns off the warnings about non-ISO "printf" and "scanf" width specifiers "I32", "I64", and "I" that get used on Windows targets, since those depend on the MS runtime ๐ŸชŸ
  • -Wpointer-arith โ€” warns about anything that depends on the "size of" a function type or of "void". GNU C gives these types a size of 1, for convenience when doing math with "void *" pointers and function pointers ๐Ÿงฎ In C++, it also warns when an arithmetic operation involves "NULL". This warning is also enabled by -Wpedantic.

This warning gets upgraded to an error by -pedantic-errors ๐Ÿšจ

  • -Wno-pointer-compare โ€” doesn't warn when a pointer gets compared with a zero character constant. That usually means the pointer was meant to be dereferenced ๐Ÿ‘€ For example:
const char *p = foo ();
if (p == '\0')
  return 42;

The code above is invalid in C++11 โŒ

This warning is enabled by default ๐Ÿ’–

  • -Wno-tsan โ€” disables warnings about unsupported features in ThreadSanitizer ๐Ÿงต ThreadSanitizer doesn't support "std::atomic_thread_fence" and can report false positives, so this helps you tune out that noise ๐Ÿซ 
  • -Wtype-limits โ€” warns if a comparison is always true or always false because of the limited range of the data type, but doesn't warn for constant expressions ๐Ÿ“ For example, it warns if an unsigned variable is compared against zero with "<" or ">=". This warning is also enabled by -Wextra.
  • -Wabsolute-value (C and Objective-C only) โ€” warns about calls to standard functions that compute the absolute value of an argument when a more appropriate standard function exists ๐Ÿ™ƒ For example, calling abs(3.14) triggers the warning, because the right function for a double is "fabs". It also triggers when the argument in such a call has an unsigned type. You can suppress it with an explicit type cast, and it's also enabled by -Wextra ๐Ÿ’
  • -Wcomment, -Wcomments โ€” warns whenever a comment-start sequence /* shows up inside a /* comment, or whenever a backslash-newline shows up in a // comment ๐Ÿ’ฌ This warning is enabled by -Wall.
  • -Wtrigraphs โ€” warns if any trigraphs are encountered that might change the meaning of the program ๐Ÿง Trigraphs inside comments aren't warned about, except for the ones that would form escaped newlines.

This one's implied by -Wall ๐Ÿ’… If -Wall isn't on, it's still enabled unless trigraphs are turned on. Want trigraph conversion without the warnings but still all the other -Wall goodies? Use -trigraphs -Wall -Wno-trigraphs ๐Ÿซถ

  • -Wundef โ€” okay so this one warns if an undefined identifier gets evaluated in an #if directive ๐Ÿ™ˆ Those identifiers get replaced with zero, so you're not getting a surprise error, just a heads-up โœจ
  • -Wexpansion-to-defined โ€” this warns whenever defined shows up in the expansion of a macro, including when the macro gets expanded by an #if directive ๐Ÿ’€ That usage is not portable, so you'll want to know about it. It's also turned on by -Wpedantic and -Wextra ๐Ÿ”ฅ
  • -Wunused-macros โ€” this one flags macros defined in the main file that nobody's using ๐Ÿ‘€ A macro counts as used if it gets expanded or tested for existence at least once. The preprocessor will also side-eye you if the macro hasn't been used by the time it's redefined or undefined ๐Ÿ’…

Built-in macros, macros defined on the command line, and macros defined in include files are not warned about, so don't stress those ๐Ÿ’–

Heads up: if a macro is used, but only inside skipped conditional blocks, the preprocessor still reports it as unused ๐Ÿ˜ญ To dodge the warning, you could improve the scope of the macro's definition, like moving it into the first skipped block. Or you could give it a dummy use with something like:
#if defined the_macro_causing_the_warning
#endif
  • -Wno-endif-labels โ€” this stops the warning when an #else or an #endif has text after it ๐ŸŽ€ That happens sometimes in older programs with code like this:
#if FOO
...
#else FOO
...
#endif FOO

The second and third FOO should really be in comments, bestie. This warning is on by default โœจ

  • -Wbad-function-cast (C and Objective-C only) โ€” warns when a function call gets cast to a non-matching type ๐ŸŒธ For example, it'll warn if a call to a function returning an integer type gets cast to a pointer type.
  • -Wc90-c99-compat (C and Objective-C only) โ€” this one warns about features that exist in ISO C99 but not in ISO C90 ๐Ÿฆ‹ Think variable length arrays, the long long type, the bool type, compound literals, designated initializers, and so on. It doesn't care about your standards mode, and warnings are disabled in the expression that follows __extension__ ๐Ÿ™ˆ
  • -Wc99-c11-compat (C and Objective-C only) โ€” same energy, but for features in ISO C11 that aren't in ISO C99 ๐Ÿงš Like anonymous structures and unions, the _Atomic type qualifier, the _Thread_local storage-class specifier, the _Alignas specifier, the Alignof operator, the _Generic keyword, and so on. Independent of the standards mode, and warnings are disabled in the expression after __extension__ ๐Ÿ’œ
  • -Wc11-c23-compat, -Wc11-c2x-compat (C and Objective-C only) โ€” warns about features in ISO C23 that aren't in ISO C11 ๐ŸŒˆ Think omitting the string in _Static_assert, using [[]] attribute syntax, decimal floating-point types, and so on. It's independent of the standards mode, and warnings are disabled in the expression after __extension__. Also, the name -Wc11-c2x-compat is deprecated, so use the newer one ๐Ÿซ 

When you're not compiling in C23 mode, these warnings get upgraded to errors by -pedantic-errors ๐Ÿ˜ค

  • -Wc23-c2y-compat (C and Objective-C only) โ€” warns about features in ISO C2Y that aren't in ISO C23 โœจ For instance, _Generic selecting with a type name instead of an expression. Also independent of the standards mode, with warnings disabled in the expression that follows __extension__.
  • -Wc23-c2y-compat (C and Objective-C only) โ€” yep, this one's listed twice in the original page, so here it is again ๐Ÿ™ˆ Same vibe: features in ISO C2Y, not in ISO C23, with the same __extension__ rule.

When you're not compiling in C2Y mode, these warnings get upgraded to errors by -pedantic-errors ๐Ÿ’…

  • -Wc++-compat (C and Objective-C only) โ€” warns about ISO C constructs that sit outside the common subset of ISO C and ISO C++ ๐Ÿ“ Like implicitly converting from void * to a pointer to non-void type.
  • -Wc++11-compat (C++ and Objective-C++ only) โ€” warns about C++ constructs whose meaning changes between ISO C++ 1998 and ISO C++ 2011 ๐Ÿ‘‘ For example, identifiers in ISO C++ 1998 that are keywords in ISO C++ 2011. This one turns on -Wnarrowing and is enabled by -Wall.
  • -Wc++14-compat (C++ and Objective-C++ only) โ€” warns about C++ constructs whose meaning differs between ISO C++ 2011 and ISO C++ 2014 ๐Ÿ’– Enabled by -Wall.
  • -Wc++17-compat (C++ and Objective-C++ only) โ€” same idea for ISO C++ 2014 vs ISO C++ 2017 โญ Enabled by -Wall.
  • -Wc++20-compat (C++ and Objective-C++ only) โ€” and this one covers ISO C++ 2017 vs ISO C++ 2020 ๐Ÿฆ‹ Enabled by -Wall.
  • -Wno-c++11-extensions (C++ and Objective-C++ only) โ€” don't warn about C++11 constructs when you're compiling with an older C++ standard ๐ŸŒธ Even without this option, some C++11 constructs only get flagged if -Wpedantic is used.
  • -Wno-c++14-extensions (C++ and Objective-C++ only) โ€” same deal, but for C++14 constructs ๐Ÿงš Some of them only get diagnosed with -Wpedantic.
  • -Wno-c++17-extensions (C++ and Objective-C++ only) โ€” you guessed it, C++17 constructs in code compiled with an older standard ๐Ÿ’œ Some still only show up under -Wpedantic.
  • -Wno-c++20-extensions (C++ and Objective-C++ only) โ€” C++20 constructs, same rules ๐ŸŒˆ Some only get diagnosed when -Wpedantic is used.
  • -Wno-c++23-extensions (C++ and Objective-C++ only) โ€” C++23 constructs in code compiled with an older C++ standard โœจ Some only get flagged under -Wpedantic.
  • -Wno-c++26-extensions (C++ and Objective-C++ only) โ€” C++26 constructs, same story ๐Ÿซถ Some still need -Wpedantic to get diagnosed.
  • -Wcast-qual โ€” warns whenever a pointer gets cast in a way that removes a type qualifier from the target type ๐Ÿ˜ญ For example, it'll warn if a const char * is cast to an ordinary char *.

It also warns when you make a cast that adds a type qualifier in an unsafe way ๐Ÿ’€ Casting char ** to const char ** is unsafe, as in this example:

/* p is char ** value.  */
const char **q = (const char **) p;
/* Assignment of readonly string to const char * is OK.  */
*q = "string";
/* Now char** pointer points to read-only memory.  */
**p = 'b';
  • -Wcast-align โ€” warns whenever a pointer is cast so that the required alignment of the target goes up ๐ŸŽ€ For example, it'll warn if a char * is cast to an int * on machines where integers can only be accessed at two- or four-byte boundaries.
  • -Wcast-align=strict โ€” same vibe, but stricter ๐Ÿ˜ค It warns whenever a pointer is cast so the required alignment of the target increases, and it does this for a char * cast to an int * regardless of the target machine.
  • -Wcast-function-type โ€” warns when a function pointer is cast to an incompatible function pointer ๐Ÿ”ฅ For casts involving function types with a variable argument list, only the types of the initial arguments that are provided get considered. Any parameter of pointer type matches any other pointer type. Benign differences in integral types get ignored, like int vs. long on ILP32 targets, and type qualifiers are ignored too. The function type void (*) (void) is special and matches everything, so it can be used to suppress this warning ๐Ÿ™ˆ For casts involving pointer to member types, this warning fires whenever the cast changes the pointer to member type. Enabled by -Wextra.
  • -Wcast-user-defined โ€” warns when a cast to a reference type doesn't involve a user-defined conversion that the programmer might expect to get called ๐Ÿ‘‘ Here's the example:
struct A { operator const int&(); } a;
auto r = (int&)a; // warning

This warning is enabled by default โœจ

  • -Wwrite-strings โ€” when compiling C, this gives string constants the type const char[length], so copying the address of one into a non-const char * pointer produces a warning ๐Ÿ’– These warnings help you catch, at compile time, code that might try to write into a string constant, but only if you've been super careful about using const in declarations and prototypes. Otherwise, honestly, it's just a nuisance. That's why -Wall doesn't request these warnings ๐Ÿ™„

When compiling C++, it warns about the deprecated conversion from string literals to char *. This warning is enabled by default for C++ programs ๐Ÿฆ‹

In C++11 mode or later, this warning is upgraded to an error by -pedantic-errors ๐Ÿ˜ญ
  • -Wclobbered โ€” warns for variables that might be changed by longjmp or vfork ๐ŸŒธ This warning is also enabled by -Wextra.
  • -Wno-complain-wrong-lang โ€” by default, language front ends complain when a command-line option is valid but not applicable to that front end ๐Ÿงš You can turn that off with -Wno-complain-wrong-lang, which is mostly useful when you invoke a single compiler driver for multiple source files written in different languages. For example:
$ g++ -fno-rtti a.cc b.f90

The driver g++ invokes the C++ front end to compile a.cc and the Fortran front end to compile b.f90. The latter front end diagnoses f951: Warning: command-line option '-fno-rtti' is valid for C++/D/ObjC++ but not for Fortran, which can be disabled with -Wno-complain-wrong-lang ๐Ÿ’œ

  • -Wcompare-distinct-pointer-types (C and Objective-C only) โ€” warns if pointers of distinct types get compared without a cast ๐ŸŒˆ This warning is enabled by default โœจ
  • -Wconversion โ€” okay so this one is all about sneaky implicit conversions that might quietly change a value ๐Ÿ‘€ It catches real-to-integer stuff like abs (x) when x is double, signed-to-unsigned stuff like unsigned ui = -1, and squishing things into smaller types like sqrtf (M_PI). It does not warn for explicit casts like abs ((int) x) and ui = (unsigned) -1, or when the value doesn't actually change, like abs (2.0). If the signed/unsigned warnings are too much, you can mute just those with -Wno-sign-conversion ๐Ÿซถ

For C++, it also flags confusing overload resolution for user-defined conversions, plus conversions that never use a type conversion operator (conversions to void, the same type, a base class, or a reference to them) โœจ Heads up though: the signed/unsigned integer warnings are off by default in C++ unless you explicitly turn on -Wsign-conversion ๐Ÿ’…

Warnings about conversion from arithmetic on a small type back to that type only show up with -Warith-conversion ๐ŸŒธ

  • -Wdangling-else โ€” this one warns when it's unclear which if an else branch actually belongs to ๐Ÿ™ˆ Check out this chaos:
{
  if (a)
    if (b)
      foo ();
  else
    bar ();
}

In C/C++, every else belongs to the innermost possible if, which here is if (b). That's usually not what the programmer meant, even though the indentation says otherwise. When that confusion is possible, GCC warns if you use this flag ๐Ÿšจ To kill the warning, slap explicit braces around the innermost if so the else can't possibly belong to the outer one. It ends up looking like this:

{
  if (a)
    {
      if (b)
        foo ();
      else
        bar ();
    }
}

This warning is enabled by -Wparentheses ๐Ÿ’–

  • -Wdangling-pointer, -Wdangling-pointer=n โ€” okay this is a big one ๐Ÿฆ‹ It warns about using pointers (or C++ references) to objects with automatic storage duration after their lifetime has ended. That covers local variables declared in nested blocks, compound literals, and other unnamed temporary objects. It also warns about storing the address of those objects in escaped pointers. It's enabled at every optimization level, but the results can look different with optimization than without, so keep that in mind ๐Ÿ‘€
    • -Wdangling-pointer=1 โ€” at level 1, it only diagnoses unconditional uses of dangling pointers ๐ŸŒท
    • -Wdangling-pointer=2 โ€” at level 2, it also diagnoses conditional uses of dangling pointers ๐ŸŒˆ

The short form -Wdangling-pointer is the same as -Wdangling-pointer=2. Meanwhile, -Wno-dangling-pointer and -Wdangling-pointer=0 both turn the warnings off ๐Ÿ’€ And -Wdangling-pointer=2 is already included in -Wall โœจ

Here's a level 1 example. The address of the unnamed temporary gets unconditionally used outside of its scope, so it gets flagged:

char f (char c1, char c2, char c3)
{
  char *p;
  {
    p = (char[]) { c1, c2, c3 };
  }
  // warning: using dangling pointer 'p' to an unnamed temporary
  return *p;
}

In this next one, storing the address of the local variable x into the escaped pointer *p triggers the level 1 warning ๐Ÿงš

void g (int **p)
{
  int x = 7;
  // warning: storing the address of local variable 'x' in '*p'
  *p = &x;
}

And here the array a is out of scope by the time the pointer s gets used. Since the code that sets s is conditional, this one triggers at level 2 ๐Ÿซ 

extern void frob (const char *);
void h (char *s)
{
  if (!s)
    {
      char a[12] = "tmpname";
      s = a;
    }
  // warning: dangling pointer 's' to 'a' may be used
  frob (s);
}
  • -Wdate-time โ€” this warns whenever the macros __TIME__, __DATE__ or __TIMESTAMP__ show up ๐Ÿ•ฐ๏ธ The reason? They might stop your compilations from being bit-wise-identical and reproducible, which is a whole vibe-killer for builds ๐Ÿ˜ญ
  • -Wempty-body โ€” warns if an empty body shows up in an if, else or do while statement ๐Ÿ“ This one is also enabled by -Wextra ๐Ÿ’œ
  • -Wno-endif-labels โ€” stops GCC from complaining about stray tokens after #else and #endif ๐ŸŽ€
  • -Wenum-compare โ€” warns about comparing values of different enumerated types ๐Ÿ‘‘ In C++, enumerated type mismatches in conditional expressions get diagnosed too, and that one is enabled by default. In C, this warning comes along with -Wall โœจ
  • -Wenum-conversion โ€” warns when a value of one enumerated type gets implicitly converted to a different enumerated type ๐Ÿ”ฅ In C, it's enabled by -Wextra ๐Ÿ’…
  • -Wenum-int-mismatch (C and Objective-C only) โ€” this one warns about mismatches between an enumerated type and an integer type in declarations ๐Ÿง For example:
enum E { l = -1, z = 0, g = 1 };
int foo(void);
enum E foo(void);

In C, an enumerated type is compatible with char, a signed integer type, or an unsigned integer type. But since the choice of underlying type for an enumerated type is implementation-defined, these mismatches can cause portability issues ๐ŸŒ In C++, mismatches like this are an error. In C, this warning is enabled by -Wall and -Wc++-compat ๐Ÿฆ‹

  • -Wjump-misses-init (C, Objective-C only) โ€” warns if a goto or a switch statement jumps forward across the initialization of a variable, or jumps backward to a label after that variable has already been initialized ๐Ÿ™ƒ It only cares about variables that are initialized when they're declared. This is only supported for C and Objective-C, because in C++ this sort of branch is an error anyway ๐Ÿšซ
In C++, jumping like this is always an error, so there's no need to worry about this warning there.

-Wjump-misses-init is included in -Wc++-compat, and you can disable it with the -Wno-jump-misses-init option ๐Ÿซถ

  • -Wsign-compare โ€” warns when a comparison between signed and unsigned values could give a wrong result because the signed value gets converted to unsigned โš ๏ธ In C++, this is also enabled by -Wall. In C, it's enabled by -Wextra too ๐Ÿ’–
  • -Wsign-conversion โ€” warns about implicit conversions that might change the sign of an integer value, like assigning a signed integer expression to an unsigned integer variable ๐ŸŒธ An explicit cast silences the warning โœจ In C, this option is also enabled by -Wconversion ๐ŸŽ€
  • -Wflex-array-member-not-at-end (C and C++ only) โ€” warns when a structure that has a C99 flexible array member as its last field ends up not at the end of another structure ๐Ÿงฉ This warning goes off for code like this:
struct flex  { int length; char data[]; };
struct mid_flex { int m; struct flex flex_data; int n; };
  • -Wfloat-conversion โ€” warns about implicit conversions that reduce the precision of a real value ๐ŸŒŠ That includes real-to-integer conversions, and conversions from higher precision real values to lower precision ones. This option is also enabled by -Wconversion ๐Ÿฆ„
  • -Wno-scalar-storage-order โ€” stops GCC from warning about suspicious constructs involving reverse scalar storage order ๐Ÿ™ˆ
  • -Wsizeof-array-div โ€” warns about dividing two sizeof operators when the first one is applied to an array and the divisor doesn't match the size of the array element ๐Ÿงฎ In that case, the math won't give you the number of elements in the array, which is probably not what you wanted. This warning goes off for code like this:
int fn ()
{
  int arr[10];
  return sizeof (arr) / sizeof (short);
}

This warning is enabled by -Wall ๐Ÿ’…

  • -Wsizeof-pointer-div โ€” warns about suspicious divisions of two sizeof expressions that divide the pointer size by the element size. That's the usual trick for computing an array's size, but it won't work with pointers ๐Ÿ’€ The warning goes off for sizeof (ptr) / sizeof (ptr[0]) if ptr is a pointer and not an array. It's enabled by -Wall โœจ
  • -Wsizeof-pointer-memaccess โ€” warns about suspicious length parameters passed to certain string and memory built-in functions when the argument uses sizeof ๐Ÿง  For example, it triggers on memset (ptr, 0, sizeof (ptr)); if ptr is a pointer and not an array, and it suggests a possible fix. It also triggers on things like memcpy (&foo, ptr, sizeof (&foo)); ๐ŸŽ€

This option also warns about calls to bounded string copy functions like strncat or strncpy that use the sizeof of the source array as the bound. In the following function, the strncat call uses the size of the source string as its bound. That's almost certainly a mistake, so the call gets diagnosed ๐Ÿ™…โ€โ™€๏ธ

void make_file (const char *name)
{
  char path[PATH_MAX];
  strncpy (path, name, sizeof path - 1);
  strncat (path, ".text", sizeof ".text");
  ...
}

-Wsizeof-pointer-memaccess is enabled by -Wall ๐Ÿ‘‘

  • -Wno-sizeof-array-argument โ€” stops GCC from warning when the sizeof operator is applied to a parameter that's declared as an array in a function definition ๐ŸŒˆ This warning is enabled by default for C and C++ programs, so this flag is how you turn it off ๐Ÿซ 
  • -Wmemset-elt-size โ€” warns about suspicious calls to the memset built-in function. It triggers when the first argument references an array, and the third argument is a number equal to the number of elements but not equal to the size of the array in memory. That usually means you forgot to multiply by the element size ๐Ÿ“ This warning is enabled by -Wall ๐Ÿซถ

โœจ OPTIONS โœจ (continued)

  • -Wmemset-transposed-args โ€” okay so this one yells at you ๐Ÿ™ˆ when you call the memset built-in with a non-zero second argument and a literal zero as the third. memset (buf, sizeof buf, 0) gets flagged because you probably meant memset (buf, 0, sizeof buf) ๐Ÿ’…. It only fires when the third argument is a literal zero. If it's an expression that folds to zero, or a cast of zero to some type, the args are way less likely to be mixed up, so it stays quiet. This one is turned on by -Wall ๐Ÿซถ.
  • -Waddress โ€” this is the suspicious-address-expression check and it is giving detective ๐Ÿ•ต๏ธโ€โ™€๏ธ. It catches comparing the address of a function or declared object against the null pointer constant, like this:
void f (void);
void g (void)
{
  if (!f)   // warning: expression evaluates to false
    abort ();
}

It also side-eyes comparing a pointer to a string literal:

void f (const char *x)
{
  if (x == "abc")   // warning: expression evaluates to false
    puts ("equal");
}

And it flags testing the result of pointer addition or subtraction for equality to null:

void f (const int *p, int i)
{
  return p + i == NULL;
}

Real talk, these usually mean a programmer error ๐Ÿ˜ญ. The address of most functions and objects always evaluates to true (the exceptions are weak symbols), so putting them in a conditional might mean you're missing parentheses in a function call or a dereference in an array expression. The object-pointer part of the warning can be shushed by casting the pointer operand to an integer type like intptr_t or uintptr_t. Comparisons against string literals give unspecified behavior and aren't portable, and they hint that you meant to call strcmp ๐Ÿ™„. The warning gets suppressed if the sus expression came out of a macro expansion. The -Waddress warning is enabled by -Wall โœจ.

  • -Wno-address-of-packed-member โ€” the "don't warn me" option ๐Ÿฆ‹. Skip the warning when the address of a packed member of a struct or union is taken, which usually ends up as an unaligned pointer value. This is enabled by default ๐Ÿ’œ.
  • -Wlogical-op โ€” warns about sus logical operators in expressions ๐Ÿ‘€. That includes using a logical operator where a bit-wise operator is probably what you wanted. It also flags when both operands of a logical operator are the same, like this:
extern int a;
if (a < 0 && a < 0) { ... }
  • -Wlogical-not-parentheses โ€” this one is looking for a logical not on the left hand side of a comparison ๐Ÿง. It doesn't warn if the right operand is considered a boolean expression. The whole point is catching code like this:
int a;
...
if (!a > 1) { ... }

You can shut the warning up by wrapping the LHS in parentheses:

if ((!a) > 1) { ... }

This warning is enabled by -Wall ๐ŸŒธ.

  • -Waggregate-return โ€” warns if any function that returns a structure or union gets defined or called ๐Ÿ“ฆ. In languages where you can return an array, this also gives you a warning, so keep an eye out.
  • -Wno-aggressive-loop-optimizations โ€” the "chill, don't warn me" option ๐Ÿ˜Œ. Skip the warning when the compiler detects undefined behavior in a loop with a constant number of iterations. -Waggressive-loop-optimizations is enabled by default ๐Ÿ“.
  • -Wno-attributes โ€” silences warnings when you use an unexpected __attribute__, like unrecognized attributes or function attributes slapped on variables ๐ŸŽ€. It doesn't stop errors for using supported attributes wrong, though, so don't get too comfy.

Heads up: ill-formed uses of standard attributes get upgraded to errors by -pedantic-errors.

Also, with -Wno-attributes=, you can suppress warnings about unknown scoped attributes (in C++11 and C23) ๐Ÿงš. For example, -Wno-attributes=vendor::attr disables the warning about this declaration:

[[vendor::attr]] void f();

You can also hush warnings about all attributes in a namespace with -Wno-attributes=vendor::, which stops the warning about both of these:

[[vendor::safe]] void f();
[[vendor::unsafe]] void f2();

Note that -Wno-attributes= does not imply -Wno-attributes โš ๏ธ.

  • -Wno-builtin-declaration-mismatch โ€” the "I know what I'm doing" option ๐Ÿ’…. This warns when a built-in function is declared with an incompatible signature or as a non-function, or when a built-in declared with a type that doesn't include a prototype gets called with args whose promoted types don't match what the function expects. With -Wextra, it also warns when a built-in that takes arguments is declared without a prototype. The -Wbuiltin-declaration-mismatch warning is enabled by default. To avoid it, include the right header so the built-in prototypes are in scope ๐Ÿซถ.

Like here, the call to memset gets diagnosed because the function expects a size_t as its argument but 32 is an int. With -Wextra, the declaration gets called out too ๐Ÿ‘€:

extern void* memset ();
void f (void *d)
{
  memset (d, '\0', 32);
}
  • -Wno-builtin-macro-redefined โ€” no more warnings when certain built-in macros get redefined ๐ŸŒˆ. This suppresses warnings for redefining __TIMESTAMP__, __TIME__, __DATE__, __FILE__, and __BASE_FILE__ โฐ.
  • -Wfree-labels (C and Objective-C only) โ€” warns if a label is slapped on a non-statement, or shows up at the end of a compound statement ๐Ÿท๏ธ. C23 and later dialects of C allow these, and they're a GCC extension in all other dialects.

This warning is also turned on by -Wc11-c23-compat. It becomes an error if you're building for a C version before C23 with -pedantic-errors ๐Ÿšซ.

  • -Wheader-guard โ€” this one is a total detective for header guards ๐Ÿ”. It warns when a valid preprocessor header multiple-inclusion guard has a #define right after the #ifndef or #if !defined directive, where that #define defines a different macro from the guard with a similar name. The real guard macro isn't defined at the matching #ifndef at the end of the header, and the #define makes an object-like macro with an empty definition. Usually that means it's just a misspelled guard name, either in the #ifndef or #if !defined directive or in the #define after it ๐Ÿ“. This warning is enabled by -Wall โœจ.
  • -Wstrict-prototypes (C and Objective-C only) โ€” warns if a function is declared or defined without specifying its argument types ๐Ÿ“œ. An old-style function definition is allowed without a warning if a declaration that specifies the argument types came before it.
  • -Wold-style-declaration (C and Objective-C only) โ€” warns about obsolescent declaration usages, according to the C Standard ๐Ÿฆ•. For example, it warns if storage-class specifiers like static aren't the first things in a declaration. This warning is also enabled by -Wextra ๐Ÿ’–.
  • -Wold-style-definition (C and Objective-C only) โ€” warns if an old-style function definition is used ๐Ÿ‘ต. You get the warning even if there's a previous prototype. In C23 mode, a definition using () is not considered old-style, because it's equivalent to (void) in that case. For older standards, though, it counts as an old-style definition.
  • -Wdeprecated-non-prototype (C and Objective-C only) โ€” warns if a function declared with an empty parameter list () gets called with one or more arguments, or if a function definition with one or more parameters shows up after such a declaration ๐Ÿšจ. Both cases are errors in C23 and later dialects of C.

This warning is also turned on by -Wc11-c23-compat ๐Ÿ’….

  • -Wmissing-parameter-name (C and Objective-C only) โ€” warns if a function definition leaves out a parameter name and only gives its type ๐Ÿท๏ธ. This is handy for documenting that a parameter is unused in the definition. It's part of C23 and later dialects of C, and available as a GCC extension in all other dialects.

This warning is also turned on by -Wc11-c23-compat. It becomes an error if you're building for a C version before C23 with -pedantic-errors ๐Ÿšซ.

  • -Wmissing-parameter-type (C and Objective-C only) โ€” warns when a function parameter gets declared without a type specifier in K&R-style functions ๐Ÿ•ฐ๏ธ:
void foo(bar) { }

This warning is also enabled by -Wextra ๐ŸŒธ.

  • -Wno-declaration-missing-parameter-type (C and Objective-C only) โ€” stops the warning when a function declaration has a parameter name with no type ๐Ÿ™…โ€โ™€๏ธ. Those declarations don't give you a function prototype, and they block most type checking in function calls.

This warning is enabled by default. In C99 and later dialects of C, it's treated as an error ๐Ÿ˜ฑ. You can downgrade that error to a warning with -fpermissive (along with certain other errors), or for this error alone, with -Wno-error=declaration-missing-parameter-type.

This warning is upgraded to an error by -pedantic-errors ๐Ÿšซ.

  • -Wmissing-prototypes (C and Objective-C only) โ€” warns if a global function gets defined without a previous prototype declaration ๐Ÿ”Ž. You get the warning even if the definition itself provides a prototype. Use this to catch global functions that don't have a matching prototype declaration in a header file ๐Ÿ“‚. This option isn't valid for C++, because all function declarations there provide prototypes, and a non-matching declaration declares an overload rather than conflicting with an earlier one. Use -Wmissing-declarations to find missing declarations in C++ ๐Ÿ’œ.
  • -Wmissing-variable-declarations (C and Objective-C only) โ€” warns if a global variable gets defined without a previous declaration ๐Ÿงพ. Use this to catch global variables that don't have a matching extern declaration in a header file โœจ.

Okay so we're still deep in the OPTIONS drama ๐Ÿ’… and this batch is packed with warnings, bestie ๐Ÿซถ

  • -Wmissing-declarations โ€” okay, this one's a vibe check on global functions ๐Ÿ”. It warns if a global function gets defined without a previous declaration, even if the definition already comes with a prototype. Use it to catch global functions that never got declared in header files. In C, nothing gets flagged for functions with earlier non-prototype declarations, so reach for -Wmissing-prototypes if you want to catch missing prototypes. In C++, it skips function templates, inline functions, and functions in anonymous namespaces โœจ
  • -Wmissing-field-initializers โ€” this one is SO relatable ๐Ÿ˜ญ. It warns when a struct's initializer leaves some fields out. Like, here x.h just quietly becomes zero:
struct s { int f, g, h; };
struct s x = { 3, 4 };

In C, this option doesn't yell about designated initializers, so this one's totally fine ๐Ÿฆ‹:

struct s { int f, g, h; };
struct s x = { .f = 3, .g = 4 };

In C it also doesn't warn about the universal zero initializer { 0 } ๐ŸŒธ:

struct s { int f, g, h; };
struct s x = { 0 };

Same deal in C++ with the empty { } initializer, no warning ๐Ÿ’–:

struct s { int f, g, h; };
s x = { };

This one is part of -Wextra. If you want the other -Wextra warnings without this one, go with -Wextra -Wno-missing-field-initializers ๐ŸŽ€

  • -Wno-missing-requires โ€” the compiler warns about a concept-id showing up as a C++20 simple-requirement by default ๐Ÿงš. Check this out:
bool satisfied = requires { C<T> };

Here satisfied will be true if C<T> is a valid expression, which it is for all T. The person probably meant this instead:

bool satisfied = requires { requires C<T> };

That way satisfied is only true if concept C is satisfied for type T. You can switch this warning off with -Wno-missing-requires ๐Ÿ™ˆ

  • -Wno-missing-template-keyword โ€” the member access tokens ., -> and :: need the "template" keyword ๐Ÿ‘‘ if the parent object is dependent and the member you're naming is a template. Look at the good and the not-so-good:
template <class X>
void DoStuff (X x)
{
  x.template DoSomeOtherStuff<X>(); // Good.
  x.DoMoreStuff<X>(); // Warning, x is dependent.
}

Every once in a while you can get false positives. To silence them, wrap the expression in parentheses. Like, this one gets treated as a template even when m and N are integers ๐Ÿฅด:

void NotATemplate (my_class t)
{
  int N = 5;

  bool test = t.m < N > (0); // Treated as a template.
  test = (t.m < N) > (0); // Same meaning, but not treated as a template.
}

You can disable this warning with -Wno-missing-template-keyword ๐Ÿ’œ

  • -Wno-multichar โ€” don't warn about multicharacter constants like 'FOOF' ๐Ÿ“. Usually these are a typo in your code, since they have implementation-defined values, so they really shouldn't show up in portable code.
  • -Wnormalized=[none|id|nfc|nfkc] โ€” this is the identifier normalization era ๐ŸŒˆ. In ISO C and ISO C++, two identifiers are different if they're different character sequences. BUT with characters outside basic ASCII you can get two different sequences that look exactly the same ๐Ÿ˜ต. ISO 10646 has normalization rules that turn lookalikes into one shared sequence, and GCC can warn you when your identifiers haven't been normalized. This option controls that warning.

There are four warning levels. The default is -Wnormalized=nfc, which warns about any identifier that isn't in the ISO 10646 "C" normalized form, NFC. NFC is the one recommended for most uses, and it's the same as -Wnormalized.

Some characters that ISO C and ISO C++ let you use in identifiers can't be used once they're turned into NFC. So there's no way to use those symbols in portable ISO C or C++ and keep all your identifiers in NFC ๐Ÿซ . -Wnormalized=id suppresses the warning for those characters. The hope is that future versions of the standards fix this, which is why it isn't the default.

You can switch the warning off for every character with -Wnormalized=none or -Wno-normalized. Only do this if you're using some other normalization scheme (like "D"), because otherwise you can easily create bugs that are literally impossible to see ๐Ÿ’€

Some ISO 10646 characters have distinct meanings but look identical in some fonts or display methods, especially once formatting is applied. For example, "\u207F", "SUPERSCRIPT LATIN SMALL LETTER N", looks just like a regular "n" that's been placed in a superscript ๐Ÿ‘€. ISO 10646 also defines the NFKC normalization scheme to turn all of these into a standard form, and GCC warns you if your code isn't NFKC when you use -Wnormalized=nfkc. This is kind of like warning about every identifier with the letter O, since it might get mixed up with the digit 0. That's why it isn't the default, but it can be a handy local coding convention if your environment can't be fixed to display these characters differently โœจ

  • -Wno-attribute-warning โ€” don't warn about using functions declared with the "warning" attribute ๐Ÿฆ‹. This warning is on by default. You can turn the warning off with -Wno-attribute-warning, or use -Wno-error=attribute-warning to turn off the error you'd get when compiling with -Werror ๐ŸŽ€
  • -Wno-deprecated โ€” don't warn about deprecated features ๐ŸŒธ. Heads up though: in C++, explicitly specifying -Wdeprecated also turns on warnings about features that are deprecated in later language standards. Those are -Wcomma-subscript, -Wvolatile, -Wdeprecated-enum-float-conversion, -Wdeprecated-enum-enum-conversion, -Wdeprecated-literal-operator, and -Wdeprecated-variadic-comma-omission ๐Ÿฅน
  • -Wno-deprecated-declarations โ€” don't warn about uses of functions, variables, and types marked as deprecated with the "deprecated" attribute ๐Ÿ™ƒ
  • -Wno-overflow โ€” don't warn about compile-time overflow in constant expressions ๐Ÿ”ข
  • -Wno-odr โ€” warn about One Definition Rule violations during link-time optimization โš–๏ธ. It's enabled by default, so you'd have to actively turn it off to lose it.
  • -Wopenacc-parallelism โ€” warn about potentially suboptimal choices related to OpenACC parallelism ๐Ÿš€
  • -Wno-openmp โ€” warn about suspicious OpenMP code ๐Ÿง
  • -Wopenmp-simd โ€” warn if the vectorizer cost model overrides the OpenMP simd directive that the user set ๐Ÿซฃ. If you want to relax the cost model, you can use -fsimd-cost-model=unlimited ๐Ÿ’œ
  • -Woverride-init (C and Objective-C only) โ€” warn if an initialized field without side effects gets overridden when you're using designated initializers ๐Ÿ‘. This one is part of -Wextra. To get the other -Wextra warnings without it, use -Wextra -Wno-override-init ๐Ÿ’…
  • -Wno-override-init-side-effects (C and Objective-C only) โ€” don't warn if an initialized field with side effects gets overridden when using designated initializers. This is enabled by default ๐ŸŒˆ
  • -Wpacked โ€” warn if a structure gets the packed attribute but that attribute has no effect on the layout or size of the structure ๐Ÿ˜ค. Structures like that can end up misaligned for basically zero benefit. In this code, the variable f.x inside struct bar is misaligned even though struct bar doesn't have the packed attribute itself ๐Ÿ™ˆ:
struct foo {
  int x;
  char a, b, c, d;
} __attribute__((packed));
struct bar {
  char z;
  struct foo f;
};
  • -Wnopacked-bitfield-compat โ€” the 4.1, 4.2 and 4.3 series of GCC ignored the packed attribute on bit-fields of type char ๐Ÿ•ฐ๏ธ. That got fixed in GCC 4.4, but the change can cause differences in structure layout. GCC tells you when the offset of such a field has changed in GCC 4.4. For example, there's no longer a 4-bit padding between fields a and b in this structure โœจ:
struct foo
{
  char a:4;
  char b:8;
} __attribute__ ((packed));

This warning is enabled by default. To disable it, use -Wno-packed-bitfield-compat ๐Ÿ’–

  • -Wpacked-not-aligned (C, C++, Objective-C and Objective-C++ only) โ€” warn if a structure field with explicitly specified alignment in a packed struct or union is misaligned ๐Ÿงฉ. For example, this code gets a warning on struct S, like: "warning: alignment 1 of 'struct S' is less than 8" ๐Ÿ˜ฌ
struct __attribute__ ((aligned (8))) S8 { char a[8]; };
struct __attribute__ ((packed)) S {
  struct S8 s8;
};

This warning is enabled by -Wall ๐ŸŽ€

  • -Wpadded โ€” warn if padding is included in a structure, either to align one of its elements or to align the whole structure ๐Ÿซง. Sometimes you can rearrange the fields to reduce the padding and make the structure smaller ๐Ÿ“
  • -Wredundant-decls โ€” warn if anything gets declared more than once in the same scope, even when multiple declarations are valid and change nothing ๐Ÿ”
  • -Wrestrict โ€” this one catches sneaky aliasing ๐Ÿ•ต๏ธโ€โ™€๏ธ. It warns when an object referenced by a "restrict"-qualified parameter (or, in C++, a "__restrict"-qualified parameter) is aliased by another argument, or when copies between those objects overlap. In the strcpy call below, the code tries to truncate the string by overwriting its start with its last four characters. But because the call writes the terminating NUL into a[4], the copies overlap and the call gets diagnosed ๐Ÿ˜ญ:
void foo (void)
{
  char a[] = "abcd1234";
  strcpy (a, a + 4);
  ...
}

-Wrestrict catches some instances of simple overlap even without optimization, but it works best at -O2 and above. It's included in -Wall ๐Ÿ‘‘

  • -Wnested-externs (C and Objective-C only) โ€” warn if an "extern" declaration shows up inside a function ๐Ÿ™…โ€โ™€๏ธ
  • -Winline โ€” ๐Ÿฆ‹ okay so this one yells if a function you marked inline can't actually get inlined ๐Ÿ˜ญ Even with it on, GCC stays quiet about functions living in system headers, so no drama there. The compiler is basically running a vibe check on its own heuristics: the size of the function and how much inlining already happened in the current function both factor in. That means tiny edits to your source can make these warnings pop up or vanish, so don't panic if it changes randomly ๐Ÿ™ˆ
  • -Winterference-size โ€” โœจ this one warns about using C++17's std::hardware_destructive_interference_size without setting its value via --param destructive-interference-size. It also side-eyes any questionable values you pass for that option ๐Ÿ‘€

This variable is built for controlling class layout so concurrent code doesn't suffer from false sharing ๐Ÿงš Check out this serve:

struct independent_fields {
  alignas(std::hardware_destructive_interference_size)
    std::atomic<int> one;
  alignas(std::hardware_destructive_interference_size)
    std::atomic<int> two;
};

The whole point is that one and two sit far enough apart that writing to one won't force a reload of the cache line holding two ๐Ÿ’…

By default, --param destructive-interference-size and --param constructive-interference-size get set based on the current -mtune option. Usually that lands on the L1 cache line size for the target CPU, though for a generic target it can be a range instead ๐ŸŒธ So every translation unit that relies on ABI compatibility for these variables needs to be compiled with the same -mtune (or -mcpu), or things get messy fr fr.

If ABI stability matters, like when you're using it in a header for a library, you should probably skip the hardware interference size variables entirely ๐Ÿซถ Or, if you want, you can force a specific value with --param. And if you're totally sure your use doesn't touch ABI outside one build of your project, you can silence the warning with -Wno-interference-size ๐Ÿ”ฅ

  • -Wint-in-bool-context โ€” ๐ŸŽ€ this one flags sus integer values showing up where booleans are expected. Think conditional expressions (?:) using non-boolean integer constants in a boolean context, like if (a <= b ? 2 : 3). It also catches left shifting signed integers in a boolean context, like for (a = 0; 1 << a; a++);. And it's the same deal for all kinds of multiplications, no matter the data type ๐Ÿ™ƒ This warning is enabled by -Wall, so you're covered if you're already using that ๐Ÿ’–
  • -Wno-int-to-pointer-cast โ€” ๐Ÿ“ suppresses warnings about casts to a pointer type from an integer of a different size. Quick heads up though: in C++, casting to a pointer type of smaller size is an error ๐Ÿ’€ Wint-to-pointer-cast is turned on by default.
  • -Wno-pointer-to-int-cast (C and Objective-C only) โ€” ๐ŸŒˆ same energy but flipped. This one silences warnings about casts from a pointer to an integer type of a different size.
  • -Winvalid-pch โ€” ๐Ÿงš warns if a precompiled header shows up in the search path but can't actually be used. Rude, but useful ๐Ÿ˜ญ
  • -Winvalid-utf8 โ€” ๐Ÿ’œ warns when an invalid UTF-8 character shows up. It's on by default for C++23 as long as -finput-charset=UTF-8 is in use, and it becomes a full-on error with -pedantic-errors ๐Ÿšซ
  • -Wno-unicode โ€” ๐ŸŒธ stops GCC from diagnosing invalid forms of delimited or named escape sequences that get treated as separate tokens. Wunicode is enabled by default, so this is the opt-out ๐Ÿ™ˆ
  • -Wlong-long โ€” โญ warns if the long long type gets used. It's enabled by either -Wpedantic or -Wtraditional in ISO C90 and C++98 modes. Want those warnings gone? Use -Wno-long-long ๐Ÿซถ
๐Ÿšจ This warning gets upgraded to an error by -pedantic-errors, so be careful with that combo!
  • -Wvariadic-macros โ€” ๐Ÿฆ‹ warns if variadic macros are used in ISO C90 mode, or if the GNU alternate syntax is used in ISO C99 mode. It's enabled by either -Wpedantic or -Wtraditional. To shush the messages, use -Wno-variadic-macros โœจ
  • -Wno-varargs โ€” ๐Ÿ’… tells GCC to chill about questionable usage of the macros that handle variable arguments, like va_start. These warnings are on by default, so this is how you turn them off ๐ŸŒˆ
  • -Wvector-operation-performance โ€” ๐Ÿ”ฅ warns if a vector operation isn't implemented using the architecture's SIMD capabilities. It's mainly handy for performance tuning. Here's the lineup: a vector operation can be done "piecewise", meaning the scalar operation runs on every vector element; "in parallel", meaning it's implemented using scalars of a wider type, which is usually more performance efficient; or "as a single scalar", meaning the vector fits into a scalar type ๐Ÿ’–
  • -Wvla โ€” ๐ŸŽ€ warns if a variable-length array is used anywhere in the code. -Wno-vla stops the -Wpedantic warning about variable-length arrays ๐Ÿ™ƒ
๐Ÿšจ This warning gets upgraded to an error by -pedantic-errors. Don't say I didn't warn you ๐Ÿ’€
  • -Wvla-larger-than=byte-size โ€” ๐Ÿ‘‘ when this is used, the compiler warns about declarations of variable-length arrays whose size is either unbounded, or bounded by an argument that lets the array size go past byte-size bytes. It's basically the VLA cousin of how -Walloca-larger-than=byte-size works ๐Ÿงš

Heads up: GCC might optimize small variable-length arrays with a known value into plain arrays, so this warning might not fire for those ๐ŸŒธ

-Wvla-larger-than=PTRDIFF_MAX is enabled by default, but it's typically only effective when -ftree-vrp is active, which is the default for -O2 and above โœจ

See also -Walloca-larger-than=byte-size.

  • -Wno-vla-larger-than โ€” ๐Ÿ™ˆ disables the -Wvla-larger-than= warnings. It's the same as -Wvla-larger-than=SIZE_MAX or larger ๐Ÿซถ
  • -Wvla-parameter โ€” ๐Ÿ”ฎ warns about redeclarations of functions involving arguments of Variable Length Array types with inconsistent kinds or forms. It also enables detecting out-of-bounds accesses to those parameters through warnings like -Warray-bounds ๐Ÿฆ‹

If the first function declaration uses the VLA form, the bound in the array is assumed to be the minimum number of elements expected in calls to the function, and also the maximum number of elements it accesses. Not passing arguments of sufficient size, or accessing more than that maximum, may get diagnosed ๐Ÿ’€

For example, the warning triggers for these redeclarations, because the first one lets an array of any size get passed to f while the second one says the array argument must have at least n elements. Plus, calling f with the associated VLA bound parameter bigger than the actual VLA bound triggers a warning too ๐Ÿ˜ญ

void f (int n, int[n]);
// warning: argument 2 previously declared as a VLA
void f (int, int[]);

void g (int n)
{
    if (n > 4)
      return;
    int a[n];
    // warning: access to a by f may be out of bounds
    f (sizeof a, a);
  ...
}

-Wvla-parameter is included in -Wall. The -Warray-parameter option triggers warnings for similar problems involving ordinary array arguments ๐Ÿ’œ

  • -Wvolatile-register-var โ€” ๐Ÿ’… warns if a register variable is declared volatile. Here's the tea: the volatile modifier doesn't stop every optimization that might eliminate reads and/or writes to register variables. This warning is enabled by -Wall ๐ŸŒˆ
  • -Wno-xor-used-as-pow (C, C++, Objective-C and Objective-C++ only) โ€” โœจ disables warnings about uses of ^, the exclusive or operator, when it looks like the code meant exponentiation. Specifically, the warning pops up when the left-hand side is the decimal constant 2 or 10 and the right-hand side is also a decimal constant ๐Ÿ™ƒ

In C and C++, ^ means exclusive or, but in some other languages (like TeX and some versions of BASIC) it means exponentiation ๐Ÿงš

This warning can be silenced by converting one of the operands to hexadecimal, and also by compiling with -Wno-xor-used-as-pow ๐Ÿซถ

  • -Wdisabled-optimization โ€” ๐Ÿ“ warns if a requested optimization pass is disabled. Don't panic, though: this doesn't usually mean your code is wrong. It just means GCC's optimizers can't handle the code effectively ๐Ÿฅบ Often the problem is that your code is too big or too complex, and GCC refuses to optimize programs when the optimization itself would likely take an inordinate amount of time ๐Ÿ’€
  • -Wpointer-sign (C and Objective-C only) โ€” ๐Ÿ’– warns for pointer argument passing or assignment with different signedness. It's only supported for C and Objective-C. It's implied by -Wall and by -Wpedantic, and you can disable it with -Wno-pointer-sign ๐ŸŒธ

This warning gets upgraded to an error by -pedantic-errors ๐Ÿšซ

  • -Wstack-protector โ€” ๐Ÿ›ก๏ธ this one's only active when -fstack-protector is active. It warns about functions that aren't protected against stack smashing ๐Ÿ˜ญ
  • -Woverlength-strings โ€” ๐ŸŽ€ warns about string constants longer than the "minimum maximum" length the C standard specifies. Modern compilers are generally fine with string constants way longer than that minimum, but very portable programs should steer clear of longer strings ๐ŸŒˆ

The limit applies after string constant concatenation, and it doesn't count the trailing NUL. In C90 the limit was 509 characters, and in C99 it got raised to 4095. C++98 doesn't specify a normative minimum maximum, so GCC doesn't diagnose overlength strings in C++ ๐Ÿ™ˆ

This option is implied by -Wpedantic, and you can disable it with -Wno-overlength-strings โœจ

  • -Wunsuffixed-float-constants (C and Objective-C only) โ€” ๐Ÿฆ‹ issues a warning for any floating constant that doesn't have a suffix. When it's used together with -Wsystem-headers, it warns about those constants in system header files too ๐Ÿ‘€ This can be really handy when you're getting code ready to use with the "FLOAT_CONST_DECIMAL64" pragma from the decimal floating-point extension to C99 ๐Ÿ’œ
  • -Wno-lto-type-mismatch โ€” ๐Ÿ”ฅ during link-time optimization, this stops GCC from warning about type mismatches in global declarations coming from different compilation units. It requires -flto to be enabled, and it's enabled by default ๐Ÿซถ
  • -Wno-designated-init (C and Objective-C only) โ€” ๐Ÿ‘‘ suppresses warnings when a positional initializer is used to initialize a structure that's been marked with the "designated_init" attribute ๐Ÿ’…

โœจ Options That Control Static Analysis โœจ

-fanalyzer ๐Ÿ”

okay so this one turns on a static analysis of program flow that hunts for "interesting" interprocedural paths through your code and yells at you about problems it finds along them ๐Ÿ“ฃ no cap, it's a whole vibe

heads up though, this analysis is way more expensive than your regular GCC warnings ๐Ÿ’€

technically speaking, it does coverage-guided symbolic execution of the code being compiled ๐Ÿงš it is neither sound nor complete, so it can throw false positives and false negatives ๐Ÿ™ˆ it's a bug-finding tool, not a tool for proving your program correct, so don't go using it as your proof ๐Ÿซถ

the analyzer is only suitable for C code in this release ๐Ÿ’œ

enabling this option effectively turns on the following warnings ๐Ÿ‘‘

-Wanalyzer-allocation-size -Wanalyzer-deref-before-check
-Wanalyzer-double-fclose -Wanalyzer-double-free
-Wanalyzer-exposure-through-output-file
-Wanalyzer-exposure-through-uninit-copy
-Wanalyzer-fd-access-mode-mismatch -Wanalyzer-fd-double-close
-Wanalyzer-fd-leak -Wanalyzer-fd-phase-mismatch
-Wanalyzer-fd-type-mismatch -Wanalyzer-fd-use-after-close
-Wanalyzer-fd-use-without-check -Wanalyzer-file-leak
-Wanalyzer-free-of-non-heap -Wanalyzer-imprecise-fp-arithmetic
-Wanalyzer-infinite-loop -Wanalyzer-infinite-recursion
-Wanalyzer-jump-through-null -Wanalyzer-malloc-leak
-Wanalyzer-mismatching-deallocation -Wanalyzer-null-argument
-Wanalyzer-null-dereference -Wanalyzer-out-of-bounds
-Wanalyzer-overlapping-buffers -Wanalyzer-possible-null-argument
-Wanalyzer-possible-null-dereference -Wanalyzer-putenv-of-auto-var
-Wanalyzer-shift-count-negative -Wanalyzer-shift-count-overflow
-Wanalyzer-stale-setjmp-buffer -Wanalyzer-tainted-allocation-size
-Wanalyzer-tainted-array-index -Wanalyzer-tainted-assertion
-Wanalyzer-tainted-divisor -Wanalyzer-tainted-offset
-Wanalyzer-tainted-size -Wanalyzer-undefined-behavior-ptrdiff
-Wanalyzer-undefined-behavior-strtok
-Wanalyzer-unsafe-call-within-signal-handler
-Wanalyzer-use-after-free
-Wanalyzer-use-of-pointer-in-stale-stack-frame
-Wanalyzer-use-of-uninitialized-value
-Wanalyzer-va-arg-type-mismatch -Wanalyzer-va-list-exhausted
-Wanalyzer-va-list-leak -Wanalyzer-va-list-use-after-va-end
-Wanalyzer-write-to-const -Wanalyzer-write-to-string-literal
โš ๏ธ this option only works if GCC was configured with analyzer support enabled, so check that before you get your hopes up ๐Ÿฅบ

-Wanalyzer-symbol-too-complex ๐ŸŒ€

if -fanalyzer is on, the analyzer uses a bunch of heuristics to keep track of the state of memory โœจ but honestly, sufficiently complicated code can totally defeat them ๐Ÿ˜ญ

by default, the analysis quietly stops tracking the values of expressions that go over the threshold set by --param analyzer-max-svalue-depth=value, and it falls back to an imprecise representation for those expressions ๐Ÿซ  -Wanalyzer-symbol-too-complex is the one that actually warns you when this happens ๐Ÿ“ข

-Wanalyzer-too-complex ๐ŸŒช๏ธ

same energy as above: with -fanalyzer enabled, the analyzer uses heuristics to explore control flow and data flow in your program ๐Ÿฆ‹ but again, sufficiently complicated code can defeat it ๐Ÿ’…

by default, the analysis quietly gives up if the code is too complicated to fully explore and it hits an internal limit ๐Ÿ™ˆ -Wanalyzer-too-complex is what warns you when that happens ๐Ÿšจ

-Wno-analyzer-allocation-size ๐Ÿ“

this warning needs -fanalyzer to be on, which is what enables it ๐Ÿ’– to turn it off, use -Wno-analyzer-allocation-size ๐Ÿ™…โ€โ™€๏ธ

this diagnostic warns about paths through the code where a pointer to a buffer gets assigned to point at a buffer whose size is not a multiple of sizeof (*pointer) ๐Ÿง

see also CWE-131: Incorrect Calculation of Buffer Size (https://cwe.mitre.org/data/definitions/131.html) ๐Ÿ”—

-Wno-analyzer-deref-before-check ๐Ÿซฃ

this one also requires -fanalyzer to enable it, and you can disable it with -Wno-analyzer-deref-before-check ๐Ÿ’œ

this diagnostic warns about paths where a pointer gets checked for NULL after it's already been dereferenced, which suggests the pointer could have been NULL all along ๐Ÿ˜ฌ those cases mean the NULL check is either redundant, or it needs to move to before the dereference ๐Ÿ”

it also treats values passed to a function argument marked with __attribute__((nonnull)) as needing a non-NULL value, so it'll complain if those values get checked for NULL after the function call returns ๐ŸŽ€

โš ๏ธ this diagnostic is unlikely to show up when any level of optimization is enabled, because GCC's optimizer usually sees those NULL checks as redundant and optimizes them away before the analyzer ever gets a look ๐Ÿฅฒ so if you're trying to trigger it, turn optimization off first ๐Ÿ™

-Wno-analyzer-double-fclose ๐Ÿ“‚

requires -fanalyzer to be on, and you turn it off with -Wno-analyzer-double-fclose ๐ŸŒธ

this diagnostic warns about paths where a FILE * can have fclose called on it more than once ๐Ÿ’€

see also CWE-1341: Multiple Releases of Same Resource or Handle (https://cwe.mitre.org/data/definitions/1341.html) ๐Ÿ”—

-Wno-analyzer-double-free ๐Ÿชฆ

again, you need -fanalyzer for this one, and you disable it with -Wno-analyzer-double-free ๐Ÿ™ƒ

this diagnostic warns about paths where a pointer can have a deallocator called on it more than once ๐Ÿ˜ฑ that deallocator can be free, or a deallocator referenced by attribute malloc ๐Ÿงน

see also CWE-415: Double Free (https://cwe.mitre.org/data/definitions/415.html) ๐Ÿ”—

-Wno-analyzer-exposure-through-output-file ๐Ÿคซ

needs -fanalyzer to enable it, and to disable it you use -Wno-analyzer-exposure-through-output-file ๐Ÿ™…โ€โ™€๏ธ

this diagnostic warns about paths where a security-sensitive value gets written to an output file, like writing a password into a log file ๐Ÿ˜ญ

see also CWE-532: Information Exposure Through Log Files (https://cwe.mitre.org/data/definitions/532.html) ๐Ÿ”—

-Wanalyzer-exposure-through-uninit-copy ๐Ÿ•ต๏ธโ€โ™€๏ธ

this one needs both -fanalyzer and the use of a plugin that specifies a function copying across a "trust boundary" ๐Ÿ”Œ to disable it, use -Wno-analyzer-exposure-through-uninit-copy ๐Ÿ™…โ€โ™€๏ธ

this diagnostic warns about "infoleaks", which are paths where uninitialized values get copied across a security boundary ๐Ÿซฃ think of code inside an OS kernel copying a partly initialized struct from the stack out to user space ๐Ÿ’€

see also CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (https://cwe.mitre.org/data/definitions/200.html) ๐Ÿ”—

-Wno-analyzer-fd-access-mode-mismatch ๐Ÿ“–

this warning needs -fanalyzer to be on, and you turn it off with -Wno-analyzer-fd-access-mode-mismatch ๐ŸŽ€

this diagnostic warns about paths where a "read" is attempted on a write-only file descriptor, or the other way around ๐Ÿ”„

it also warns about code paths where a function with attribute fd_arg_read (N) gets called with a file descriptor opened with O_WRONLY at referenced argument N, or a function with attribute fd_arg_write (N) gets called with a file descriptor opened with O_RDONLY at referenced argument N โœจ

-Wno-analyzer-fd-double-close โœ‚๏ธ

requires -fanalyzer to enable, and you disable it with -Wno-analyzer-fd-double-close ๐Ÿ’…

this diagnostic warns about paths where a file descriptor can be closed more than once ๐Ÿ˜ต

see also CWE-1341: Multiple Releases of Same Resource or Handle (https://cwe.mitre.org/data/definitions/1341.html) ๐Ÿ”—

-Wno-analyzer-fd-leak ๐Ÿ’ง

you need -fanalyzer for this one, and to turn it off use -Wno-analyzer-fd-leak ๐Ÿซถ

this diagnostic warns about paths where an open file descriptor gets leaked ๐Ÿšฐ

see also CWE-775: Missing Release of File Descriptor or Handle after Effective Lifetime (https://cwe.mitre.org/data/definitions/775.html) ๐Ÿ”—

-Wno-analyzer-fd-phase-mismatch โณ

requires -fanalyzer, and you disable it with -Wno-analyzer-fd-phase-mismatch ๐ŸŒท

this diagnostic warns about paths where an operation gets attempted in the wrong phase of a file descriptor's lifetime ๐Ÿงฌ for example, it'll warn if you try to call accept on a stream socket that hasn't had listen successfully called on it yet ๐Ÿ™ˆ

see also CWE-666: Operation on Resource in Wrong Phase of Lifetime (https://cwe.mitre.org/data/definitions/666.html) ๐Ÿ”—

-Wno-analyzer-fd-type-mismatch ๐Ÿงฉ

needs -fanalyzer to enable, and you turn it off with -Wno-analyzer-fd-type-mismatch ๐Ÿ’–

this diagnostic warns about paths where an operation is attempted on the wrong type of file descriptor ๐Ÿ™…โ€โ™€๏ธ for example, it'll warn if you try socket operations on a file descriptor you got from open, or if you try a stream socket operation on a datagram socket ๐Ÿ“ก

-Wno-analyzer-fd-use-after-close ๐Ÿšช

this one requires -fanalyzer, and you disable it with -Wno-analyzer-fd-use-after-close ๐Ÿ™ƒ

this diagnostic warns about paths where a read or write gets called on a closed file descriptor ๐Ÿ’€

it also warns about paths where a function with attribute fd_arg (N), fd_arg_read (N), or fd_arg_write (N) gets called with a closed file descriptor at referenced argument N ๐Ÿซ 

-Wno-analyzer-fd-use-without-check ๐Ÿ”Ž

needs -fanalyzer to be on, and to disable it you use -Wno-analyzer-fd-use-without-check ๐ŸŒˆ

this diagnostic warns about paths where a file descriptor gets used without being checked for validity ๐Ÿ‘€

it also warns about paths where a function with attribute fd_arg (N), fd_arg_read (N), or fd_arg_write (N) gets called with a file descriptor at referenced argument N without being checked for validity ๐Ÿซถ

-Wno-analyzer-file-leak ๐Ÿ—‚๏ธ

requires -fanalyzer, and you turn it off with -Wno-analyzer-file-leak โœจ

this diagnostic warns about paths through the code where a <stdio.h> FILE * stream object gets leaked ๐Ÿฅน

see also CWE-775: Missing Release of File Descriptor or Handle after Effective Lifetime (https://cwe.mitre.org/data/definitions/775.html) ๐Ÿ”—

Okay so the analyzer warnings keep rolling in, bestie ๐Ÿ’…๐ŸŒธ

  • -Wno-analyzer-free-of-non-heap โ€” ๐Ÿซถ this one needs -fanalyzer to be on (that's what switches it on), and if it's bugging you, -Wno-analyzer-free-of-non-heap shuts it up. It flags paths where free gets called on a pointer that isn't on the heap, like an on-stack buffer or something global. Lowkey a classic mistake ๐Ÿ™ˆ
  • -Wno-analyzer-imprecise-fp-arithmetic โ€” โœจ again, this needs -fanalyzer to be enabled, and -Wno-analyzer-imprecise-fp-arithmetic turns it off. It warns on paths where floating-point math is used somewhere that needs precise results. Right now it only checks floating-point operands that go into calculating an allocation size ๐Ÿงฎ
  • -Wno-analyzer-infinite-loop โ€” ๐Ÿ” requires -fanalyzer, and -Wno-analyzer-infinite-loop is the mute button. This one warns on paths that look like they lead into an infinite loop. The analyzer speaks up when it spots a loop where:
    • no externally-visible work could be happening inside the loop
    • there's no way to escape from the loop
    • the analyzer is confident enough about the program state across the whole loop to know that both of the above are true

One way this gets triggered is when there's an execution path through a loop where taking that path on one iteration means it gets taken on every iteration after too. Like, consider this:

          while (1)
            {
              char opcode = *cpu_state.pc;
              switch (opcode)
               {
               case OPCODE_FOO:
                 handle_opcode_foo (&cpu_state);
                 break;
               case OPCODE_BAR:
                 handle_opcode_bar (&cpu_state);
                 break;
               }
            }

The analyzer will absolutely complain about that one ๐Ÿ˜ญ because if opcode ever matches none of the cases, the switch falls into the implicit default case. That makes the loop body a "no-op" with cpu_state.pc unchanged, so every later iteration uses the same opcode value, and boom, infinite loop ๐Ÿ’€

  • -Wno-analyzer-infinite-recursion โ€” ๐ŸŒ€ needs -fanalyzer, and -Wno-analyzer-infinite-recursion silences it. This one warns on paths that seem to lead into infinite recursion. Here's the vibe: whenever the analyzer sees a recursive call, it compares the state of memory at the entry of the new frame with the state at the entry of the previous frame for that same function on the stack. If nothing in memory seems to change, it warns. But if parameters or globals change, it assumes the recursion will eventually end and keeps quiet ๐Ÿคซ

This one can miss cases where the optimizer turns the recursion into iteration before the analyzer gets a look, so you should turn optimization off if you're trying to trigger it on purpose.

  • -Wno-analyzer-jump-through-null โ€” ๐Ÿ™ƒ requires -fanalyzer, and -Wno-analyzer-jump-through-null turns it off. It warns on paths where a NULL function pointer gets called. Yikes, don't call the void ๐Ÿ‘ป
  • -Wno-analyzer-malloc-leak โ€” ๐Ÿ’ง needs -fanalyzer, and -Wno-analyzer-malloc-leak mutes it. This one warns on paths where a pointer from an allocator gets leaked, whether that allocator is malloc or a function marked with attribute malloc. Memory that never gets released is not it ๐Ÿ™…โ€โ™€๏ธ
  • -Wno-analyzer-mismatching-deallocation โ€” ๐ŸŽ€ requires -fanalyzer, and -Wno-analyzer-mismatching-deallocation is the off switch. It warns on paths where the wrong deallocation function gets called on a pointer, based on which function allocated it. It'll catch mismatches between free, scalar delete, and vector delete[], plus any allocator and deallocator pairs marked with attribute malloc. Mixing up your partners is a no-go ๐Ÿ’…
  • -Wno-analyzer-out-of-bounds โ€” ๐Ÿšง needs -fanalyzer, and -Wno-analyzer-out-of-bounds turns it off. It warns on paths where a buffer is definitely read or written out of bounds. It works when the analyzer can work out a constant offset, and for accesses past the end of a buffer with a constant capacity. It also does a bit of limited checking for accesses past the end when both the offset and the capacity are symbolic ๐Ÿ‘€

When it can, the analyzer also prints a text art diagram showing how the memory it predicts would be accessed relates to the memory that's actually valid to access. It shows whether they overlap, touch, sit close together, or are far apart, which one comes first in memory, the relative sizes, whether it's a read or a write, and sometimes even the data values involved. Honestly so cute ๐Ÿฆ‹ If you don't want the diagram, you can suppress it with -fdiagnostics-text-art-charset=none.

  • -Wno-analyzer-overlapping-buffers โ€” ๐Ÿงฉ requires -fanalyzer, and -Wno-analyzer-overlapping-buffers mutes it. This one warns on paths where overlapping buffers get passed to an API that behaves undefined on overlapping buffers. Specifically, it triggers on calls to these functions:
    • memcpy
    • strcat
    • strcpy

but only when the buffers are known to overlap โš ๏ธ

  • -Wno-analyzer-possible-null-argument โ€” ๐Ÿค” needs -fanalyzer, and -Wno-analyzer-possible-null-argument silences it. This one warns on paths where a possibly-NULL value gets passed to a function argument marked with __attribute__((nonnull)) as needing a non-NULL value.
  • -Wno-analyzer-possible-null-dereference โ€” ๐Ÿซฃ requires -fanalyzer, and -Wno-analyzer-possible-null-dereference turns it off. It warns on paths where a possibly-NULL value gets dereferenced.
  • -Wno-analyzer-null-argument โ€” ๐Ÿšซ needs -fanalyzer, and -Wno-analyzer-null-argument is the mute button. It warns on paths where a value known to be NULL gets passed to a function argument marked with __attribute__((nonnull)) as needing a non-NULL value.
  • -Wno-analyzer-null-dereference โ€” ๐Ÿฅ€ requires -fanalyzer, and -Wno-analyzer-null-dereference shuts it up. It warns on paths where a value known to be NULL gets dereferenced.
  • -Wno-analyzer-putenv-of-auto-var โ€” ๐ŸŒฟ needs -fanalyzer, and -Wno-analyzer-putenv-of-auto-var is the off switch. It warns on paths where a call to putenv gets passed a pointer to an automatic variable or an on-stack buffer.
  • -Wno-analyzer-shift-count-negative โ€” โž– requires -fanalyzer, and -Wno-analyzer-shift-count-negative turns it off. It warns on paths where a shift is attempted with a negative count. It's the analyzer's cousin of -Wshift-count-negative, which lives in the C/C++ front ends. The difference is that this one works by analyzing interprocedural paths instead of just parsing the syntax tree. Heads up though: the analyzer doesn't prioritize finding these paths, so false negatives are more likely compared to other warnings ๐Ÿฅบ
  • -Wno-analyzer-shift-count-overflow โ€” ๐ŸŒŠ needs -fanalyzer, and -Wno-analyzer-shift-count-overflow silences it. It warns on paths where a shift is attempted with a count greater than or equal to the precision of the operand's type. It's the analyzer's version of -Wshift-count-overflow from the C/C++ front ends, but it works by analyzing interprocedural paths rather than just parsing the syntax tree. Same catch as before: the analyzer doesn't prioritize detecting these paths, so false negatives are more likely relative to other warnings.
  • -Wno-analyzer-stale-setjmp-buffer โ€” ๐Ÿ•ฐ๏ธ requires -fanalyzer, and -Wno-analyzer-stale-setjmp-buffer is the mute option. It warns on paths where longjmp gets called to rewind to a jmp_buf tied to a setjmp call in a function that has already returned. That's a stale buffer, and jumping into it is a whole mess ๐Ÿ˜ตโ€๐Ÿ’ซ

The analyzer has a sneaky habit of checking whether a setjmp rewind point is still alive. When setjmp is called on a jmp_buf to record a rewind location, it records the stack frame. That frame becomes invalid once the function containing the setjmp call returns. Trying to rewind to it with longjmp would point at a stack frame that no longer exists, which likely leads to a crash (or worse) ๐Ÿ˜ญ๐Ÿซฃ

  • -Wno-analyzer-tainted-allocation-size โ€” โœจ this one needs -fanalyzer to turn it on, and you can silence it with -Wno-analyzer-tainted-allocation-size. It flags paths where a value an attacker could control gets used as the size of an allocation without being sanitized first. That means an attacker could inject a huge allocation and cause a denial of service ๐Ÿ™…โ€โ™€๏ธ

Check out CWE-789: Memory Allocation with Excessive Size Value ("https://cwe.mitre.org/data/definitions/789.html").

  • -Wno-analyzer-tainted-assertion โ€” ๐Ÿฆ‹ also needs -fanalyzer, and you turn it off with -Wno-analyzer-tainted-assertion. It warns about paths where an unsanitized, attacker-controllable value is part of a condition, and that condition guards a call to a function marked with attribute noreturn (like __builtin_unreachable). Those functions usually mean the program is terminating abnormally, like assertion failure handlers. For example:
        assert (some_tainted_value < SOME_LIMIT);

In those cases, two things can happen:

  • when assertion-checking is enabled, an attacker could trigger a denial of service by injecting an assertion failure
  • when assertion-checking is disabled (say, by defining NDEBUG), an attacker could inject data that subverts the process, since it presumably violates a precondition the code is assuming

Heads up: when assertion-checking is disabled, the preprocessor usually strips the assertions out before the analyzer gets a chance to "see" them. So this diagnostic can only generate warnings on builds where assertion-checking is enabled ๐Ÿ‘€

For this warning, any function marked with attribute noreturn counts as a possible assertion failure handler, including __builtin_unreachable. The optimizer sometimes removes these functions before the analyzer "sees" them, so disable optimization when you're trying to trigger this diagnostic ๐Ÿ”ฅ

See CWE-617: Reachable Assertion ("https://cwe.mitre.org/data/definitions/617.html").

The warning can also catch problematic constructions like this one:

        switch (some_tainted_value) {
        case 0:
          /* [...etc; various valid cases omitted...] */
          break;

        default:
          __builtin_unreachable (); /* BUG: attacker can trigger this  */
        }

Strictly speaking that's not an assertion failure, but the warning still shows up for it ๐Ÿ’…

  • -Wno-analyzer-tainted-array-index โ€” ๐ŸŒธ requires -fanalyzer to enable, and -Wno-analyzer-tainted-array-index switches it off. It flags paths where an attacker-controllable value is used as an array index without being sanitized, so an attacker could inject an out-of-bounds access.

See CWE-129: Improper Validation of Array Index ("https://cwe.mitre.org/data/definitions/129.html").

  • -Wno-analyzer-tainted-divisor โ€” ๐Ÿงš needs -fanalyzer on, and you can turn it off with -Wno-analyzer-tainted-divisor. It warns about paths where an unsanitized, attacker-controllable value is used as the divisor in a division or modulus operation, which means an attacker could inject a division-by-zero.

See CWE-369: Divide By Zero ("https://cwe.mitre.org/data/definitions/369.html").

  • -Wno-analyzer-tainted-offset โ€” ๐Ÿ“ requires -fanalyzer, and -Wno-analyzer-tainted-offset disables it. It warns about paths where an attacker-controllable value is used as a pointer offset without being sanitized, so an attacker could inject an out-of-bounds access.

See CWE-823: Use of Out-of-range Pointer Offset ("https://cwe.mitre.org/data/definitions/823.html").

  • -Wno-analyzer-tainted-size โ€” ๐Ÿ’œ needs -fanalyzer to be on, and you disable it with -Wno-analyzer-tainted-size. It warns about paths where an unsanitized, attacker-controllable value is used as the size of an operation such as memset, so an attacker could inject an out-of-bounds access.

See CWE-129: Improper Validation of Array Index ("https://cwe.mitre.org/data/definitions/129.html").

  • -Wno-analyzer-undefined-behavior-ptrdiff โ€” โญ requires -fanalyzer, and you silence it with -Wno-analyzer-undefined-behavior-ptrdiff. It flags paths where a pointer subtraction happens between pointers that refer to different chunks of memory. That code leans on undefined behavior, because pointer subtraction is only defined when both pointers point within (or just past) the same array ๐Ÿ™ˆ

See CWE-469: Use of Pointer Subtraction to Determine Size ("https://cwe.mitre.org/data/definitions/469.html").

  • -Wno-analyzer-undefined-behavior-strtok โ€” ๐ŸŽ€ requires -fanalyzer, and -Wno-analyzer-undefined-behavior-strtok turns it off. It warns about paths where a call to strtok has undefined behavior. Specifically, passing NULL as the first parameter for the initial call to strtok within a process has undefined behavior.
  • -Wno-analyzer-unsafe-call-within-signal-handler โ€” ๐Ÿซถ requires -fanalyzer, and you can disable it with -Wno-analyzer-unsafe-call-within-signal-handler. It flags paths where a function known to be async-signal-unsafe (such as fprintf) gets called from a signal handler.

See CWE-479: Signal Handler Use of a Non-reentrant Function ("https://cwe.mitre.org/data/definitions/479.html").

  • -Wno-analyzer-use-after-free โ€” ๐Ÿ’€ needs -fanalyzer on, and -Wno-analyzer-use-after-free switches it off. It warns about paths where a pointer gets used after a deallocator has been called on it. A deallocator here is either free, or a deallocator referenced by attribute malloc.

See CWE-416: Use After Free ("https://cwe.mitre.org/data/definitions/416.html").

  • -Wno-analyzer-use-of-pointer-in-stale-stack-frame โ€” ๐ŸŒˆ requires -fanalyzer, and you can turn it off with -Wno-analyzer-use-of-pointer-in-stale-stack-frame. It warns about paths where a pointer is dereferenced and points to a variable in a stale stack frame.
  • -Wno-analyzer-va-arg-type-mismatch โ€” ๐Ÿ‘‘ needs -fanalyzer, and -Wno-analyzer-va-arg-type-mismatch disables it. It warns about interprocedural paths where the analyzer detects an attempt to use va_arg to extract a value passed to a variadic call, but with a type that doesn't match the expression passed to the call.

See CWE-686: Function Call With Incorrect Argument Type ("https://cwe.mitre.org/data/definitions/686.html").

  • -Wno-analyzer-va-list-exhausted โ€” ๐Ÿฆ‹ requires -fanalyzer to enable, and you disable it with -Wno-analyzer-va-list-exhausted. It warns about interprocedural paths where the analyzer sees an attempt to use va_arg to access the next value passed to a variadic call, but every value in the va_list has already been consumed.

See CWE-685: Function Call With Incorrect Number of Arguments ("https://cwe.mitre.org/data/definitions/685.html").

  • -Wno-analyzer-va-list-leak โ€” ๐ŸŒธ needs -fanalyzer, and -Wno-analyzer-va-list-leak turns it off. It warns about interprocedural paths where the analyzer detects that va_start or va_copy has been called on a va_list without a matching call to va_end.
  • -Wno-analyzer-va-list-use-after-va-end โ€” ๐Ÿ’… requires -fanalyzer, and you silence it with -Wno-analyzer-va-list-use-after-va-end. It warns about interprocedural paths where a va_list gets used after va_end has been called on it. "va_list".
  • -Wno-analyzer-write-to-const โ€” ๐Ÿงš needs -fanalyzer on, and -Wno-analyzer-write-to-const disables it. It warns about paths where the analyzer detects an attempt to write through a pointer to a const object. Heads up though: the analyzer doesn't prioritize catching these paths, so false negatives are more likely compared to other warnings ๐Ÿฅบ
  • -Wno-analyzer-write-to-string-literal โ€” ๐Ÿ“ requires -fanalyzer, and -Wno-analyzer-write-to-string-literal switches it off. It warns about paths where the analyzer detects an attempt to write through a pointer to a string literal. Same deal as above: the analyzer doesn't prioritize these paths, so false negatives are more likely compared to other warnings.
  • -Wno-analyzer-use-of-uninitialized-value โ€” ๐Ÿ’œ needs -fanalyzer to be enabled, and you can disable it with -Wno-analyzer-use-of-uninitialized-value. It warns about paths where an uninitialized value gets used.

See CWE-457: Use of Uninitialized Variable ("https://cwe.mitre.org/data/definitions/457.html").

The analyzer has hardcoded knowledge about the behavior of the following memory-management functions:

okay so the analyzer already knows a whole squad of memory functions, bestie ๐Ÿงš and it's giving very specific vibes with each one:

  • "alloca"
  • The built-in functions "__builtin_alloc", "__builtin_alloc_with_align", @item "__builtin_calloc", "__builtin_free", "__builtin_malloc", "__builtin_memcpy", "__builtin_memcpy_chk", "__builtin_memset", "__builtin_memset_chk", "__builtin_realloc", "__builtin_stack_restore", and "__builtin_stack_save"
  • "calloc"
  • "free"
  • "malloc"
  • "memset"
  • "operator delete"
  • "operator delete []"
  • "operator new"
  • "operator new []"
  • "realloc"
  • "strdup"
  • "strndup"

It also knows these file descriptor functions, which are so iconic ๐ŸŒธ

  • "open"
  • "close"
  • "creat"
  • "dup", "dup2", and "dup3"
  • "isatty"
  • "pipe", and "pipe2"
  • "read"
  • "write"
  • "socket", "bind", "listen", "accept", and "connect"

Then there's the <stdio.h> stream crew ๐Ÿ“š which is a whole mood:

  • The built-in functions "__builtin_fprintf", "__builtin_fprintf_unlocked", "__builtin_fputc", "__builtin_fputc_unlocked", "__builtin_fputs", "__builtin_fputs_unlocked", "__builtin_fwrite", "__builtin_fwrite_unlocked", "__builtin_printf", "__builtin_printf_unlocked", "__builtin_putc", "__builtin_putchar", "__builtin_putchar_unlocked", "__builtin_putc_unlocked", "__builtin_puts", "__builtin_puts_unlocked", "__builtin_vfprintf", and "__builtin_vprintf"
  • "fopen"
  • "fclose"
  • "ferror"
  • "fgets"
  • "fgets_unlocked"
  • "fileno"
  • "fread"
  • "getc"
  • "getchar"
  • "fprintf"
  • "printf"
  • "fwrite"

And of the following functions too, periodt ๐Ÿ’…

  • The built-in functions "__builtin_expect", "__builtin_expect_with_probability", "__builtin_strchr", "__builtin_strcpy", "__builtin_strcpy_chk", "__builtin_strlen", "__builtin_va_copy", and "__builtin_va_start"
  • The GNU extensions "error" and "error_at_line"
  • "getpass"
  • "longjmp"
  • "putenv"
  • "setjmp"
  • "siglongjmp"
  • "signal"
  • "sigsetjmp"
  • "strcat"
  • "strchr"
  • "strlen"

Also, any function with an "__analyzer_" prefix has special meaning to the analyzer ๐Ÿ”ฎ and that's all written up in the GCC Internals manual. no cap, go read it if you're curious โœจ

Pertinent parameters for controlling the exploration are these four bad boys ๐ŸŽ€

  • --param analyzer-bb-explosion-factor=value
  • --param analyzer-max-enodes-per-program-point=value
  • --param analyzer-max-recursion-depth=value
  • --param analyzer-min-snodes-for-call-summary=value

The following options control the analyzer ๐Ÿ’–

  • -fanalyzer-call-summaries โ€” okay this one simplifies interprocedural analysis by computing the effect of certain calls, instead of exploring every single path through the function from the callsite to each possible return. It's giving efficiency ๐Ÿง  If enabled, call summaries are only used for functions with more than one call site, and that are sufficiently complicated (as per --param analyzer-min-snodes-for-call-summary=value).
  • -fanalyzer-checker=name โ€” restricts the analyzer to run just the named checker, and enables it too ๐ŸŽฏ
  • -fanalyzer-debug-text-art-headings โ€” this one is for the analyzer developers ๐Ÿ› ๏ธ If enabled, the analyzer adds extra annotations to any diagrams it generates.
  • -fno-analyzer-feasibility โ€” also for analyzer developers. By default the analyzer checks that there is a feasible control flow path for each diagnostic it emits, meaning the conditions that hold are not mutually exclusive. Diagnostics where no feasible path can be found get rejected ๐Ÿ™…โ€โ™€๏ธ This filtering can be suppressed with -fno-analyzer-feasibility, for debugging issues in this code.
  • -fanalyzer-fine-grained โ€” developer-only vibes ๐Ÿงช Internally the analyzer builds an "exploded graph" that combines control flow graphs with data flow information. By default, an edge in this graph can contain the effects of a run of multiple statements within a basic block. With -fanalyzer-fine-grained, each statement gets its own edge.
  • -fanalyzer-show-duplicate-count โ€” another developer one ๐Ÿ™ˆ If multiple diagnostics have been detected as duplicates of each other, it emits a note when reporting the best diagnostic, giving the number of additional diagnostics that were suppressed by the deduplication logic.
  • -fanalyzer-show-events-in-system-headers โ€” by default the analyzer hides events fully located within a system header so the diagnostic paths stay simplified โœจ With -fanalyzer-show-events-in-system-headers, those events stop getting suppressed.
  • -fno-analyzer-state-merge โ€” developer-only ๐Ÿซถ By default the analyzer tries to simplify things by merging sufficiently similar states at each program point as it builds its "exploded graph". With -fno-analyzer-state-merge this merging can be suppressed, for debugging state-handling issues.
  • -fno-analyzer-state-purge โ€” developer-only ๐Ÿงน By default the analyzer purges aspects of state at a program point that appear to no longer be relevant, e.g. the values of locals that aren't accessed later in the function and which aren't relevant to leak analysis. With -fno-analyzer-state-purge this purging can be suppressed, for debugging state-handling issues.
  • -fno-analyzer-suppress-followups โ€” developer-only ๐Ÿ‘€ By default the analyzer stops exploring an execution path after encountering certain diagnostics, so it doesn't spam a cascade of follow-up diagnostics. The diagnostics that terminate analysis along a path are:
    • -Wanalyzer-null-argument
    • -Wanalyzer-null-dereference
    • -Wanalyzer-use-after-free
    • -Wanalyzer-use-of-pointer-in-stale-stack-frame
    • -Wanalyzer-use-of-uninitialized-value

With -fno-analyzer-suppress-followups the analyzer keeps exploring such paths even after those diagnostics, which may be helpful for debugging issues in the analyzer, or for microbenchmarks for detecting undefined behavior.

  • -fanalyzer-transitivity โ€” enables transitivity of constraints within the analyzer ๐Ÿ’œ
  • -fno-analyzer-undo-inlining โ€” developer-only ๐Ÿฆ‹ -fanalyzer runs relatively late compared to other code analysis tools, and some optimizations have already been applied to the code. In particular, function inlining may have occurred, so the interprocedural execution paths the analyzer emits can contain function frames that don't match the original source code. By default the analyzer tries to reconstruct the original function frames, and to emit events showing the inlined calls. With -fno-analyzer-undo-inlining this attempt to reconstruct the original frame information gets disabled, which may help when debugging issues in the analyzer.
  • -fanalyzer-verbose-edges โ€” developer-only ๐Ÿ” It enables more verbose, lower-level detail in the descriptions of control flow within diagnostic paths.
  • -fanalyzer-verbose-state-changes โ€” developer-only ๐ŸŒ€ It enables more verbose, lower-level detail in the descriptions of events relating to state machines within diagnostic paths.
  • -fanalyzer-verbosity=level โ€” controls how complex the control flow paths emitted for analyzer diagnostics are ๐Ÿ“œ The level can be one of:
    • 0 โ€” at this level, interprocedural call and return events are displayed, along with the most pertinent state-change events relating to a diagnostic. For example, for a double-"free" diagnostic, both calls to "free" will be shown.
    • 1 โ€” same as the previous level, but also shows events for the entry to each function.
    • 2 โ€” same as the previous level, but also shows events relating to control flow that are significant to triggering the issue (e.g. "true path taken" at a conditional). This level is the default ๐Ÿ‘‘
    • 3 โ€” same as the previous level, but shows all control flow events, not just significant ones.
    • 4 โ€” developer-only ๐Ÿ› ๏ธ this level adds various other events intended for debugging the analyzer.
  • -fdump-analyzer โ€” dumps internal details about what the analyzer is doing to file.analyzer.txt. -fdump-analyzer-stderr overrides this option.
  • -fdump-analyzer-stderr โ€” dumps internal details about what the analyzer is doing to stderr. This option overrides -fdump-analyzer.
  • -fdump-analyzer-callgraph โ€” dumps a representation of the call graph suitable for viewing with GraphViz to file.callgraph.dot ๐Ÿ—บ๏ธ
  • -fdump-analyzer-exploded-graph โ€” dumps a representation of the "exploded graph" suitable for viewing with GraphViz to file.eg.dot. Nodes are color-coded based on state-machine states to emphasize state changes ๐ŸŒˆ
  • -fdump-analyzer-exploded-nodes โ€” emits diagnostics showing where nodes in the "exploded graph" are in relation to the program source.
  • -fdump-analyzer-exploded-nodes-2 โ€” dumps a textual representation of the "exploded graph" to file.eg.txt.
  • -fdump-analyzer-exploded-nodes-3 โ€” dumps a textual representation of the "exploded graph" to one dump file per node, as file.eg-id.txt. This is typically a large number of dump files, so brace yourself ๐Ÿ˜ญ
  • -fdump-analyzer-exploded-paths โ€” dumps a textual representation of the "exploded path" for each diagnostic to file.idx.kind.epath.txt.
  • -fdump-analyzer-feasibility โ€” dumps internal details about the analyzer's search for feasible paths. The details are written in a form suitable for viewing with GraphViz, to filenames of the form file.*.fg.dot, file.*.tg.dot, and file.*.fpath.txt ๐Ÿงฉ
  • -fdump-analyzer-infinite-loop โ€” dumps internal details about the analyzer's search for infinite loops. The details are written in a form suitable for viewing with GraphViz, to filenames of the form file.*.infinite-loop.dot โ™พ๏ธ
  • -fdump-analyzer-json โ€” dumps a compressed JSON representation of analyzer internals to file.analyzer.json.gz. The precise format is subject to change, so don't build your life around it ๐Ÿซ 

Okay so the analyzer dumps are next, bestie ๐Ÿ’…๐Ÿซถ

  • -fdump-analyzer-state-purge โ€” ๐Ÿ”ฎ Same deal as -fdump-analyzer-supergraph, but this one annotates the "supergraph" with what state gets purged at each node. It's written to file.state-purge.dot, and you view it with GraphViz.
  • -fdump-analyzer-supergraph โ€” ๐Ÿ—บ๏ธ Dumps the "supergraph" for GraphViz into file.supergraph.dot and file.supergraph-eg.dot. It shows every control flow graph in the program, with interprocedural edges for calls and returns. The second dump is the glow-up: it has annotations for nodes in the "exploded graph" plus any diagnostics tied to them โœจ
  • -fdump-analyzer-untracked โ€” ๐Ÿ› ๏ธ Emits custom warnings packed with internal details. This one is for the analyzer developers, not us mortals ๐Ÿ™ˆ

๐Ÿž Options for Debugging Your Program

Okay so if you want GCC to throw extra info at your debugger, you literally only need to add -g to your other options in almost every case ๐Ÿซถ Some debug formats can co-exist (like DWARF with CTF) as long as you enable each one explicitly with its own option.

-g with -O is allowed!! But the shortcuts optimized code takes can be lowkey surprising ๐Ÿ’€ Some variables you declared might not exist at all, control flow might jump somewhere you didn't expect, some statements won't run because they compute constant results or their values are already at hand, and some statements might execute in different places because they got moved out of loops. Still, you can debug optimized output, so it's reasonable to use the optimizer for programs that might have bugs ๐Ÿ”ฅ

If you aren't using some other optimization option, consider -Og with -g. With no -O option at all, some compiler passes that collect debugging info don't run, so -Og might give you a better debugging experience โœจ

  • -g โ€” ๐Ÿ› Produces debugging info in the operating system's native format (stabs, COFF, XCOFF, or DWARF). GDB can read this.

On most systems that use stabs, -g turns on extra info that only GDB can use. That makes debugging in GDB way better, but other debuggers might crash or refuse to read the program ๐Ÿ˜ญ If you want to be certain about whether the extra info gets generated, use -gvms (see below).

  • -ggdb โ€” ๐Ÿ’– Produces debugging info for GDB. It uses the most expressive format available (DWARF, stabs, or the native format if neither of those are supported), and it includes GDB extensions whenever possible.
  • -gdwarf, -gdwarf-version โ€” ๐ŸŒธ Produces debugging info in DWARF format (if your target supports it). version can be 2, 3, 4 or 5. The default is 5 for most targets, except VxWorks, TPF and Darwin / macOS, which default to 2, and AIX, which defaults to 4.

Heads up: with DWARF Version 2, some ports require and always use some non-conflicting DWARF 3 extensions in the unwind tables.

Version 4 may need GDB 7.0 and -fvar-tracking-assignments for the maximum benefit. Version 5 needs GDB 8.0 or higher.

GCC no longer supports DWARF Version 1, which is substantially different from Version 2 and later. For historical reasons, some other DWARF-related options such as -fno-dwarf2-cfi-asm) keep a reference to DWARF Version 2 in their names, but they apply to all currently supported versions of DWARF.
  • -gbtf โ€” ๐Ÿฆ‹ Requests BTF debug info. BTF is the default debugging format for the eBPF target. On other targets, like x86, BTF debug info can be generated along with DWARF debug info when both formats are enabled explicitly through their respective command line options.
  • -gprune-btf, -gno-prune-btf โ€” โœ‚๏ธ Prunes BTF info before it's emitted. When pruning, only type info for types used by global variables and file-scope functions gets emitted. If you're compiling for the BPF target with BPF CO-RE enabled, type info also gets emitted for types used in BPF CO-RE relocations. On top of that, struct and union types that are only referred to through pointers from members of other struct or union types get pruned and replaced with BTF_KIND_FWD, as though those types were only present in the input as forward declarations.

This option substantially shrinks the BTF info, but you lose a lot of detailed type info in the process. It's mainly useful for the BPF target, to minimize the size of the object and drop BTF info that isn't immediately relevant to the BPF program loading process ๐ŸŽ€

This option is enabled by default for the BPF target when generating BTF info.

  • -gctf, -gctflevel โ€” ๐Ÿงฉ Requests CTF debug info, and level tells GCC how much CTF info to produce. If you use -gctf without a value for level, the default level is 2.

CTF debug info can be generated along with DWARF debug info when both formats are enabled explicitly through their respective command line options.

  • Level 0 produces no CTF debug info at all, so -gctf0 negates -gctf.
  • Level 1 produces CTF info for tracebacks only. This includes callsite info, but no type info.
  • Level 2 produces type info for entities (functions, data objects etc.) at file-scope or global-scope only.
  • -gvms โ€” ๐Ÿ–ฅ๏ธ Produces debugging info in Alpha/VMS debug format (if supported). This is the format DEBUG uses on Alpha/VMS systems.
  • -gcodeview โ€” ๐ŸชŸ Produces debugging info in CodeView debug format (if supported). This is the format Microsoft Visual C++ uses on Windows.
  • -glevel, -ggdblevel, -gvmslevel โ€” ๐ŸŽš๏ธ Requests debugging info and uses level to decide how much. The default level is 2.
    • Level 0 produces no debug info at all, so -g0 negates -g.
    • Level 1 produces minimal info, enough for backtraces in parts of the program you don't plan to debug. This includes descriptions of functions and external variables, plus line number tables, but no info about local variables.
    • Level 3 includes extra info, like all the macro definitions present in the program. Some debuggers support macro expansion when you use -g3.

If you use multiple -g options, with or without level numbers, the last one is the one that takes effect ๐Ÿ‘‘

-gdwarf doesn't accept a concatenated debug level, to avoid confusion with -gdwarf-level. Instead, use an additional -glevel option to change the debug level for DWARF.

  • -fno-eliminate-unused-debug-symbols โ€” ๐Ÿ™… By default, no debug info is produced for symbols that aren't actually used. Use this option if you want debug info for all symbols.
  • -femit-class-debug-always โ€” ๐Ÿ“š Instead of emitting debugging info for a C++ class in only one object file, this emits it in all object files that use the class. Only use this with debuggers that can't handle how GCC normally emits class debug info, because it can make the debug info up to a factor of two bigger ๐Ÿ˜ฌ
  • -fno-merge-debug-strings โ€” ๐Ÿงต Tells the linker not to merge strings in the debug info that are identical across different object files. Not every assembler or linker supports merging. Merging shrinks the debug info in the output file but makes link processing take longer. Merging is enabled by default.
  • -fdebug-prefix-map=old=new โ€” ๐Ÿ—บ๏ธ When compiling files in directory old, records debugging info as if the files lived in directory new instead. This is handy for swapping a build-time path for an install-time path in the debug info. You can also use it to change an absolute path to a relative one by using . for new. It can give more reproducible, location-independent builds, but you may need an extra command to tell GDB where to find the source files. See also -ffile-prefix-map and -fcanon-prefix-map.
  • -fvar-tracking โ€” ๐Ÿ” Runs the variable tracking pass, which works out where variables are stored at each position in the code. Better debug info gets generated (if the format supports it).

It's enabled by default when you compile with optimization (-Os, -O, -O2, ...), debugging info (-g), and a debug info format that supports it.

  • -fvar-tracking-assignments โ€” ๐Ÿช„ Annotates assignments to user variables early in compilation and tries to carry those annotations all the way to the end, to improve debug info while optimizing. Using -gdwarf-4 alongside it is recommended.

It can be enabled even when var-tracking is disabled. In that case the annotations are created and maintained, but discarded at the end. By default this flag is enabled together with -fvar-tracking, except when selective scheduling is enabled.

  • -gsplit-dwarf โ€” ๐Ÿ“ฆ If DWARF debug info is enabled, this separates as much of it as possible into a separate output file with the extension .dwo. This lets your build system avoid linking files with debug info. To be useful, you need a debugger that can read .dwo files.
  • -gdwarf32, -gdwarf64 โ€” ๐Ÿ”ข If DWARF debug info is enabled, -gdwarf32 selects the 32-bit DWARF format and -gdwarf64 selects the 64-bit DWARF format. The default is target specific, though on most targets it's -gdwarf32. The 32-bit format is smaller but can't hold more than 2GiB of debug info in any of the DWARF debug info sections. The 64-bit format allows larger debug info, but not every consumer supports it well yet.
  • -gdescribe-dies โ€” ๐Ÿ“ Adds description attributes to some DWARF DIEs that have no name attribute, like artificial variables, external references and call site parameter DIEs.
  • -gpubnames โ€” Okay so this one makes DWARF ".debug_pubnames" and ".debug_pubtypes" sections. ๐Ÿ’œ
  • -ggnu-pubnames โ€” This makes ".debug_pubnames" and ".debug_pubtypes" sections too, but in a format that's ready to be turned into a GDB index. Heads up, it's only useful if your linker can produce GDB index version 7. ๐Ÿ”ฅ
  • -fdebug-types-section โ€” When you're on DWARF Version 4 or higher, type DIEs can get their own ".debug_types" section instead of living inside ".debug_info". It's more efficient in a separate comdat section, since the linker can then remove duplicates โœจ. BUT not every DWARF consumer supports ".debug_types" yet, and on some objects it makes the debug info bigger instead of smaller ๐Ÿ’€.
  • -grecord-gcc-switches, -gno-record-gcc-switches โ€” This one tacks the command-line options that might affect code generation onto the DW_AT_producer attribute in your DWARF debug info ๐Ÿงš. The options get joined with spaces, separating them from each other and from the compiler version. It's on by default. See also -frecord-gcc-switches for another way to stash compiler options in the object file.
  • -gstrict-dwarf โ€” Bans using extensions from a later DWARF standard version than the one you picked with -gdwarf-version ๐Ÿ™…โ€โ™€๏ธ. On most targets, using non-conflicting extensions from later versions is actually allowed.
  • -gno-strict-dwarf โ€” Lets you use extensions from a later DWARF standard version than the one picked with -gdwarf-version ๐Ÿฆ‹.
  • -gas-loc-support โ€” Tells the compiler that the assembler supports ".loc" directives, so it can use them to have the assembler generate DWARF2+ line number tables ๐ŸŒธ. This is usually the move, because assembler-generated line-number tables are a lot more compact than the ones the compiler makes itself. This option turns on by default if, at GCC configure time, the assembler was found to support those directives.
  • -gno-as-loc-support โ€” Forces GCC to generate DWARF2+ line number tables internally, if those tables are going to be generated at all ๐Ÿ™ˆ.
  • -gas-locview-support โ€” Tells the compiler that the assembler supports "view" assignment and reset assertion checking in ".loc" directives ๐Ÿ‘‘. This turns on by default if the assembler was found to support them at GCC configure time.
  • -gno-as-locview-support โ€” Forces GCC to assign view numbers internally, if -gvariable-location-views is explicitly requested ๐Ÿ’….
  • -gcolumn-info, -gno-column-info โ€” Puts location column information into DWARF debug info, not just the file and line ๐ŸŽ€. Enabled by default.
  • -gstatement-frontiers, -gno-statement-frontiers โ€” This makes GCC drop markers into the internal representation at the start of statements, and keeps them roughly in place through the whole compilation โญ. Those markers then guide the output of "is_stmt" markers in the line number table. It's enabled by default when you compile with optimization (-Os, -O1, -O2, ...) and output DWARF 2 debug info at the normal level ๐Ÿ’–.
  • -gvariable-location-views, -gvariable-location-views=incompat5, -gno-variable-location-views โ€” Augments variable location lists with progressive view numbers implied from the line number table ๐ŸŒˆ. This lets debug info consumers inspect state at certain points in the program, even when no instructions tied to the corresponding source locations are there. If the assembler doesn't support view numbers in line number tables, this makes the compiler emit the line number table, which makes them somewhat less compact ๐Ÿซถ. The augmented line number tables and location lists are fully backward-compatible, so consumers that don't know about these augmentations can still read them, but they won't get any benefit from them either.

This is enabled by default when outputting DWARF 2 debug info at the normal level, as long as there's assembler support, -fvar-tracking-assignments is enabled, and -gstrict-dwarf is not. When assembler support isn't available, it may still be enabled, but that forces GCC to output internal line number tables. And if -ginternal-reset-location-views isn't enabled, that will most certainly lead to silently mismatching location views ๐Ÿ˜ญ.

There's a proposed representation for view numbers that is not backward compatible with the location list format introduced in DWARF 5. You can turn it on with -gvariable-location-views=incompat5. This option may be removed in the future, and it's only provided as a reference implementation of the proposed representation. Debug info consumers aren't expected to support this extended format, and they'd be unable to decode location lists that use it.

โš ๏ธ Heads up bestie: -gvariable-location-views=incompat5 is NOT backward compatible with the DWARF 5 location list format, and debug info consumers aren't expected to support it. They'd literally be unable to decode the location lists. Only reach for it if you're experimenting with the reference implementation ๐Ÿ’€.
  • -ginternal-reset-location-views, -gno-internal-reset-location-views โ€” Tries to figure out which location views can be left out of location view lists ๐Ÿง. This needs the compiler to have very accurate insn length estimates, which isn't always the case. It may also silently generate incorrect view lists when you're using an assembler that doesn't support location view lists. The GNU assembler will flag any such error as a "view number mismatch". This is only enabled on ports that define a reliable estimation function.
  • -ginline-points, -gno-inline-points โ€” Generates extended debug info for inlined functions ๐Ÿฆ‹. Location view tracking markers get inserted at inlined entry points, so address and view numbers can be computed and output in debug info. This can be enabled independently of location views, in which case the view numbers won't be output. It can only be enabled along with statement frontiers, and it's only enabled by default if location views are enabled.
  • -gz[=type] โ€” Produces compressed debug sections in DWARF format, if that's supported ๐Ÿ—œ๏ธ. If type isn't given, the default depends on what the assembler and linker are capable of. type can be one of none (don't compress debug sections) or zlib (use zlib compression in ELF gABI format). If the linker doesn't support writing compressed debug sections, the option gets rejected. Otherwise, if the assembler doesn't support them, -gz is silently ignored when producing object files.
  • -femit-struct-debug-baseonly โ€” Only emits debug info for struct-like types when the base name of the compilation source file matches the base name of the file where the struct is defined ๐ŸŽฏ. This substantially reduces the size of debug info, but there's a significant potential loss in type information for the debugger. See -femit-struct-debug-reduced for a less aggressive option, and -femit-struct-debug-detailed for more detailed control. This option works only with DWARF debug output.
  • -femit-struct-debug-reduced โ€” Only emits debug info for struct-like types when the base name of the compilation source file matches the base name of the file where the type is defined, unless the struct is a template or is defined in a system header ๐ŸŒท. This significantly reduces the size of debug info, with some potential loss in type information for the debugger. See -femit-struct-debug-baseonly for a more aggressive option, and -femit-struct-debug-detailed for more detailed control. This option works only with DWARF debug output.
  • -femit-struct-debug-detailed[=spec-list] โ€” Lets you specify which struct-like types the compiler generates debug info for ๐Ÿงฉ. The goal is to cut down on duplicate struct debug info between different object files in the same program. This is the detailed version of -femit-struct-debug-reduced and -femit-struct-debug-baseonly, and it covers most needs.

A specification uses the syntax [dir:|ind:][ord:|gen:](any|sys|base|none).

The optional first word limits the specification to structs that are used directly (dir:) or used indirectly (ind:). A struct type is used directly when it's the type of a variable or member. Indirect uses come through pointers to structs. In other words, when use of an incomplete struct is valid, the use is indirect. An example is struct one direct; struct two * indirect;.

The optional second word limits the specification to ordinary structs (ord:) or generic structs (gen:). Generic structs are a bit complicated to explain ๐Ÿ™ƒ. For C++, these are non-explicit specializations of template classes, or non-template classes within the above. Other programming languages have generics, but -femit-struct-debug-detailed doesn't implement them yet.

The third word specifies the source files for those structs that the compiler should emit debug info for. The values none and any have their normal meaning. The value base means the base of the name of the file where the type declaration appears must match the base of the name of the main compilation file. In practice, this means that when compiling foo.c, debug info is generated for types declared in that file and foo.h, but not other header files. The value sys means those types satisfying base, or declared in system or compiler headers.

You may need to experiment to find the best settings for your application ๐Ÿงช.

The default is -femit-struct-debug-detailed=all. This option works only with DWARF debug output.

  • -fno-dwarf2-cfi-asm โ€” Emits DWARF unwind info as a compiler-generated ".eh_frame" section, instead of using GAS ".cfi_*" directives ๐Ÿ› ๏ธ.
  • -fno-eliminate-unused-debug-types โ€” Normally, when producing DWARF output, GCC skips debug symbol output for types that are nowhere used in the source file being compiled ๐Ÿงน. Sometimes you want debug info for all types declared in a compilation unit, whether or not they're actually used there. For example, you might want to cast a value to a type that's declared but not actually used in your program, right in the debugger. Honestly though, this usually just results in a significant amount of wasted space ๐Ÿ˜….

๐ŸŒธ Options That Control Optimization ๐ŸŒธ

These options control various sorts of optimizations. โœจ

Okay so with no optimization flag at all, the compiler's whole vibe is cutting compile time and making debugging act right ๐Ÿ’… Statements stay independent, which means if you pause at a breakpoint between statements, you can give any variable a new value or jump the program counter to any other statement in the function and get exactly what the source code promised โœจ

Turning on optimization flags is a total glow-up for performance and/or code size ๐Ÿฆ‹ but it costs you compilation time and maybe your ability to debug the program ๐Ÿฅฒ

The compiler optimizes based on what it knows about your program ๐Ÿง  Compiling multiple files at once into a single output file lets it use info from all of those files while it works on each one, which is so iconic ๐Ÿ‘‘

Not every optimization has its own flag, btw ๐Ÿ™ˆ Only the ones that do have a flag get listed in this section.

Most optimizations are fully switched off at -O0, or whenever no -O level is set on the command line, even if you add individual optimization flags ๐Ÿ’€ And -Og suppresses a bunch of optimization passes too, so keep that in mind.

Which exact set of optimizations gets enabled at each level can change depending on the target and how GCC was configured ๐ŸŒˆ If you want the real list for your setup, invoke GCC with -Q --help=optimizers and it'll spill every enabled optimization at each level ๐Ÿซถ


โœจ -O levels โœจ

  • -O, -O1 โ€” Optimize! This takes a bit more time to compile, and a lot more memory for a big function ๐Ÿง 

With -O, the compiler tries to shrink code size and speed up execution, but it skips any optimization that eats a ton of compile time ๐Ÿ’…

-O is the recommended level for large machine-generated code, because it's the sensible middle ground between compile time and memory use. Higher levels run optimizations with more algorithmic complexity than -O does ๐Ÿซถ

-O turns on these optimization flags:

-fauto-inc-dec -fbranch-count-reg -fcombine-stack-adjustments
-fcompare-elim -fcprop-registers -fdce -fdefer-pop -fdelayed-branch
-fdse -fforward-propagate -fguess-branch-probability -fif-conversion
-fif-conversion2 -finline-functions-called-once -fipa-modref
-fipa-profile -fipa-pure-const -fipa-reference
-fipa-reference-addressable -fivopts -fmerge-constants
-fmove-loop-invariants -fmove-loop-stores -fomit-frame-pointer
-freorder-blocks -fshrink-wrap -fshrink-wrap-separate
-fsplit-wide-types -fssa-backprop -fssa-phiopt -ftree-bit-ccp
-ftree-ccp -ftree-ch -ftree-coalesce-vars -ftree-copy-prop
-ftree-dce -ftree-dominator-opts -ftree-dse -ftree-forwprop
-ftree-fre -ftree-phiprop -ftree-pta -ftree-scev-cprop -ftree-sink
-ftree-slsr -ftree-sra -ftree-ter -funit-at-a-time
  • -O2 โ€” Optimize even more ๐Ÿ”ฅ GCC runs nearly every supported optimization that doesn't involve a space-speed tradeoff. Compared to -O, this one increases both compilation time and the performance of the generated code ๐Ÿ“ˆ

-O2 turns on every optimization flag that -O1 does. It also turns on these:

-falign-functions  -falign-jumps -falign-labels  -falign-loops
-fcaller-saves -fcode-hoisting -fcrossjumping -fcse-follow-jumps
-fcse-skip-blocks -fdelete-null-pointer-checks -fdevirtualize
-fdevirtualize-speculatively -fexpensive-optimizations
-ffinite-loops -fgcse  -fgcse-lm -fhoist-adjacent-loads
-finline-functions -finline-small-functions -findirect-inlining
-fipa-bit-cp  -fipa-cp  -fipa-icf -fipa-ra  -fipa-sra  -fipa-vrp
-fisolate-erroneous-paths-dereference -flra-remat -foptimize-crc
-foptimize-sibling-calls -foptimize-strlen -fpartial-inlining
-fpeephole2 -freorder-blocks-algorithm=stc
-freorder-blocks-and-partition  -freorder-functions
-frerun-cse-after-loop -fschedule-insns  -fschedule-insns2
-fsched-interblock  -fsched-spec -fstore-merging -fstrict-aliasing
-fthread-jumps -ftree-builtin-call-dce -ftree-loop-vectorize
-ftree-pre -ftree-slp-vectorize -ftree-switch-conversion
-ftree-tail-merge -ftree-vrp -fvect-cost-model=very-cheap

Please note the warning under -fgcse about invoking -O2 on programs that use computed gotos. โš ๏ธ

NOTE: In Ubuntu 8.10 and later versions, -D_FORTIFY_SOURCE=2 is set by default, and in Ubuntu 24.04 and later versions, -D_FORTIFY_SOURCE=3 is set by default. Either one is activated when -O is set to 2 or higher ๐Ÿ›ก๏ธ It turns on extra compile-time and run-time checks for several libc functions. To turn it off, specify either -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0.

NOTE: In Debian 13 and Ubuntu 24.04 and later versions, -D_TIME_BITS=64 together with -D_FILE_OFFSET_BITS=64 is set by default on the 32bit architectures armel, armhf, hppa, m68k, mips, mipsel, powerpc and sh4 ๐ŸŒธ

  • -O3 โ€” Optimize yet more ๐Ÿš€ -O3 turns on everything -O2 does, plus these:
-fgcse-after-reload -fipa-cp-clone -floop-interchange
-floop-unroll-and-jam -fpeel-loops -fpredictive-commoning
-fsplit-loops -fsplit-paths -ftree-loop-distribution
-ftree-partial-pre -funswitch-loops -fvect-cost-model=dynamic
-fversion-loops-for-strides
  • -O0 โ€” Reduce compilation time and make debugging produce the expected results ๐ŸŽ€ This is the default, bestie.

At -O0, GCC completely disables most optimization passes. They don't run even if you explicitly enable them on the command line, or if -Q --help=optimizers lists them as enabled by default ๐Ÿ™…โ€โ™€๏ธ A lot of the optimizations GCC does depend on code analysis or canonicalization passes that only -O turns on, so running individual passes in isolation wouldn't be useful anyway.

  • -Os โ€” Optimize for size ๐Ÿงธ -Os enables all the -O2 optimizations except the ones that often make code bigger:
-falign-functions  -falign-jumps -falign-labels  -falign-loops
-fprefetch-loop-arrays  -freorder-blocks-algorithm=stc

It also enables -finline-functions, makes the compiler tune for code size instead of execution speed, and runs extra optimizations designed to shrink code ๐Ÿ“‰

  • -Ofast โ€” Disregard strict standards compliance ๐Ÿ˜ค -Ofast enables all -O3 optimizations, plus optimizations that aren't valid for every standard-compliant program. It turns on -ffast-math, -fallow-store-data-races, and the Fortran-specific -fstack-arrays (unless -fmax-stack-var-size is specified), and -fno-protect-parens. It turns off -fsemantic-interposition ๐Ÿ’€
  • -Og โ€” Optimize the debugging experience ๐Ÿ” -Og should be your go-to level for the standard edit-compile-debug cycle. It offers a reasonable amount of optimization while keeping compilation fast and debugging smooth ๐Ÿซถ It's a better pick than -O0 for debuggable code, because some compiler passes that collect debug information are disabled at -O0.

Like -O0, -Og completely disables a number of optimization passes, so the individual options that control them have no effect. Otherwise, -Og enables all the -O1 optimization flags except the ones that can get in the way of debugging:

-fbranch-count-reg  -fdelayed-branch -fdse  -fif-conversion
-fif-conversion2 -finline-functions-called-once
-fmove-loop-invariants  -fmove-loop-stores  -fssa-phiopt
-ftree-bit-ccp  -ftree-dse  -ftree-pta  -ftree-sra
  • -Oz โ€” Optimize aggressively for size rather than speed ๐Ÿฃ This may increase the number of instructions executed if those instructions need fewer bytes to encode. -Oz behaves similarly to -Os, including enabling most -O2 optimizations.

If you use multiple -O options, with or without level numbers, the last one is the one that takes effect ๐Ÿ™ƒ


โœจ -f flags โœจ

Options of the form -fflag are machine-independent flags ๐Ÿฆ‹ Most flags have both a positive and a negative form, and the negative form of -ffoo is -fno-foo. The table below only lists one of the forms, the one you'll typically use. You can figure out the other one by either removing no- or adding it ๐Ÿ’œ

The following options control specific optimizations. They're either activated by -O options or related to ones that are. You can use these flags in the rare cases when "fine-tuning" the optimizations to be performed is needed ๐ŸŽฏ

  • -fno-defer-pop โ€” For machines that have to pop arguments after a function call, this always pops the arguments as soon as each function returns ๐Ÿงน At -O1 and higher, -fdefer-pop is the default, which lets the compiler let arguments pile up on the stack across several function calls and pop them all at once.
  • -fforward-propagate โ€” Performs a forward propagation pass on RTL โœจ The pass tries to combine two instructions and checks whether the result can be simplified. If loop unrolling is active, two passes run, and the second one is scheduled after loop unrolling.

This option is enabled by default at optimization levels -O1, -O2, -O3, -Os.

  • -favoid-store-forwarding, -fno-avoid-store-forwarding โ€” Many CPUs stall for many cycles when a load partially depends on previous smaller stores ๐Ÿ˜ฉ This pass tries to detect those cases and dodge the penalty by changing the order of the load and store, then fixing up the loaded value.

Disabled by default.

  • -ffp-contract=style โ€” -ffp-contract=off disables floating-point expression contraction. -ffp-contract=fast enables it, such as forming fused multiply-add operations when the target has native support for them. -ffp-contract=on enables contraction if the language standard allows it. This is implemented for C and C++, where it enables contraction within one expression, but not across different statements ๐Ÿงฎ

The default is -ffp-contract=off for C in a standards compliant mode (-std=c11 or similar), and -ffp-contract=fast otherwise.

  • -fomit-frame-pointer โ€” Omits the frame pointer in functions that don't need one ๐ŸŒ™ This skips the instructions that save, set up, and restore the frame pointer, and on many targets it also frees up an extra register.

On some targets this flag does nothing, because the standard calling sequence always uses a frame pointer, so it can't be omitted ๐Ÿ™ˆ

Note that -fno-omit-frame-pointer doesn't guarantee the frame pointer is used in all functions. Several targets always omit the frame pointer in leaf functions.

Enabled by default at -O1 and higher. ๐Ÿ’–

  • -foptimize-crc โ€” okay so this one is ๐Ÿ”ฅ. GCC hunts down loops that do CRC calculations (that polynomial long division thing) and swaps them for a much faster version. It can spot 8, 16, 32, and 64 bit CRCs with a constant polynomial that doesn't include the leading 1 bit, in both bit-forward and bit-reversed flavours. If your target has a CRC instruction and the polynomial in your code matches the one that instruction uses, GCC generates that instruction. If not, but the target has a carry-less-multiplication instruction, it generates CRC with that. Otherwise, it falls back to a table-based CRC. ๐Ÿ’…

Enabled by default at -O2 and higher. โœจ

  • -foptimize-sibling-calls โ€” optimizes sibling and tail recursive calls. Main character energy ๐Ÿ‘‘

Enabled at levels -O2, -O3, -Os. ๐ŸŒธ

  • -foptimize-strlen โ€” makes various standard C string functions (like "strlen", "strchr" or "strcpy") and their "_FORTIFY_SOURCE" counterparts run faster by swapping in quicker alternatives. ๐Ÿงš

Enabled at levels -O2, -O3. ๐Ÿซถ

  • -finline-stringops[=fn] โ€” expands memory and string operations inline (for now, only "memset"), even when the length is variable or big enough that it'd normally need a loop. This is most useful alongside -ffreestanding and -fno-builtin. ๐Ÿ“

Sometimes it can use known alignment and length multipliers to make nicer code, but even then it might be less efficient than optimized runtime implementations. It can also bloat your code size so much that a slower but shared implementation actually runs faster, thanks to better use of code caches. ๐Ÿ˜ญ This option is disabled by default.

  • -fno-inline โ€” don't expand any functions inline, except the ones marked with the "always_inline" attribute. This is the default when you're not optimizing. ๐Ÿ™ˆ

You can exempt individual functions from inlining by marking them with the "noinline" attribute. ๐Ÿฆ‹

  • -finline-small-functions โ€” integrates functions into their callers when their body is smaller than the code for the function call itself, so the overall program gets smaller. GCC heuristically decides which functions are simple enough to be worth it. This applies to all functions, even ones not declared inline. โœจ

Enabled at levels -O2, -O3, -Os. ๐Ÿ’–

  • -findirect-inlining โ€” also inlines indirect calls that become known at compile time thanks to earlier inlining. This only has an effect when inlining is turned on by -finline-functions or -finline-small-functions. ๐Ÿ”ฅ

Enabled at levels -O2, -O3, -Os. ๐ŸŒˆ

  • -finline-functions โ€” considers all functions for inlining, even if they aren't declared inline. GCC heuristically decides which ones are worth integrating. ๐Ÿงš

If every call to a given function gets integrated and the function is declared "static", then it's normally not output as assembler code on its own. Iconic. ๐Ÿ‘‘

Enabled at levels -O2, -O3, -Os. Also enabled by -fprofile-use and -fauto-profile. ๐Ÿ’…

  • -finline-functions-called-once โ€” considers all "static" functions that get called once for inlining into their caller, even if they aren't marked "inline". If the call gets integrated, the function isn't output as assembler code by itself. ๐ŸŽ€

Enabled at levels -O1, -O2, -O3 and -Os, but not -Og. ๐Ÿ™ˆ

  • -fearly-inlining โ€” inlines functions marked by "always_inline", plus functions whose body seems smaller than the function call overhead, early, before the -fprofile-generate instrumentation and the real inlining pass. Doing this makes profiling way cheaper and usually makes inlining faster for programs with big chains of nested wrapper functions. ๐Ÿฆ‹

Enabled by default. โœจ

  • -fipa-sra โ€” performs interprocedural scalar replacement of aggregates, removes unused parameters, and swaps parameters passed by reference for parameters passed by value. ๐ŸŒธ

Enabled at levels -O2, -O3 and -Os. ๐Ÿ’œ

  • -finline-limit=n โ€” by default GCC limits the size of functions that can be inlined. This flag gives you coarse control over that limit. n is the size of functions that can be inlined, measured in pseudo instructions. ๐Ÿงš

Inlining is actually controlled by a bunch of parameters, which you can set one at a time with --param name=value. The -finline-limit=n option sets some of them like this:

  • max-inline-insns-single โ€” is set to n/2.
  • max-inline-insns-auto โ€” is set to n/2.

See below for documentation on the individual parameters controlling inlining and for their defaults. ๐Ÿซถ

Note: there may be no value of -finline-limit that results in default behavior. ๐Ÿ’€
Note: "pseudo instruction" here is just an abstract measurement of a function's size. It in no way counts assembly instructions, so its exact meaning might change from one release to another. ๐Ÿ™ˆ
  • -fno-keep-inline-dllexport โ€” a more fine-grained version of -fkeep-inline-functions, which only applies to functions declared with the "dllexport" attribute or declspec. ๐ŸŒˆ
  • -fkeep-inline-functions โ€” in C, emits "static" functions declared "inline" into the object file, even if they've been inlined into all of their callers. This switch doesn't affect functions using the "extern inline" extension in GNU C90. In C++, it emits any and all inline functions into the object file. ๐Ÿ’–
  • -fkeep-static-functions โ€” emits "static" functions into the object file, even if they're never used. ๐Ÿ“
  • -fkeep-static-consts โ€” emits variables declared "static const" when optimization isn't turned on, even if they aren't referenced. ๐Ÿ’œ

GCC enables this one by default. If you want the compiler to check whether a variable is referenced, regardless of optimization, use -fno-keep-static-consts. ๐Ÿ‘€

  • -fmerge-constants โ€” tries to merge identical constants (string constants and floating-point constants) across compilation units. ๐Ÿซถ

This is the default for optimized compilation if the assembler and linker support it. Use -fno-merge-constants to stop this from happening. Enabled at levels -O1, -O2, -O3, -Os. โœจ

  • -fmerge-all-constants โ€” tries to merge identical constants and identical variables. ๐Ÿ”ฅ

This option implies -fmerge-constants. On top of that, it also considers things like constant initialized arrays, or initialized constant variables with integral or floating-point types. Careful, bestie! C and C++ require each variable, including multiple instances of the same variable in recursive calls, to have distinct locations, so using this option results in non-conforming behavior. ๐Ÿ’€

  • -fmodulo-sched โ€” performs swing modulo scheduling right before the first scheduling pass. It looks at innermost loops and reorders their instructions by overlapping different iterations. ๐Ÿงš
  • -fmodulo-sched-allow-regmoves โ€” performs more aggressive SMS-based modulo scheduling with register moves allowed. Setting this flag deletes certain anti-dependence edges, which triggers the generation of reg-moves based on life-range analysis. ๐Ÿฆ‹

This option only works when -fmodulo-sched is also enabled. ๐ŸŒธ

  • -fno-branch-count-reg โ€” disables the optimization pass that looks for chances to use "decrement and branch" instructions on a count register, instead of instruction sequences that decrement a register, compare it against zero, and then branch based on the result. This only matters on architectures that support those instructions, which include x86, PowerPC, IA-64 and S/390. ๐ŸŽ€

Heads up: -fno-branch-count-reg doesn't remove decrement and branch instructions that other optimization passes already put into the instruction stream. ๐Ÿ™ˆ

The default is -fbranch-count-reg at -O1 and higher, except for -Og. ๐Ÿ’…

  • -fno-function-cse โ€” doesn't put function addresses in registers. Instead, every instruction that calls a constant function contains the function's address explicitly. ๐ŸŒˆ

This gives you less efficient code, but some strange hacks that alter the assembler output might get confused by the optimizations performed when this option is not used. ๐Ÿ˜ญ

The default is -ffunction-cse

  • -fno-zero-initialized-in-bss โ€” if the target supports a BSS section, GCC by default puts variables initialized to zero into BSS, which can save space in the resulting code. โœจ

This option turns that behavior off, because some programs explicitly rely on variables going to the data section, e.g. so the resulting executable can find the beginning of that section and/or make assumptions based on it. ๐Ÿซถ

The default is -fzero-initialized-in-bss except in Ada. ๐Ÿ’œ

  • -fthread-jumps โ€” performs optimizations that check whether a jump branches to a location where another comparison, subsumed by the first, is found. If so, the first branch gets redirected to either the destination of the second branch or a point right after it, depending on whether the condition is known to be true or false. ๐Ÿงš

Enabled at levels -O1, -O2, -O3, -Os. ๐ŸŒธ

  • -fsplit-wide-types โ€” when using a type that occupies multiple registers, like "long long" on a 32-bit system, GCC splits the registers apart and allocates them independently. This usually generates better code for those types, but it may make debugging harder. ๐Ÿ”ฅ

Enabled at levels -O1, -O2, -O3, -Os. ๐Ÿ’…

  • -fsplit-wide-types-early โ€” fully splits wide types early, instead of very late. This option does nothing unless -fsplit-wide-types is turned on. ๐ŸŽ€

This is the default on some targets. ๐Ÿ‘‘

  • -fcse-follow-jumps โ€” during common subexpression elimination (CSE), scans through jump instructions when the jump's target isn't reached by any other path. For example, when CSE meets an "if" statement with an "else" clause, it follows the jump when the condition tested is false. ๐Ÿ‘€

Enabled at levels -O2, -O3, -Os. ๐Ÿ’–

  • -fcse-skip-blocks โ€” similar to -fcse-follow-jumps, but this makes CSE follow jumps that conditionally skip over blocks. When CSE meets a simple "if" statement with no "else" clause, -fcse-skip-blocks makes CSE follow the jump around the body of the "if". ๐Ÿฆ‹

Okay so this next batch is all about the -f flags that squeeze extra juice out of your code ๐Ÿงƒโœจ Keep reading, bestie, it's giving optimization main character energy ๐Ÿ’…

Enabled at levels -O2, -O3, -Os. ๐ŸŒธ

  • -frerun-cse-after-loop โ€” okay so this one re-runs common subexpression elimination after the loop optimizations are done, because why not do it twice, periodt ๐Ÿ”

Enabled at levels -O2, -O3, -Os. ๐Ÿ’–

  • -fgcse โ€” this one runs a global common subexpression elimination pass ๐Ÿ”ฅ It also handles global constant and copy propagation too, so it's pulling double duty.
Note: if you're compiling a program that uses computed gotos (a GCC extension, not standard C, so be aware ๐Ÿ‘€), you might get better run-time performance by turning off this pass. Just add -fno-gcse to your command line.

Enabled at levels -O2, -O3, -Os. โœจ

  • -fgcse-lm โ€” when this one is on, global common subexpression elimination tries to move loads that only get killed by stores into themselves ๐Ÿงš That means a loop with a load/store sequence can be rearranged so the load happens outside the loop, and a copy/store happens inside it.

Enabled by default when -fgcse is enabled. ๐Ÿฆ‹

  • -fgcse-sm โ€” this one turns on a store motion pass after global common subexpression elimination ๐ŸŽ€ It tries to pull stores out of loops. Team it up with -fgcse-lm and loops with a load/store sequence can become a load before the loop and a store after it, iykyk ๐Ÿซถ

Not enabled at any optimization level. ๐Ÿ™ˆ

  • -fgcse-las โ€” with this one on, the global common subexpression elimination pass gets rid of redundant loads that come right after stores to the same memory location. It catches both partial and full redundancies, so it's thorough af ๐Ÿ’…

Not enabled at any optimization level. ๐Ÿ™ˆ

  • -fgcse-after-reload โ€” when enabled, this one runs a redundant load elimination pass after reload ๐Ÿงน Its whole purpose is to clean up redundant spilling, very much a tidy-up girl.

Enabled by -O3, -fprofile-use and -fauto-profile. โญ

  • -faggressive-loop-optimizations โ€” this one tells the loop optimizer to use language constraints to work out bounds for the number of iterations of a loop ๐Ÿง  It assumes your loop code doesn't invoke undefined behavior, like causing signed integer overflows or out-of-bound array accesses. Those bounds then guide loop unrolling, loop peeling and loop exit test optimizations.

This option is enabled by default. ๐Ÿ‘‘

  • -funconstrained-commons โ€” this tells the compiler that variables declared in common blocks (for example in Fortran) might later be overridden with longer trailing arrays ๐ŸŒˆ Because of that, it stops certain optimizations that depend on knowing the array bounds. Bit of a trade-off, bestie.
  • -fcrossjumping โ€” this performs the cross-jumping transformation, which unifies equivalent code and saves code size ๐Ÿ“ฆ Heads up though: the resulting code may or may not perform better than without cross-jumping, so it's a vibe check, not a guarantee ๐Ÿคทโ€โ™€๏ธ

Enabled at levels -O2, -O3, -Os. ๐Ÿ’œ

  • -fauto-inc-dec โ€” this combines increments or decrements of addresses with memory accesses ๐Ÿ“ This pass is always skipped on architectures that don't have instructions to support it.

Enabled by default at -O1 and higher on architectures that support this. โœจ

  • -fdce โ€” performs dead code elimination (DCE) on RTL ๐Ÿ—‘๏ธ Bye bye, useless code.

Enabled by default at -O1 and higher. ๐ŸŒธ

  • -fdse โ€” performs dead store elimination (DSE) on RTL ๐Ÿ’€ Stores that nobody reads? Gone.

Enabled by default at -O1 and higher. ๐ŸŒธ

  • -fif-conversion โ€” this tries to transform conditional jumps into branch-less equivalents ๐Ÿช„ It includes conditional moves, min, max, set flags and abs instructions, plus some tricks you can do with standard arithmetic. Whether conditional execution gets used on chips that support it is controlled by -fif-conversion2.

Enabled at levels -O1, -O2, -O3, -Os, but not with -Og. ๐ŸŽ€

  • -fif-conversion2 โ€” uses conditional execution (where available) to transform conditional jumps into branch-less equivalents ๐Ÿ’ซ

Enabled at levels -O1, -O2, -O3, -Os, but not with -Og. ๐ŸŽ€

  • -fdeclone-ctor-dtor โ€” okay this one is a whole story ๐Ÿ“– The C++ ABI needs multiple entry points for constructors and destructors: one for a base subobject, one for a complete object, and one for a virtual destructor that calls operator delete afterwards. For a hierarchy with virtual bases, the base and complete variants are clones, which means two copies of the function. With this option, those two variants become thunks that call a common implementation, so you only keep one real copy ๐Ÿงšโ€โ™€๏ธ

Enabled by -Os. ๐Ÿ’…

  • -fdelete-null-pointer-checks โ€” this one assumes programs can't safely dereference null pointers, and that no code or data element lives at address zero ๐Ÿ™…โ€โ™€๏ธ It enables simple constant folding optimizations at all optimization levels. Other passes in GCC use this flag to control global dataflow analyses that get rid of useless null pointer checks. Those analyses assume that a memory access to address zero always results in a trap, so if a pointer gets checked after it's already been dereferenced, it can't be null.
Careful, bestie: in some environments this assumption isn't true. If your program depends on that behavior, use -fno-delete-null-pointer-checks to turn this optimization off ๐Ÿšจ

This option is enabled by default on most targets. On AVR and MSP430, this option is completely disabled. ๐Ÿ˜ญ

Passes that use the dataflow information are enabled independently at different optimization levels. ๐ŸŒˆ

  • -fdevirtualize โ€” tries to convert calls to virtual functions into direct calls ๐ŸŽฏ This happens both within a procedure and interprocedurally, as part of indirect inlining (-findirect-inlining) and interprocedural constant propagation (-fipa-cp).

Enabled at levels -O2, -O3, -Os. โœจ

  • -fdevirtualize-speculatively โ€” this one tries to convert calls to virtual functions into speculative direct calls ๐Ÿ”ฎ Based on the analysis of the type inheritance graph, it figures out the set of likely targets for a given call. If that set is small, ideally of size 1, the call becomes a conditional that picks between a direct call and an indirect call. Those speculative calls unlock more optimizations, like inlining. If they seem useless after further optimization, they get converted back into their original form.
  • -fdevirtualize-at-ltrans โ€” this streams extra information needed for aggressive devirtualization when the link-time optimizer runs in local transformation mode ๐Ÿš› It enables more devirtualization, but it significantly increases the size of the streamed data. For that reason it's disabled by default.
  • -fexpensive-optimizations โ€” performs a number of minor optimizations that are relatively expensive ๐Ÿ’ธ

Enabled at levels -O2, -O3, -Os. ๐Ÿ’–

  • -free โ€” attempts to remove redundant extension instructions ๐Ÿงผ This is especially helpful for the x86-64 architecture, which implicitly zero-extends in 64-bit registers after writing to their lower 32-bit half.

Enabled for Alpha, AArch64, LoongArch, PowerPC, RISC-V, SPARC, h83000 and x86 at levels -O2, -O3, -Os. ๐ŸŒŸ

  • -fno-lifetime-dse โ€” in C++, an object's value only gets affected by changes within its lifetime ๐Ÿ•ฐ๏ธ When the constructor begins, the object has an indeterminate value, and any changes during its lifetime are dead once it's destroyed. Normally dead store elimination takes advantage of this. If your code relies on the object storage persisting beyond the object's lifetime, use this flag to disable the optimization. To keep stores made before the constructor starts (for example, because your operator new clears the object storage) but still treat the object as dead after the destructor, you can use -flifetime-dse=1. The default behavior can be explicitly selected with -flifetime-dse=2. And -flifetime-dse=0 is equivalent to -fno-lifetime-dse. ๐Ÿซถ
  • -flive-range-shrinkage โ€” attempts to decrease register pressure through register live range shrinkage ๐ŸŒฌ๏ธ It's helpful for fast processors with small or moderate size register sets.
  • -fira-algorithm=algorithm โ€” picks the coloring algorithm for the integrated register allocator ๐ŸŽจ The algorithm argument can be priority, which specifies Chow's priority coloring, or CB, which specifies Chaitin-Briggs coloring. Chaitin-Briggs coloring isn't implemented for all architectures, but on the targets that do support it, it's the default because it generates better code ๐Ÿ‘‘
  • -fira-region=region โ€” lets you choose the regions the integrated register allocator uses ๐Ÿ—บ๏ธ The region argument should be one of the following:
    • all โ€” uses all loops as register allocation regions. This can give the best results for machines with a small and/or irregular register set ๐Ÿ’œ
    • mixed โ€” uses all loops except for loops with small register pressure as the regions. This value usually gives the best results in most cases and for most architectures, and it's enabled by default when compiling with optimization for speed (-O, -O2, ...) ๐Ÿ”ฅ
    • one โ€” uses all functions as a single region. This typically results in the smallest code size, and it's enabled by default for -Os or -O0 ๐Ÿ“
  • -fira-hoist-pressure โ€” uses IRA to evaluate register pressure in the code hoisting pass when deciding whether to hoist expressions ๐Ÿ—๏ธ This option usually results in smaller code, but it can slow the compiler down.

This option is enabled at level -Os for all targets. ๐ŸŒธ

  • -fira-loop-pressure โ€” uses IRA to evaluate register pressure in loops when deciding whether to move loop invariants ๐Ÿ” This usually results in faster and smaller code on machines with large register files (>= 32 registers), but it can slow the compiler down too.

This option is enabled at level -O3 for some targets. โœจ

  • -fno-ira-share-save-slots โ€” disables sharing of stack slots used for saving call-used hard registers that live through a call ๐Ÿ™…โ€โ™€๏ธ Each hard register gets its own stack slot, so function stack frames end up larger.
  • -fno-ira-share-spill-slots โ€” disables sharing of stack slots allocated for pseudo-registers ๐Ÿ™…โ€โ™€๏ธ Each pseudo-register that doesn't get a hard register gets its own stack slot, so, yep, the stack frames are larger.
  • -flra-remat โ€” enables CFG-sensitive rematerialization in LRA ๐Ÿง™โ€โ™€๏ธ Instead of loading values of spilled pseudos, LRA tries to rematerialize (recalculate) them when that's profitable.

Enabled at levels -O2, -O3, -Os. ๐Ÿ’…

  • -fdelayed-branch โ€” ๐Ÿงš If the target machine supports it, the compiler tries to shuffle instructions around so they fill the empty slots right after delayed branch instructions. Enabled at levels -O1, -O2, -O3, -Os, but not at -Og. ๐Ÿ’…
  • -fschedule-insns โ€” โœจ If the target supports it, the compiler reorders instructions so the CPU isn't left twiddling its thumbs waiting for data that isn't ready yet. This is a lifesaver on machines with slow floating point or memory loads, because other instructions can get issued while the load or float op is still cooking. Conventionally enabled at -O2 and -O3. However, many targets override this. For example, on x86 it's disabled at all levels, while on AArch64 it's only enabled at -O3. ๐Ÿ™ˆ
  • -fschedule-insns2 โ€” ๐ŸŽ€ Basically the same vibe as -fschedule-insns, but this one asks for an extra scheduling pass after register allocation is done. It's especially useful on machines with a relatively small number of registers, where memory loads take more than one cycle. Enabled at levels -O2, -O3, -Os. ๐Ÿ”ฅ
  • -fno-sched-interblock โ€” ๐Ÿšซ Turns off instruction scheduling across basic blocks. This is normally on when scheduling happens before register allocation, i.e. with -fschedule-insns or at -O2 or higher. ๐Ÿฆ‹
  • -fno-sched-spec โ€” ๐Ÿ™…โ€โ™€๏ธ Turns off speculative motion of non-load instructions. This is normally on when scheduling happens before register allocation, i.e. with -fschedule-insns or at -O2 or higher. ๐Ÿ’œ
  • -fsched-pressure โ€” ๐ŸŒธ Turns on register pressure sensitive instruction scheduling before register allocation. This only makes sense when that kind of scheduling is on, i.e. with -fschedule-insns or at -O2 or higher. Using it can improve the generated code and shrink its size, because it stops register pressure from climbing above the number of available hard registers, which means fewer spills during register allocation. ๐Ÿ‘‘
  • -fsched-spec-load โ€” ๐Ÿ“ Lets the compiler speculatively move some load instructions. Again, this only makes sense when scheduling happens before register allocation, i.e. with -fschedule-insns or at -O2 or higher. โœจ
  • -fsched-spec-load-dangerous โ€” ๐Ÿ˜ญ Lets the compiler speculatively move more load instructions. Same deal: it only makes sense when scheduling happens before register allocation, i.e. with -fschedule-insns or at -O2 or higher. This one is spicy, bestie, so proceed with care. ๐Ÿ’€
  • -fsched-stalled-insns, -fsched-stalled-insns=n โ€” ๐Ÿงš Decides how many instructions (if any) can be yanked early from the queue of stalled instructions and dropped into the ready list during the second scheduling pass. -fno-sched-stalled-insns means no instructions get moved early. -fsched-stalled-insns=0 means there's no limit at all on how many queued instructions can be moved early. Using -fsched-stalled-insns with no value is the same as -fsched-stalled-insns=1. โญ
  • -fsched-stalled-insns-dep, -fsched-stalled-insns-dep=n โ€” ๐Ÿ” Sets how many instruction groups (cycles) get checked for a dependency on a stalled instruction that's a candidate for being moved early. This only has an effect during the second scheduling pass, and only if -fsched-stalled-insns is being used. -fno-sched-stalled-insns-dep is the same as -fsched-stalled-insns-dep=0. Using -fsched-stalled-insns-dep with no value is the same as -fsched-stalled-insns-dep=1. ๐Ÿซถ
  • -fsched2-use-superblocks โ€” ๐ŸŒˆ When scheduling after register allocation, this uses superblock scheduling. That lets instructions move across basic block boundaries, which can give you faster schedules. โš ๏ธ This option is experimental, because not all machine descriptions used by GCC model the CPU closely enough to avoid unreliable results from the algorithm, so it's not guaranteed to be trustworthy yet. It only makes sense when scheduling after register allocation, i.e. with -fschedule-insns2 or at -O2 or higher. ๐Ÿงช
โš ๏ธ Heads up, bestie: -fsched2-use-superblocks is experimental. Some machine descriptions don't model the CPU closely enough, so the results can be unreliable. Use it with your eyes open. ๐Ÿ‘€
  • -fsched-group-heuristic โ€” ๐Ÿ’– Turns on the group heuristic in the scheduler. This heuristic favors the instruction that belongs to a schedule group. It's enabled by default whenever scheduling is enabled, i.e. with -fschedule-insns, -fschedule-insns2, or at -O2 or higher. ๐ŸŽ€
  • -fsched-critical-path-heuristic โ€” ๐Ÿ›ค๏ธ Turns on the critical-path heuristic in the scheduler. This one favors instructions that sit on the critical path. It's enabled by default whenever scheduling is enabled, i.e. with -fschedule-insns, -fschedule-insns2, or at -O2 or higher. ๐Ÿ‘‘
  • -fsched-spec-insn-heuristic โ€” ๐Ÿฆ„ Turns on the speculative instruction heuristic in the scheduler. It favors speculative instructions with greater dependency weakness. Enabled by default whenever scheduling is enabled, i.e. with -fschedule-insns, -fschedule-insns2, or at -O2 or higher. โœจ
  • -fsched-rank-heuristic โ€” ๐Ÿ… Turns on the rank heuristic in the scheduler. It favors the instruction living in a basic block with greater size or frequency. Enabled by default whenever scheduling is enabled, i.e. with -fschedule-insns, -fschedule-insns2, or at -O2 or higher. ๐ŸŒธ
  • -fsched-last-insn-heuristic โ€” ๐ŸŽฏ Turns on the last-instruction heuristic in the scheduler. It favors the instruction that is less dependent on the last instruction scheduled. Enabled by default whenever scheduling is enabled, i.e. with -fschedule-insns, -fschedule-insns2, or at -O2 or higher. ๐Ÿ’…
  • -fsched-dep-count-heuristic โ€” ๐Ÿ“ˆ Turns on the dependent-count heuristic in the scheduler. It favors the instruction that has more instructions depending on it. Enabled by default whenever scheduling is enabled, i.e. with -fschedule-insns, -fschedule-insns2, or at -O2 or higher. ๐Ÿซถ
  • -freschedule-modulo-scheduled-loops โ€” ๐Ÿ” Modulo scheduling happens before the traditional scheduling. If a loop gets modulo scheduled, later scheduling passes are allowed to change its schedule. Use this option to control that behavior. ๐Ÿงš
  • -fselective-scheduling โ€” ๐ŸŽ€ Schedules instructions using the selective scheduling algorithm. Selective scheduling runs instead of the first scheduler pass. ๐Ÿ’œ
  • -fselective-scheduling2 โ€” ๐Ÿฆ‹ Schedules instructions using the selective scheduling algorithm too, but this time it runs instead of the second scheduler pass. ๐ŸŒˆ
  • -fsel-sched-pipelining โ€” ๐ŸŒ€ Enables software pipelining of innermost loops during selective scheduling. This does nothing unless -fselective-scheduling or -fselective-scheduling2 is turned on. ๐Ÿ“
  • -fsel-sched-pipelining-outer-loops โ€” ๐Ÿช† When pipelining loops during selective scheduling, this also pipelines the outer loops. It has no effect unless -fsel-sched-pipelining is turned on. โœจ
  • -fsemantic-interposition โ€” ๐Ÿง Some object formats, like ELF, let the dynamic linker interpose symbols. That means that for symbols exported from the DSO, the compiler can't do interprocedural propagation, inlining, or other optimizations, because the function or variable might get swapped out at runtime. Interposition is useful, for example, when you want to replace memory allocation functions with a debugging implementation, but it costs you a lot in code quality. With -fno-semantic-interposition, the compiler assumes that if interposition happens for functions, the overwriting function will have precisely the same semantics (and side effects). Similarly, if interposition happens for variables, the constructor of the variable will be the same. This flag has no effect on functions explicitly declared inline (where interposition is never allowed to change semantics), and it has no effect on symbols explicitly declared weak. ๐Ÿ’œ
  • -fshrink-wrap โ€” ๐ŸŽ Emits function prologues only before the parts of the function that actually need them, instead of at the very top of the function. Enabled by default at -O and higher. โœจ
  • -fshrink-wrap-separate โ€” โœ‚๏ธ Shrink-wraps the separate parts of the prologue and epilogue on their own, so those parts only run when they're needed. This option is on by default, but it has no effect unless -fshrink-wrap is also turned on and the target supports this. ๐ŸŒธ
  • -fcaller-saves โ€” ๐Ÿ’พ Lets the compiler allocate values to registers that get clobbered by function calls, by emitting extra instructions to save and restore those registers around such calls. That allocation only happens when it seems to produce better code. This option is always enabled by default on certain machines, usually the ones with no call-preserved registers to use instead. Enabled at levels -O2, -O3, -Os. ๐Ÿซถ
  • -fcombine-stack-adjustments โ€” ๐Ÿฅž Tracks stack adjustments (pushes and pops) and stack memory references, then tries to find ways to combine them. Enabled by default at -O1 and higher. ๐Ÿฆ„
  • -fipa-ra โ€” ๐Ÿง  Lets the compiler use caller-save registers for allocation if those registers aren't used by any called function. In that case, there's no need to save and restore them around calls. This is only possible if the called functions are part of the same compilation unit as the current function and they're compiled before it. Enabled at levels -O2, -O3, -Os. However, the option is disabled if the generated code will be instrumented for profiling (-p, or -pg), or if the callee's register usage can't be known exactly (which happens on targets that don't expose prologues and epilogues in RTL). ๐Ÿ”
  • -fconserve-stack โ€” ๐Ÿชถ Tries to minimize stack usage. The compiler works to use less stack space, even if that makes the program slower. This option implies setting the large-stack-frame parameter to 100 and the large-stack-frame-growth parameter to 400. ๐ŸŒ™
  • -ftree-reassoc โ€” ๐ŸŒณ Performs reassociation on trees. Enabled by default at -O1 and higher. ๐Ÿ’…
  • -fcode-hoisting โ€” ๐Ÿ—๏ธ Performs code hoisting. This tries to move the evaluation of expressions that run on all paths to the function exit as early as possible. It's especially useful as a code size optimization, but it often helps code speed too. Enabled by default at -O2 and higher. โœจ
  • -ftree-pre โ€” ๐Ÿ”ฎ Performs partial redundancy elimination (PRE) on trees. Enabled by default at -O2 and -O3. ๐ŸŽ€
  • -ftree-partial-pre โ€” ๐Ÿ”ฅ Makes partial redundancy elimination (PRE) more aggressive. Enabled by default at -O3. ๐Ÿ‘‘
  • -ftree-forwprop โ€” โžก๏ธ Performs forward propagation on trees. Enabled by default at -O1 and higher. ๐Ÿฆ‹
  • -ftree-fre โ€” okay so this one does full redundancy elimination (FRE) on trees ๐Ÿ’…. The difference between FRE and PRE is that FRE only looks at expressions that are computed on all paths leading to the redundant computation. It's faster than PRE, though it catches fewer redundancies ๐Ÿซถ. This flag is enabled by default at -O1 and higher โœจ.
  • -ftree-phiprop โ€” this one hoists loads from conditional pointers on trees ๐Ÿฆ‹. It's enabled by default at -O1 and higher.
  • -fhoist-adjacent-loads โ€” ngl this one is so cute ๐ŸŽ€. It speculatively hoists loads from both branches of an if-then-else, but only when the loads come from adjacent locations in the same structure and the target architecture has a conditional move instruction. It's enabled by default at -O2 and higher.
  • -ftree-copy-prop โ€” copy propagation on trees, bestie ๐Ÿงš. It gets rid of unnecessary copy operations. Enabled by default at -O1 and higher.
  • -fipa-pure-const โ€” this one figures out which functions are pure or constant ๐Ÿ”ฎ. Enabled by default at -O1 and higher.
  • -fipa-reference โ€” this one finds out which static variables don't escape the compilation unit ๐ŸŒธ. Enabled by default at -O1 and higher.
  • -fipa-reference-addressable โ€” this one discovers read-only, write-only and non-addressable static variables ๐Ÿ’œ. Enabled by default at -O1 and higher.
  • -fipa-reorder-for-locality โ€” main character energy ๐Ÿ‘‘. It groups call chains close together in the binary layout, which improves code locality and shortens jump distances between functions that get called a lot ๐ŸŒˆ. Unlike -freorder-functions, this pass looks at the call chains between functions and clusters them together, instead of sorting everything into separate hot/normal/cold/never-executed sections. Unlike -fprofile-reorder-functions, it's going for locality across the whole runtime of the program, not just startup ๐Ÿš€.
This option is incompatible with an explicit -flto-partition= option, since it enforces its own custom partitioning scheme. If you use it, it's recommended to also use profile feedback, but this option is not enabled by default otherwise ๐Ÿ’€.
  • -fipa-stack-alignment โ€” reduces stack alignment on call sites when it's possible โœจ. Enabled by default.
  • -fipa-pta โ€” interprocedural pointer analysis plus interprocedural modification and reference analysis ๐Ÿง.
This one can cause excessive memory and compile-time usage on large compilation units, so be careful ๐Ÿ˜ญ. It is not enabled by default at any optimization level.
  • -fipa-profile โ€” interprocedural profile propagation ๐Ÿ“. Functions that are only called from cold functions get marked as cold, and functions that run once (like "cold", "noreturn", static constructors or destructors) get identified too. Cold functions, plus the loop-less parts of functions that run once, then get optimized for size. Enabled by default at -O1 and higher.
  • -fipa-modref โ€” interprocedural mod/ref analysis ๐Ÿ’–. It analyzes the side effects of functions (the memory locations they modify or reference), which allows better optimization across the function call boundary. Enabled by default at -O1 and higher.
  • -fipa-cp โ€” interprocedural constant propagation ๐Ÿ”ฅ. It figures out when values passed to functions are constants, then optimizes accordingly. This can seriously boost performance if your app passes constants into functions. Enabled by default at -O2, -Os and -O3, and also enabled by -fprofile-use and -fauto-profile.
  • -fipa-cp-clone โ€” function cloning to make interprocedural constant propagation stronger ๐Ÿฆ‹. When enabled, it clones externally visible functions that can be called with constant arguments. Since this can create multiple copies of functions, it may significantly increase code size (see --param ipa-cp-unit-growth=value) โš ๏ธ. Enabled by default at -O3, and also enabled by -fprofile-use and -fauto-profile.
  • -fipa-bit-cp โ€” when enabled, does interprocedural bitwise constant propagation ๐ŸŽ€. Enabled by default at -O2, and also by -fprofile-use and -fauto-profile. It requires that -fipa-cp is enabled.
  • -fipa-vrp โ€” when enabled, does interprocedural propagation of value ranges ๐Ÿ“. Enabled by default at -O2. It requires that -fipa-cp is enabled.
  • -fipa-icf โ€” Identical Code Folding for functions and read-only variables ๐ŸงŠ. It shrinks code size, but it can mess with unwind stacks, because a function gets replaced by an equivalent one with a different name ๐Ÿ™ˆ. It works better with link-time optimization turned on.

Although it's similar to the Gold Linker's ICF optimization, GCC's ICF works on different levels, so the optimizations aren't the same. Some equivalences are only found by GCC, and some are only found by Gold ๐Ÿค“.

This flag is enabled by default at -O2 and -Os.

  • -flate-combine-instructions โ€” turns on two instruction combination passes that run pretty late in compilation ๐Ÿงฉ. One runs before register allocation and the other runs after it. The main goal is to substitute definitions into all their uses. Most targets enable this by default at -O2 and -Os.
  • -flive-patching=level โ€” controls GCC's optimizations so the output is suitable for live-patching ๐Ÿฉน.

So here's the deal: if an optimization uses a function's body (or info pulled from its body) to optimize a different function, that other function is called an impacted function of the first one. If a function gets patched, its impacted functions should be patched too ๐Ÿซถ. Impacted functions are determined by the compiler's interprocedural optimizations. For example, a caller is impacted when a function gets inlined into it, when a function gets cloned and its caller is changed to call the clone, or when a function's pureness/constness info gets used to optimize its direct or indirect callers.

Usually, the more IPA optimizations you have enabled, the more impacted functions each function ends up with. To keep that number under control and make the list easier to compute, IPA optimizations can be partially enabled at two different levels ๐ŸŒธ.

The level argument should be one of the following:

  • inline-clone โ€” only inlining and cloning optimizations get enabled. That includes inlining, cloning, interprocedural scalar replacement of aggregates and partial inlining. So when you patch a function, all its callers and its clones' callers are impacted, and need patching too. This disables the following optimization flags: -fwhole-program -fipa-pta -fipa-reference -fipa-ra -fipa-icf -fipa-icf-functions -fipa-icf-variables -fipa-bit-cp -fipa-vrp -fipa-pure-const -fipa-reference-addressable -fipa-stack-alignment -fipa-modref
  • inline-only-static โ€” only inlining of static functions is enabled ๐ŸŒ™. So when you patch a static function, all its callers are impacted and need patching too. On top of everything inline-clone disables, -flive-patching=inline-only-static also disables these: -fipa-cp-clone -fipa-sra -fpartial-inlining -fipa-cp

When -flive-patching is specified without any value, the default value is inline-clone. This flag is disabled by default.

Note that -flive-patching is not supported with link-time optimization (-flto) ๐Ÿšซ.
  • -fisolate-erroneous-paths-dereference โ€” detects paths that trigger erroneous or undefined behavior by dereferencing a null pointer ๐Ÿ•ต๏ธโ€โ™€๏ธ. It isolates those paths from the main control flow and turns the statement with the erroneous or undefined behavior into a trap. Enabled by default at -O2 and higher, and it depends on -fdelete-null-pointer-checks also being enabled.
  • -fisolate-erroneous-paths-attribute โ€” detects paths that trigger erroneous or undefined behavior because a null value is used in a way that a returns_nonnull or nonnull attribute forbids ๐Ÿ™…โ€โ™€๏ธ. It isolates those paths and turns the statement into a trap, same as above. This one is not currently enabled, but it may be enabled by -O2 in the future ๐Ÿ‘€.
  • -ftree-sink โ€” forward store motion on trees ๐Ÿ›ผ. Enabled by default at -O1 and higher.
  • -ftree-bit-ccp โ€” sparse conditional bit constant propagation on trees, plus propagation of pointer alignment information ๐Ÿง . This pass only operates on local scalar variables. It's enabled by default at -O1 and higher, except for -Og, and it requires that -ftree-ccp is enabled.
  • -ftree-ccp โ€” sparse conditional constant propagation (CCP) on trees ๐Ÿ’ซ. This pass only operates on local scalar variables, and it's enabled by default at -O1 and higher.
  • -fssa-backprop โ€” propagates info about how a value gets used back up the definition chain so the definitions can be simplified ๐Ÿช„. For example, this pass strips sign operations if the sign of a value never matters. Enabled by default at -O1 and higher.
  • -fssa-phiopt โ€” pattern matching on SSA PHI nodes to optimize conditional code ๐ŸŽฏ. Enabled by default at -O1 and higher, except for -Og.
  • -ftree-switch-conversion โ€” turns simple initializations in a switch into initializations from a scalar array ๐Ÿญ. Enabled by default at -O2 and higher.
  • -ftree-tail-merge โ€” looks for identical code sequences, and when it finds them, replaces one with a jump to the other ๐Ÿงต. This is known as tail merging or cross jumping. Enabled by default at -O2 and higher. The compilation time for this pass can be limited with the max-tail-merge-comparisons parameter and the max-tail-merge-iterations parameter.
  • -ftree-cselim โ€” conditional store elimination on trees ๐ŸŒท. Enabled by default at -O1 and higher, on targets that have conditional move instructions.

Okay bestie, let's keep going with the optimization flags ๐Ÿ’…โœจ

  • -ftree-dce โ€” ๐Ÿงน Does dead code elimination (DCE) on trees, aka it yeets code that never actually does anything. This one's switched on by default at -O1 and higher, so you get it for free ๐Ÿ’–
  • -ftree-builtin-call-dce โ€” ๐ŸŽ€ Does conditional DCE for calls to built-in functions that might set errno but otherwise have no side effects. It's on by default at -O2 and higher, but only if -Os isn't also specified. Main character energy, but make sure you're not stacking -Os in there ๐Ÿ™ˆ
  • -ffinite-loops โ€” ๐Ÿ” Tells the compiler to assume that a loop with an exit will eventually take that exit and won't loop forever. That lets it remove loops that have no side effects, and it doesn't count eventual endless looping as a side effect. This is turned on by default at -O2 for C++ with -std=c++11 or higher ๐Ÿ‘‘
  • -ftree-dominator-opts โ€” ๐Ÿงผ Runs a bunch of simple scalar cleanups based on a dominator tree traversal: constant/copy propagation, redundancy elimination, range propagation, and expression simplification. It also does jump threading, which reduces jumps to jumps โœจ Enabled by default at -O1 and higher.
  • -ftree-dse โ€” ๐Ÿ—‘๏ธ Does dead store elimination (DSE) on trees. A dead store is a store into a memory location that gets overwritten by another store later on with no loads in between, so the earlier store can be deleted ๐Ÿฆ‹ Enabled by default at -O1 and higher.
  • -ftree-ch โ€” ๐Ÿ”„ Does loop header copying on trees. It's super beneficial because it makes code motion optimizations more effective, and it also saves one jump ๐ŸŒธ Enabled by default at -O1 and higher, but NOT for -Os, since it usually bumps up code size.
  • -ftree-loop-optimize โ€” ๐ŸŒ€ Does loop optimizations on trees. Enabled by default at -O1 and higher ๐Ÿ’œ
  • -ftree-loop-linear, -floop-strip-mine, -floop-block โ€” ๐Ÿงฉ Loop nest optimizations. These are the same as -floop-nest-optimize. To actually use this transformation, GCC has to be configured with --with-isl so the Graphite loop transformation infrastructure gets turned on ๐Ÿ› ๏ธ
  • -fgraphite-identity โ€” ๐Ÿชž Enables the identity transformation for Graphite. For every SCoP, it builds the polyhedral representation and transforms it back into GIMPLE. This is how you can check the costs or benefits of the GIMPLE -> GRAPHITE -> GIMPLE round trip ๐Ÿ” Along the way the isl code generator does some minimal optimizations too, like index splitting and dead code elimination in loops.
  • -floop-nest-optimize โ€” ๐ŸŒŸ Turns on the isl-based loop nest optimizer. It's a generic one based on the Pluto optimization algorithms, and it calculates a loop structure that's optimized for data locality and parallelism. Heads up: this option is experimental โš ๏ธ
  • -floop-parallelize-all โ€” ๐Ÿงต Uses the Graphite data dependence analysis to find loops that can be parallelized. It parallelizes ALL the loops that can be analyzed as having no loop carried dependences, and it does this without checking whether parallelizing them is actually profitable ๐Ÿ’€
  • -ftree-coalesce-vars โ€” ๐Ÿค When the program is being transformed out of SSA representation, this tries to cut down on copying by coalescing versions of different user-defined variables, not just compiler temporaries. Fair warning: this can severely limit your ability to debug an optimized program compiled with -fno-var-tracking-assignments ๐Ÿ˜ญ In the negated form, this flag stops SSA coalescing of user variables. It's enabled by default if optimization is enabled, and it does very little otherwise.
  • -ftree-loop-if-convert โ€” ๐Ÿช„ Tries to transform conditional jumps in the innermost loops into branch-less equivalents. The goal is to get rid of control flow in the innermost loops so the vectorization pass can handle them better. It's enabled by default if vectorization is enabled โœจ
  • -ftree-loop-distribution โ€” โœ‚๏ธ Performs loop distribution. This can improve cache performance on big loop bodies and lets further loop optimizations, like parallelization or vectorization, happen. Here's the vibe with a Fortran loop:
DO I = 1, N
  A(I) = B(I) + C
  D(I) = E(I) * F
ENDDO

It gets split into this:

DO I = 1, N
   A(I) = B(I) + C
ENDDO
DO I = 1, N
   D(I) = E(I) * F
ENDDO

This is enabled by default at -O3. It's also enabled by -fprofile-use and -fauto-profile ๐ŸŒˆ

  • -ftree-loop-distribute-patterns โ€” ๐Ÿ“š Does loop distribution for patterns that can be code generated as calls to a library. It's enabled by default at -O2 and higher, and by -fprofile-use and -fauto-profile too. This pass distributes the initialization loops and generates a call to memset zero. Check out this loop:
DO I = 1, N
  A(I) = 0
  B(I) = A(I) + I
ENDDO

It becomes:

DO I = 1, N
   A(I) = 0
ENDDO
DO I = 1, N
   B(I) = A(I) + I
ENDDO

And then the initialization loop gets turned into a call to memset zero ๐Ÿงธ

  • -floop-interchange โ€” ๐Ÿ”€ Performs loop interchange outside of Graphite. This can improve cache performance on loop nests and lets further loop optimizations, like vectorization, take place. Like this C loop:
for (int i = 0; i < N; i++)
  for (int j = 0; j < N; j++)
    for (int k = 0; k < N; k++)
      c[i][j] = c[i][j] + a[i][k]*b[k][j];

gets flipped into this:

for (int i = 0; i < N; i++)
  for (int k = 0; k < N; k++)
    for (int j = 0; j < N; j++)
      c[i][j] = c[i][j] + a[i][k]*b[k][j];

This is enabled by default at -O3, and also by -fprofile-use and -fauto-profile ๐Ÿซถ

  • -floop-unroll-and-jam โ€” ๐Ÿฅช Applies unroll and jam transformations on feasible loops. In a loop nest, this unrolls the outer loop by some factor and fuses the resulting multiple inner loops together. Enabled by default at -O3, and also by -fprofile-use and -fauto-profile ๐Ÿ”ฅ
  • -ftree-loop-im โ€” ๐Ÿšš Performs loop invariant motion on trees. This pass only moves invariants that are hard to handle at the RTL level, like function calls and operations that expand into nontrivial sequences of insns. With -funswitch-loops, it also moves operands of conditions that are invariant out of the loop, so that trivial invariantness analysis can be used in loop unswitching. The pass also includes store motion ๐Ÿง 
  • -ftree-loop-ivcanon โ€” ๐Ÿ”ข Creates a canonical counter for the number of iterations in loops where figuring out the iteration count takes complicated analysis. Later optimizations can then figure out the number easily. It's especially useful alongside unrolling ๐ŸŒท
  • -ftree-scev-cprop โ€” ๐ŸŽฏ Performs final value replacement. If a variable gets modified in a loop in such a way that its value when exiting the loop can be determined using only its initial value and the number of loop iterations, then uses of the final value get replaced by that computation, as long as it's sufficiently cheap. This reduces data dependencies and may allow further simplifications. Enabled by default at -O1 and higher ๐Ÿ’…
  • -fivopts โ€” ๐Ÿ“ˆ Performs induction variable optimizations on trees: strength reduction, induction variable merging, and induction variable elimination. Enabled by default at -O1 and higher ๐Ÿฆ„
  • -ftree-parallelize-loops=n โ€” ๐Ÿงถ Parallelizes loops by splitting their iteration space so it runs in n threads. This is only possible for loops whose iterations are independent and can be arbitrarily reordered. It's only really profitable on multiprocessor machines, and only for CPU-intensive loops rather than loops that are constrained by something like memory bandwidth. This option implies -pthread, so it's only supported on targets that support -pthread ๐Ÿงต
  • -ftree-pta โ€” ๐Ÿ” Performs function-local points-to analysis on trees. It's enabled by default at -O1 and higher, except for -Og ๐ŸŒธ
  • -ftree-sra โ€” ๐Ÿช“ Performs scalar replacement of aggregates. This pass swaps structure references out for scalars, so structures don't get committed to memory too early. Enabled by default at -O1 and higher, except for -Og ๐Ÿ’–
  • -fstore-merging โ€” ๐Ÿงฑ Merges narrow stores to consecutive memory addresses. It takes contiguous stores of immediate values that are narrower than a word and combines them into fewer, wider stores to cut down on instructions. Enabled by default at -O2 and higher, as well as -Os โœจ
  • -ftree-ter โ€” ๐Ÿ” Performs temporary expression replacement during the SSA->normal phase. Single use/single def temporaries get replaced at their use location with their defining expression. This results in non-GIMPLE code, but it gives the expanders much more complex trees to work with, which leads to better RTL generation ๐Ÿงš Enabled by default at -O1 and higher.
  • -ftree-slsr โ€” ๐Ÿ“ Performs straight-line strength reduction on trees. It recognizes related expressions involving multiplications and swaps them for less expensive calculations when possible. Enabled by default at -O1 and higher ๐ŸŒˆ
  • -ftree-vectorize โ€” ๐Ÿš€ Performs vectorization on trees. This flag enables -ftree-loop-vectorize and -ftree-slp-vectorize if they aren't explicitly specified already ๐Ÿ’œ
  • -ftree-loop-vectorize โ€” ๐Ÿ‹๏ธ Performs loop vectorization on trees. Enabled by default at -O2, and also by -ftree-vectorize, -fprofile-use, and -fauto-profile ๐ŸŽ€
  • -ftree-slp-vectorize โ€” ๐Ÿ“ Performs basic block vectorization on trees. Enabled by default at -O2, and also by -ftree-vectorize, -fprofile-use, and -fauto-profile ๐Ÿซง

โœจ -ftrivial-auto-var-init=choice โœจ

okay so this one is about initializing automatic variables ๐ŸŽ€ to make your program safer and more predictable by stopping uninitialized memory from leaking out or getting used ๐Ÿ˜ญ. GCC still treats an automatic variable with no explicit initializer as uninitialized, so -Wuninitialized and -Wanalyzer-use-of-uninitialized-value will still flag them ๐Ÿšฉ, and the optimizer still treats them as uninitialized too. With this option, GCC also zeroes any padding in automatic variables that are structs or unions ๐Ÿซถ. bestie, the catch is that the current implementation can't initialize automatic variables declared between the controlling expression and the first case of a "switch" statement ๐Ÿ’€. Use -Wtrivial-auto-var-init to report all such cases.

The three values of choice are:

  • uninitialized โ€” doesn't initialize any automatic variables at all. This is C and C++'s default ๐Ÿ’….
  • pattern โ€” initializes automatic variables with values that will likely turn logic bugs into crashes down the line ๐Ÿ’ฅ. They're easy to spot in a crash dump and aren't values programmers can rely on for real program semantics โœจ. The current value is a byte-repeatable pattern with the byte 0xFE. The values used for pattern initialization might change in the future ๐Ÿ‘€.
  • zero โ€” initializes automatic variables with zeroes ๐ŸŒธ.

The default is uninitialized ๐Ÿฆ‹.

Note that the initializer values, whether zero or pattern, refer to the data representation (in memory or machine registers), not to how the value gets interpreted as a number ๐Ÿงš. This distinction matters a lot in languages that support types with biases or implicit multipliers, and with extensions like hardbool ๐Ÿ”ฅ. For example, a variable that uses 8 bits to represent (biased) quantities in the "range 160..400" will be initialized with the bit patterns 0x00 or 0xFE, depending on choice. That's true whether or not those representations stand for values in that range, and even if they do, the interpretation of the held value depends on the bias ๐Ÿ™ˆ. A hardbool variable that uses say "0X5A" and 0xA5 for "false" and "true", respectively, will trap with either choice of trivial initializer. So zero initialization won't convert to the representation for "false", even though it would for a "static" variable of the same type ๐Ÿ’€. This means the initializer pattern doesn't generally depend on the type of the initialized variable. One notable exception: (non-hardened) boolean variables that fit in registers are initialized with "false" (zero), even when pattern is requested ๐Ÿซถ.

You can control this behavior for a specific variable by using the variable attribute "uninitialized" ๐Ÿ’–.

โœจ -fvect-cost-model=model โœจ

this one changes the cost model used for vectorization ๐ŸŒˆ. The model argument should be one of unlimited, dynamic, cheap or very-cheap. With unlimited, the vectorized code path is assumed to be profitable ๐Ÿ’ช. With dynamic, a runtime check guards the vectorized code path so it only runs for iteration counts that will likely execute faster than the original scalar loop ๐Ÿƒโ€โ™€๏ธ. cheap disables vectorization of loops where doing so would be cost prohibitive, like when it needs runtime checks for data dependence or alignment, but otherwise it's the same as dynamic ๐Ÿง. very-cheap disables vectorization of loops whenever any runtime check for data dependence or alignment is required. It also disables vectorization of epilogue loops, but otherwise it's the same as cheap ๐Ÿฅฒ.

The default cost model depends on other optimization flags and is either dynamic or cheap โญ.

โœจ -fsimd-cost-model=model โœจ

this one is like the one above, but for loops marked with the OpenMP simd directive ๐Ÿฆ‹. The model argument should be one of unlimited, dynamic, cheap. All values of model mean the same thing as described in -fvect-cost-model, and by default, a cost model defined with -fvect-cost-model is used ๐Ÿซถ.

โœจ -ftree-vrp โœจ

this turns on Value Range Propagation on trees ๐ŸŒฟ. It's kind of like the constant propagation pass, except instead of propagating values, it propagates ranges of values โœจ. That lets the optimizers remove unnecessary range checks, like array bound checks and null pointer checks ๐Ÿง . It's enabled by default at -O2 and higher. Null pointer check elimination only happens if -fdelete-null-pointer-checks is enabled ๐Ÿ™ˆ.

โœจ -fsplit-paths โœจ

this splits paths that lead to loop backedges ๐Ÿ›ค๏ธ. It can improve dead code elimination and common subexpression elimination ๐Ÿงน. It's enabled by default at -O3 and above ๐Ÿ”ฅ.

โœจ -fsplit-ivs-in-unroller โœจ

this lets the compiler express the values of induction variables in later iterations of an unrolled loop using the value from the first iteration ๐Ÿงฎ. That breaks up long dependency chains, which makes the scheduling passes more efficient ๐Ÿš€.

A combination of -fweb and CSE is often enough to get the same effect ๐Ÿ’…. But that isn't reliable when the loop body is more complicated than a single basic block ๐Ÿ˜ฌ. It also doesn't work at all on some architectures because of restrictions in the CSE pass ๐Ÿ’€.

This optimization is enabled by default ๐Ÿ’–.

โœจ -fvariable-expansion-in-unroller โœจ

with this option, the compiler makes multiple copies of some local variables when it unrolls a loop, which can result in superior code ๐Ÿ‘‘.

This optimization is enabled by default for PowerPC targets, but disabled by default otherwise ๐ŸŒธ.

โœจ -fpartial-inlining โœจ

this inlines parts of functions ๐Ÿงฉ. It only has any effect when inlining itself is turned on by the -finline-functions or -finline-small-functions options ๐Ÿ™ƒ.

Enabled at levels -O2, -O3, -Os ๐ŸŽ€.

โœจ -fpredictive-commoning โœจ

this performs predictive commoning, which means reusing computations (especially memory loads and stores) that were done in previous iterations of loops ๐Ÿ”.

This option is enabled at level -O3 ๐Ÿ”ฅ. It's also enabled by -fprofile-use and -fauto-profile ๐Ÿ’œ.

โœจ -fprefetch-loop-arrays โœจ

if the target machine supports it, this generates instructions to prefetch memory, which can speed up loops that access large arrays ๐ŸŽ๏ธ.

This option may generate better or worse code ๐Ÿซ . The results depend heavily on the structure of the loops in your source code ๐Ÿ™ˆ.

Disabled at level -Os ๐Ÿ˜ญ.

โœจ -fno-printf-return-value โœจ

this stops GCC from substituting constants for the known return value of formatted output functions like "sprintf", "snprintf", "vsprintf", and "vsnprintf" (but not "printf" or "fprintf") ๐Ÿ™…โ€โ™€๏ธ. That transformation lets GCC optimize or even eliminate branches based on the known return value of these functions, when they're called with arguments that are either constant or known to be in a range that makes the exact return value possible ๐Ÿง . For example, when -fprintf-return-value is in effect, both the branch and the body of the "if" statement (but not the call to "snprint") can be optimized away when i is a 32-bit or smaller integer, because the return value is guaranteed to be at most 8 ๐ŸŽฏ.

char buf[9];
if (snprintf (buf, "%08x", i) >= sizeof buf)
  ...

The -fprintf-return-value option relies on other optimizations and gives the best results with -O2 and above ๐Ÿ’…. It works in tandem with the -Wformat-overflow and -Wformat-truncation options ๐Ÿซถ. The -fprintf-return-value option is enabled by default โœจ.

โœจ -fno-peephole and -fno-peephole2 โœจ

these disable any machine-specific peephole optimizations ๐Ÿ”. The difference between -fno-peephole and -fno-peephole2 is how they're implemented in the compiler ๐Ÿ› ๏ธ. Some targets use one, some use the other, and a few use both ๐Ÿคทโ€โ™€๏ธ.

-fpeephole is enabled by default. -fpeephole2 is enabled at levels -O2, -O3, -Os ๐ŸŒˆ.

โœจ -fno-guess-branch-probability โœจ

this stops GCC from guessing branch probabilities with heuristics ๐Ÿ™…โ€โ™€๏ธ.

GCC uses heuristics to guess branch probabilities when they aren't provided by profiling feedback (-fprofile-arcs) ๐Ÿ”ฎ. These heuristics are based on the control flow graph ๐Ÿ—บ๏ธ. If some branch probabilities are specified by "__builtin_expect", then the heuristics guess the probabilities for the rest of the control flow graph, taking the "__builtin_expect" info into account ๐Ÿง . The interactions between the heuristics and "__builtin_expect" can get really complex, so in some cases it might help to disable the heuristics so the effects of "__builtin_expect" are easier to understand ๐Ÿฅน.

You can also specify the expected probability of an expression with the "__builtin_expect_with_probability" built-in function ๐Ÿ’–.

The default is -fguess-branch-probability at levels -O, -O2, -O3, -Os ๐ŸŒธ.

โœจ -freorder-blocks โœจ

this reorders basic blocks in the compiled function to reduce the number of taken branches and improve code locality ๐Ÿงฉ.

Enabled at levels -O1, -O2, -O3, -Os ๐ŸŽ€.

โœจ -freorder-blocks-algorithm=algorithm โœจ

this picks the algorithm used for basic block reordering ๐Ÿงญ. The algorithm argument can be simple, which doesn't increase code size (except sometimes because of secondary effects like alignment) ๐Ÿค. Or it can be stc, the "software trace cache" algorithm, which tries to put all the often-executed code together and minimizes the number of branches executed by making extra copies of code ๐Ÿƒโ€โ™€๏ธ.

The default is simple at levels -O1 and -Os, and stc at levels -O2 and -O3 ๐ŸŒŸ.

โœจ -freorder-blocks-and-partition โœจ

in addition to reordering basic blocks in the compiled function to reduce taken branches, this partitions hot and cold basic blocks into separate sections of the assembly and .o files ๐ŸงŠ๐Ÿ”ฅ. The goal is better paging and cache locality performance ๐Ÿ“ˆ.

This optimization is automatically turned off in the presence of exception handling or unwind tables (on targets using setjump/longjump or a target-specific scheme) ๐Ÿ™…โ€โ™€๏ธ. It's also off for linkonce sections, for functions with a user-defined section attribute, and on any architecture that doesn't support named sections ๐Ÿ’€. When -fsplit-stack is used, this option isn't enabled by default (to avoid linker errors), but you can still turn it on explicitly if you're using a working linker ๐Ÿ› ๏ธ.

Enabled for x86 at levels -O2, -O3, -Os ๐Ÿ‘‘.

  • -freorder-functions: okay so this one shuffles the functions in your object file around to boost code locality โœจ. Unlike -fipa-reorder-for-locality, it keeps every function in the same category (hot/normal/cold/never-executed) grouped together ๐Ÿซถ. It does this with special subsections: ".text.hot" for the most frequently executed functions and ".text.unlikely" for the unlikely ones. The linker does the actual reordering, so your object file format has to support named sections and the linker has to place them in a reasonable way ๐Ÿ’….

This option isn't effective unless you either provide profile feedback (see -fprofile-arcs for details) or manually annotate functions with "hot" or "cold" attributes ๐Ÿ™ˆ.

Enabled at levels -O2, -O3, -Os.

  • -fstrict-aliasing: this one lets the compiler assume the strictest aliasing rules that apply to the language being compiled ๐Ÿ”ฅ. For C (and C++), it turns on optimizations based on the type of expressions. The main idea is that an object of one type is assumed never to live at the same address as an object of a different type, unless the types are almost the same. For example, an "unsigned int" can alias an "int", but not a "void*" or a "double". A character type may alias any other type ๐Ÿ’œ.

Pay special attention to code like this:

union a_union {
  int i;
  double d;
};

int f() {
  union a_union t;
  t.d = 3.0;
  return t.i;
}

Reading from a different union member than the one most recently written to (called "type-punning") is super common ๐Ÿ˜ญ. Even with -fstrict-aliasing, type-punning is allowed, as long as the memory is accessed through the union type. So, the code above works as expected. However, this code might not:

int f() {
  union a_union t;
  int* ip;
  t.d = 3.0;
  ip = &t.i;
  return *ip;
}
Accessing memory by taking the address, casting the resulting pointer and dereferencing the result has undefined behavior, even if the cast uses a union type. For example: int f() { double d = 3.0; return ((union a_union *) &d)->i; }

The -fstrict-aliasing option is enabled at levels -O2, -O3, -Os.

  • -fipa-strict-aliasing: controls whether the rules of -fstrict-aliasing get applied across function boundaries ๐Ÿฆ‹. Heads up: if multiple functions get inlined into a single function, the memory accesses are no longer considered to be crossing a function boundary.

The -fipa-strict-aliasing option is enabled by default and is effective only in combination with -fstrict-aliasing.

  • -falign-functions, -falign-functions=n, -falign-functions=n:m, -falign-functions=n:m:n2, -falign-functions=n:m:n2:m2: this lines up the start of functions to the next power-of-two that's greater than or equal to n, skipping up to m-1 bytes ๐ŸŒธ. That way at least the first m bytes of the function can be fetched by the CPU without crossing an n-byte alignment boundary. It's a code performance optimization, and alignment gets ignored for functions considered cold. If you need alignment for all functions, use -fmin-function-alignment instead โœจ.

If m isn't specified, it defaults to n.

Examples: -falign-functions=32 aligns functions to the next 32-byte boundary, -falign-functions=24 aligns to the next 32-byte boundary only if this can be done by skipping 23 bytes or less, -falign-functions=32:7 aligns to the next 32-byte boundary only if this can be done by skipping 6 bytes or less.

The second pair of n2:m2 values lets you set a secondary alignment: -falign-functions=64:7:32:3 aligns to the next 64-byte boundary if this can be done by skipping 6 bytes or less, otherwise it aligns to the next 32-byte boundary if this can be done by skipping 2 bytes or less. If m2 isn't specified, it defaults to n2 ๐ŸŽ€.

Some assemblers only support this flag when n is a power of two; in that case, it gets rounded up.

-fno-align-functions and -falign-functions=1 are equivalent and both mean that functions are not aligned.

If n isn't specified or is zero, the compiler uses a machine-dependent default. The maximum allowed n option value is 65536.

Enabled at levels -O2, -O3.

  • -flimit-function-alignment: when this is enabled, the compiler tries to avoid overaligning functions for no reason ๐Ÿงš. It tries to tell the assembler to align by the amount specified by -falign-functions, but without skipping more bytes than the size of the function.
  • -falign-labels, -falign-labels=n, -falign-labels=n:m, -falign-labels=n:m:n2, -falign-labels=n:m:n2:m2: aligns all branch targets to a power-of-two boundary ๐Ÿ“.

The parameters of this option work the same way as they do for -falign-functions. -fno-align-labels and -falign-labels=1 are equivalent and mean that labels are not aligned.

If -falign-loops or -falign-jumps apply and are greater than this value, then their values get used instead.

If n isn't specified or is zero, the compiler uses a machine-dependent default, which is very likely to be 1, meaning no alignment. The maximum allowed n option value is 65536.

Enabled at levels -O2, -O3.

  • -falign-loops, -falign-loops=n, -falign-loops=n:m, -falign-loops=n:m:n2, -falign-loops=n:m:n2:m2: aligns loops to a power-of-two boundary ๐ŸŒˆ. If the loops run many times, this makes up for the cost of executing the dummy padding instructions. It's a code performance optimization, and alignment is ignored for loops considered cold.

If -falign-labels is greater than this value, then its value is used instead.

The parameters of this option work the same way as they do for -falign-functions. -fno-align-loops and -falign-loops=1 are equivalent and mean that loops are not aligned. The maximum allowed n option value is 65536.

If n isn't specified or is zero, the compiler uses a machine-dependent default.

Enabled at levels -O2, -O3.

  • -falign-jumps, -falign-jumps=n, -falign-jumps=n:m, -falign-jumps=n:m:n2, -falign-jumps=n:m:n2:m2: aligns branch targets to a power-of-two boundary, but only for branch targets that can only be reached by jumping ๐Ÿฆ‹. In this case, no dummy operations need to be executed. It's a code performance optimization, and alignment is ignored for jumps considered cold.

If -falign-labels is greater than this value, then its value is used instead.

The parameters of this option work the same way as they do for -falign-functions. -fno-align-jumps and -falign-jumps=1 are equivalent and mean that loops are not aligned ๐Ÿ™ˆ.

If n isn't specified or is zero, the compiler uses a machine-dependent default. The maximum allowed n option value is 65536.

Enabled at levels -O2, -O3.

  • -fmin-function-alignment: specifies the minimal alignment of functions to the next power-of-two greater than or equal to n ๐Ÿ’…. Unlike -falign-functions, this alignment is applied to all functions, even the ones considered cold. The alignment also isn't affected by -flimit-function-alignment.
  • -fno-allocation-dce: do not remove unused C++ allocations (using operator "new" and operator "delete") during dead code elimination ๐Ÿ’€.

See also -fmalloc-dce.

  • -fallow-store-data-races: lets the compiler perform optimizations that might introduce new data races on stores, without proving that the variable can't be accessed by other threads at the same time ๐Ÿ”ฅ. It doesn't affect optimization of local data. It's safe to use this option if you know that global data won't be accessed by multiple threads.

Examples of optimizations enabled by -fallow-store-data-races include hoisting or if-conversions that might cause a value already in memory to be rewritten with that same value. That kind of rewriting is safe in a single-threaded context, but may be unsafe in a multi-threaded one. Note that on some processors, if-conversions may be required in order to enable vectorization โœจ.

Enabled at level -Ofast.

  • -funit-at-a-time: this one is only kept around for compatibility reasons ๐Ÿซถ. -funit-at-a-time has no effect, while -fno-unit-at-a-time implies -fno-toplevel-reorder and -fno-section-anchors.

Enabled by default.

  • -fno-toplevel-reorder: do not reorder top-level functions, variables, and "asm" statements ๐Ÿ‘‘. Output them in the same order they appear in the input file. When this option is used, unreferenced static variables are not removed. It's meant to support existing code that relies on a particular ordering. For new code, it's better to use attributes when possible.

-ftoplevel-reorder is the default at -O1 and higher, and also at -O0 if -fsection-anchors is explicitly requested. Additionally, -fno-toplevel-reorder implies -fno-section-anchors.

  • -funreachable-traps: with this option, the compiler turns calls to "__builtin_unreachable" into traps, instead of using them for optimization ๐Ÿ˜ญ. This also affects any such calls the compiler generates implicitly.

This option has the same effect as -fsanitize=unreachable -fsanitize-trap=unreachable, but it doesn't affect the values of those options. If -fsanitize=unreachable is enabled, that option takes priority over this one.

This option is enabled by default at -O0 and -Og.

  • -fweb: constructs webs as commonly used for register allocation purposes, and assigns each web its own individual pseudo register ๐Ÿงš. This lets the register allocation pass work on pseudos directly, and it also strengthens several other optimization passes, such as CSE, the loop optimizer and the trivial dead code remover. It can, however, make debugging impossible, since variables no longer stay in a "home register" ๐Ÿฅบ.

Enabled by default with -funroll-loops.

  • -fwhole-program: assumes that the current compilation unit represents the whole program being compiled ๐Ÿ’–. All public functions and variables, with the exception of main and those merged by the attribute externally_visible, become static functions, and in effect get optimized more aggressively by interprocedural optimizers ๐ŸŒธ.

With -flto this option has limited use, bestie ๐Ÿฅบ Most of the time the linker plugin already knows exactly which symbols get used or exported from the binary, and it passes that info to the link-time optimizer. It's still useful if no linker plugin is used, or during an incremental link step where the final code gets produced (with -flto -flinker-output=nolto-rel). ๐Ÿฆ‹

โœจ -flto[=n] โœจ

okay so this one is the standard link-time optimizer and it's giving main character ๐Ÿ‘‘ When you feed it source code, it generates GIMPLE (one of GCC's internal representations) and writes it into special ELF sections in the object file. Then when the object files get linked together, all the function bodies are read back out of those ELF sections and instantiated as if they had been part of the same translation unit. iconic, right? ๐Ÿ’…

To use the link-time optimizer, -flto and your optimization options should be specified at compile time AND during the final link. It's recommended that you compile every file in the same link with the same options, and also specify those options at link time. For example:

gcc -c -O2 -flto foo.c
gcc -c -O2 -flto bar.c
gcc -o myprog -flto -O2 foo.o bar.o

The first two invocations to GCC save a bytecode representation of GIMPLE into special ELF sections inside foo.o and bar.o. The final invocation reads the GIMPLE bytecode from foo.o and bar.o, merges the two files into a single internal image, and compiles the result as usual. Since both foo.o and bar.o get merged into one image, all the interprocedural analyses and optimizations in GCC work across the two files as if they were a single one ๐Ÿคฏ. That means, for example, the inliner can inline functions in bar.o into functions in foo.o and vice-versa. literally so cool.

Another (simpler) way to enable link-time optimization is:

gcc -o myprog -flto -O2 foo.c bar.c

That generates bytecode for foo.c and bar.c, merges them together into a single GIMPLE representation, and optimizes them as usual to produce myprog. ๐ŸŽ€

The important thing to keep in mind is that to enable link-time optimizations you need to use the GCC driver to do the link step. GCC automatically performs link-time optimization if any of the objects involved were compiled with the -flto command-line option. You can always override that automatic decision by passing -fno-lto to the link command. not me needing an override button ๐Ÿ™ˆ

To make whole program optimization effective, you have to make certain whole program assumptions. The compiler needs to know which functions and variables can be accessed by libraries and runtime outside of the link-time optimized unit. When the linker supports it, the linker plugin (see -fuse-linker-plugin) passes info to the compiler about used and externally visible symbols. When the linker plugin isn't available, use -fwhole-program so the compiler can make these assumptions, which leads to more aggressive optimization decisions. โœจ

When a file is compiled with -flto but without -fuse-linker-plugin, the generated object file is bigger than a regular one, because it contains GIMPLE bytecodes AND the usual final code (see -ffat-lto-objects). This means object files with LTO information can be linked as normal object files. If -fno-lto is passed to the linker, no interprocedural optimizations get applied. Note that when -fno-fat-lto-objects is enabled, the compile stage is faster, but you cannot perform a regular, non-LTO link on them. ๐Ÿ˜ญ

When producing the final binary, GCC only applies link-time optimizations to the files that contain bytecode. So you can mix and match object files and libraries with GIMPLE bytecodes and final object code. GCC automatically picks which files to optimize in LTO mode and which files to link without further processing. ๐Ÿซถ

Generally, options specified at link time override those specified at compile time, although in some cases GCC tries to infer link-time options from the settings used to compile the input files.

If you don't specify an optimization level option -O at link time, GCC uses the highest optimization level used when compiling the object files. Note that it's generally ineffective to specify an optimization level option only at link time and not at compile time, for two reasons. First, compiling without optimization suppresses the compiler passes that gather the information needed for effective optimization at link time. Second, some early optimization passes can only be performed at compile time and not at link time. ๐Ÿ’€

There are some code generation flags that GCC preserves when generating bytecodes, since they need to be used during the final link. Right now, the following options and their settings are taken from the first object file that explicitly specifies them: -fcommon, -fexceptions, -fnon-call-exceptions, -fgnu-tm and all the -m target flags. ๐ŸŒธ

The following options -fPIC, -fpic, -fpie and -fPIE are combined based on this scheme:

-fPIC + -fpic = -fpic
-fPIC + -fno-pic = -fno-pic
-fpic/-fPIC + (no option) = (no option)
-fPIC + -fPIE = -fPIE
-fpic + -fPIE = -fpie
-fPIC/-fpic + -fpie = -fpie

Certain ABI-changing flags have to match in all compilation units, and trying to override them at link time with a conflicting value is ignored. This includes options such as -freg-struct-return and -fpcc-struct-return. ๐Ÿšซ

Other options such as -ffp-contract, -fno-strict-overflow, -fwrapv, -fno-trapv or -fno-strict-aliasing are passed through to the link stage and merged conservatively for conflicting translation units. Specifically, -fno-strict-overflow, -fwrapv and -fno-trapv take precedence, and for example -ffp-contract=off takes precedence over -ffp-contract=fast. You can override them at link time. ๐Ÿ”ฅ

Diagnostic options such as -Wstringop-overflow are passed through to the link stage, and their setting matches that of the compile step at function granularity. This only matters for diagnostics emitted during optimization. Also, code transforms such as inlining can lead to warnings being enabled or disabled for regions of code that aren't consistent with the setting at compile time. ๐Ÿ™ˆ

When you need to pass options to the assembler via -Wa or -Xassembler, make sure to either compile those translation units with -fno-lto, or consistently use the same assembler options on all translation units. You can also specify assembler options at LTO link time, as an alternative. ๐Ÿงš

To enable debug info generation you need to supply -g at compile time. If any of the input files at link time were built with debug info generation enabled, the link will enable debug info generation as well. Any elaborate debug info settings, like the dwarf level -gdwarf-5, need to be explicitly repeated on the linker command line, and mixing different settings in different translation units is discouraged. ๐Ÿ’œ

If LTO encounters objects with C linkage declared with incompatible types in separate translation units that are being linked together (undefined behavior according to ISO C99 6.2.7), a non-fatal diagnostic may be issued. The behavior is still undefined at run time. Similar diagnostics may be raised for other languages. โš ๏ธ

Another feature of LTO is that you can apply interprocedural optimizations on files written in different languages:

gcc -c -flto foo.c
g++ -c -flto bar.cc
gfortran -c -flto baz.f90
g++ -o myprog -flto -O3 foo.o bar.o baz.o -lgfortran

Notice that the final link is done with g++ to get the C++ runtime libraries, and -lgfortran is added to get the Fortran runtime libraries. In general, when mixing languages in LTO mode, you should use the same link command options as you would when mixing languages in a regular (non-LTO) compilation. ๐ŸŒˆ

If object files containing GIMPLE bytecode are stored in a library archive, say libfoo.a, you can extract and use them in an LTO link if you're using a linker with plugin support. To create static libraries suitable for LTO, use gcc-ar and gcc-ranlib instead of ar and ranlib, and to show the symbols of object files with GIMPLE bytecode, use gcc-nm. Those commands require that ar, ranlib and nm have been compiled with plugin support. At link time, use the flag -fuse-linker-plugin to make sure the library participates in the LTO optimization process:

gcc -o myprog -O2 -flto -fuse-linker-plugin a.o b.o -lfoo

With the linker plugin enabled, the linker extracts the needed GIMPLE files from libfoo.a and passes them on to the running GCC to make them part of the aggregated GIMPLE image to be optimized. ๐Ÿฆ‹

If you're not using a linker with plugin support, and/or you don't enable the linker plugin, then the objects inside libfoo.a are extracted and linked as usual, but they do not participate in the LTO optimization process. To make a static library suitable for both LTO optimization and usual linkage, compile its object files with -flto -ffat-lto-objects. ๐ŸŽ€

Link-time optimizations don't require the presence of the whole program to operate. If the program doesn't require any symbols to be exported, you can combine -flto and -fwhole-program to let the interprocedural optimizers use more aggressive assumptions, which may lead to better optimization opportunities. Using -fwhole-program isn't needed when the linker plugin is active (see -fuse-linker-plugin). โœจ

The current implementation of LTO makes no attempt to generate bytecode that is portable between different types of hosts. The bytecode files are versioned and there's a strict version check, so bytecode files generated by one version of GCC do not work with an older or newer version of GCC. ๐Ÿซ 

Link-time optimization does not work well with generation of debugging information on systems other than those using a combination of ELF and DWARF. ๐ŸŒธ

If you pass the optional n, the link-time optimization and code generation run in parallel with n jobs, using an installed make program. The MAKE environment variable can be set to override which program gets used. ๐Ÿงš

You can also say -flto=jobserver to let GNU make's job server decide how many parallel jobs to run. This is super useful when the Makefile calling GCC is already running in parallel ๐Ÿ”ฅ. You have to put a + in front of the command recipe in the parent Makefile for this to work. This option probably only works if MAKE is GNU make. Even without an option value, GCC will try to auto-detect a running GNU make job server, so it's kind of doing the work for you ๐Ÿ’….

Use -flto=auto if you want GNU make's job server when it's available. If it isn't, GCC falls back to auto-detecting how many CPU threads your system has โœจ.


  • -flto-partition=alg โ€” Picks the partitioning algorithm the link-time optimizer uses. The options are 1to1, which mirrors your original source files; balanced, which makes equally sized chunks whenever it can (this is the default ๐ŸŽ€); max, which makes a new partition for every symbol where possible; and cache, which balances chunk sizes while keeping related symbols together so incremental LTO caches better ๐Ÿฆ‹. Choosing none turns partitioning and streaming off completely ๐Ÿ™ˆ. 1to1 can be used as a workaround for various code ordering issues, but max is intended for internal testing only, so don't go there in real life ๐Ÿ’€. The value one means exactly one partition gets used, and none skips partitioning and runs the link-time optimization step straight from the WPA phase.
  • -flto-incremental=path โ€” Turns on incremental LTO and stores its cache in the given directory, which has to already exist ๐ŸŒธ. This can seriously shorten your edit-compile cycles with LTO ๐Ÿ˜ญ.
When you use LTO (-flto), the output of the translation units inside LTO gets cached. Cached translation units are likely to show up again when you recompile after small code changes, so your recompile times go down ๐Ÿซถ.

Multiple GCC instances can share the same cache in parallel, so no fighting over it ๐Ÿ’œ.

  • -flto-incremental-cache-size=n โ€” Sets how many cache entries incremental LTO keeps before it prunes the old ones. This is a soft limit, so you might briefly have more entries than that ๐Ÿงน.
  • -flto-compression-level=n โ€” Controls how hard the intermediate language written to LTO object files gets compressed. It only matters together with LTO mode (-flto) ๐Ÿ—œ๏ธ. GCC currently supports two LTO compression algorithms. For zstd, valid values run from 0 (no compression) to 19 (maximum compression), while zlib goes from 0 to 9. Anything outside that range gets clamped to the minimum or maximum of whatever's supported. If you don't give this option, GCC uses a default balanced compression setting ๐ŸŒˆ.
  • -fuse-linker-plugin โ€” Turns on the linker plugin during link-time optimization ๐Ÿ”Œ. This relies on plugin support in the linker, which you'll find in gold or in GNU ld 2.21 or newer โœจ.

This option lets GCC pull object files with GIMPLE bytecode out of library archives. That's a big glow-up for optimization quality, since it exposes more code to the link-time optimizer ๐Ÿฆ‹. The plugin also shares which symbols can be accessed externally (from non-LTO objects or during dynamic linking). The code quality gains for binaries (and shared libraries that use hidden visibility) are similar to -fwhole-program. Check out -flto for a description of what this flag does and how to use it ๐Ÿ‘‘.

This option is enabled by default when LTO support is enabled in GCC and GCC was configured for a linker that supports plugins (GNU ld 2.21 or newer, or gold). Main character energy ๐Ÿ’….

  • -ffat-lto-objects โ€” Fat LTO objects are object files that hold both the intermediate language and the object code ๐Ÿ“. That means they work for both LTO linking and normal linking. This option only does anything when you're compiling with -flto, and it's ignored at link time ๐Ÿซ .
  • -fno-fat-lto-objects โ€” Compiles faster than plain LTO, but it needs your whole toolchain to be LTO-aware ๐Ÿ› ๏ธ. It needs a linker with linker plugin support for basic functionality. On top of that, nm, ar and ranlib need to support linker plugins for a full-featured build environment (one that can build static libraries and so on). GCC provides the gcc-ar, gcc-nm and gcc-ranlib wrappers to pass the right options to these tools. Without fat LTO, your makefiles need to be modified to use them ๐Ÿ™ƒ.

Heads up: modern binutils have a plugin auto-load mechanism. Installing the linker plugin into $libdir/bfd-plugins has the same effect as using the command wrappers (gcc-ar, gcc-nm and gcc-ranlib) ๐Ÿงš.

The default is -fno-fat-lto-objects on targets with linker plugin support โœจ.


  • -fcompare-elim โ€” After register allocation and post-register allocation instruction splitting, GCC looks for arithmetic instructions that compute processor flags the way a comparison would. If it can, it removes the explicit comparison entirely ๐Ÿงผ. This pass only applies to certain targets that can't explicitly represent the comparison before register allocation is done ๐ŸŽฏ.

Enabled at levels -O1, -O2, -O3, -Os ๐Ÿ’–.

  • -ffold-mem-offsets, -fno-fold-mem-offsets โ€” Tries to get rid of add instructions by folding them into memory loads and stores ๐Ÿซถ.

Enabled at levels -O2, -O3 ๐ŸŒธ.

NOTE: On Debian/Ubuntu this is disabled by default on m68k-linux-gnu ๐Ÿ™ˆ.
  • -fcprop-registers โ€” After register allocation and post-register allocation instruction splitting, this does a copy-propagation pass to reduce scheduling dependencies and occasionally eliminate the copy ๐Ÿ’.

Enabled at levels -O1, -O2, -O3, -Os ๐Ÿ’œ.

  • -fprofile-correction โ€” Profiles collected from an instrumented binary for multi-threaded programs can come out inconsistent because counter updates got missed ๐Ÿ˜ตโ€๐Ÿ’ซ. When you use this option, GCC uses heuristics to correct or smooth out those inconsistencies. By default, GCC throws an error message when it detects an inconsistent profile ๐Ÿšจ.

This option is enabled by -fauto-profile ๐ŸŽ€.

  • -fprofile-partial-training โ€” With -fprofile-use, every part of the program that wasn't executed during the train run gets optimized aggressively for size instead of speed ๐Ÿ“. Sometimes it isn't practical to train every possible hot path (say your program has functions specific to some hardware, and training can't cover every hardware configuration it runs on ๐Ÿ–ฅ๏ธ). With -fprofile-partial-training, profile feedback gets ignored for all functions that weren't executed during the train run. Those functions get optimized as if they were compiled without profile feedback ๐Ÿงƒ. This gives better performance when the train run isn't representative, but it also makes the code significantly bigger ๐Ÿ“ฆ.
  • -fprofile-use, -fprofile-use=path โ€” Turns on profile feedback-directed optimizations, plus the following optimizations, many of which are only really profitable when profile feedback is available ๐Ÿ“ˆ:
-fbranch-probabilities  -fprofile-values -funroll-loops
-fpeel-loops  -ftracer  -fvpt -finline-functions  -fipa-cp
-fipa-cp-clone  -fipa-bit-cp -fpredictive-commoning  -fsplit-loops
-funswitch-loops -fgcse-after-reload  -ftree-loop-vectorize
-ftree-slp-vectorize -fvect-cost-model=dynamic
-ftree-loop-distribute-patterns -fprofile-reorder-functions

Before you can use this option, you have to generate profiling information first ๐Ÿ“.

By default, GCC emits an error message if the feedback profiles don't match the source code. You can turn that error into a warning with -Wno-error=coverage-mismatch. Just know this might give you poorly optimized code ๐Ÿ˜ฌ. Also by default, GCC emits a warning message if the feedback profiles don't exist at all (see -Wmissing-profile) ๐Ÿ‘€.

If you give a path, GCC looks there to find the profile feedback data files. See -fprofile-dir ๐Ÿ“‚.

  • -fauto-profile, -fauto-profile=path โ€” Turns on sampling-based feedback-directed optimizations, plus the following optimizations, many of which are only really profitable with profile feedback available ๐ŸŽฏ:
-fbranch-probabilities  -fprofile-values -funroll-loops
-fpeel-loops  -ftracer  -fvpt -finline-functions  -fipa-cp
-fipa-cp-clone  -fipa-bit-cp -fpredictive-commoning  -fsplit-loops
-funswitch-loops -fgcse-after-reload  -ftree-loop-vectorize
-ftree-slp-vectorize -fvect-cost-model=dynamic
-ftree-loop-distribute-patterns -fprofile-correction

path is the name of a file containing AutoFDO profile information. If you leave it out, it defaults to fbdata.afdo in the current directory ๐Ÿ“.

Making an AutoFDO profile data file means running your program with the perf utility on a supported GNU/Linux target system ๐Ÿง. For more info, check out <https://perf.wiki.kernel.org/> ๐Ÿ”—.

E.g.

perf record -e br_inst_retired:near_taken -b -o perf.data \
    -- your_program

Then use the create_gcov tool to convert the raw profile data into a format GCC can use ๐Ÿ”„. You also have to hand this tool the unstripped binary for your program. See <https://github.com/google/autofdo> ๐Ÿง .

E.g.

create_gcov --binary=your_program.unstripped --profile=perf.data \
    --gcov=profile.afdo

The following options control how the compiler handles floating-point arithmetic. These options trade speed for correctness, so they all have to be turned on specifically ๐Ÿง.

  • -fexcess-precision=style โ€” Lets you control excess precision on machines where floating-point operations happen in a format with more precision or range than the IEEE standard and interchange floating-point types ๐Ÿค“. A classic example is x87 floating point on x86 processors, which uses an 80-bit representation internally instead of the 64-bit IEEE format ๐Ÿ’ซ. For most programs the excess precision is harmless, but some programs might rely on the C or C++ language standards' rules for handling IEEE values ๐Ÿฅน.

By default, -fexcess-precision=fast is in effect, which means operations may be carried out in a wider precision than the types in the source if that gives faster code. It's unpredictable when rounding to the source types actually happens ๐Ÿ™ˆ When compiling C or C++, if you specify -fexcess-precision=standard, excess precision follows the rules in ISO C99 or C++. Casts and assignments both cause values to be rounded to their semantic types, whereas -ffloat-store only affects assignments ๐Ÿ’… This option is enabled by default for C or C++ if a strict conformance option like -std=c99 or -std=c++17 is used. -ffast-math turns on -fexcess-precision=fast by default no matter whether a strict conformance option is used โœจ If you specify -fexcess-precision=16, constants and the results of expressions with the types _Float16 and __bf16 are computed without excess precision.

-fexcess-precision=standard isn't implemented for languages other than C or C++. On x86, it does nothing if you specify -mfpmath=sse or -mfpmath=sse+387. With the former, IEEE semantics apply without excess precision, and with the latter, rounding is unpredictable ๐Ÿฅบ

  • -ffloat-store โ€” Okay so this one tells GCC not to keep floating-point variables in registers, and it also blocks other options that might change whether a float gets pulled from a register or from memory ๐Ÿงš It's mostly been absorbed by -fexcess-precision=standard, which is more general. If you do use -ffloat-store, you might need to rewrite your program so intermediate results go into temporary variables explicitly, because -ffloat-store only handles rounding to IEEE format on assignments and not on casts the way -fexcess-precision=standard does ๐Ÿ’€
  • -ffast-math โ€” This one is a whole vibe ๐Ÿ”ฅ It sets -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range and -fexcess-precision=fast all at once. It also causes the preprocessor macro __FAST_MATH__ to be defined ๐ŸŒธ

This option isn't turned on by any -O option besides -Ofast, since it can give incorrect output for programs that depend on an exact implementation of IEEE or ISO rules and specifications for math functions. It may, however, make faster code for programs that don't need those guarantees ๐Ÿฆ‹

  • -fno-math-errno โ€” Don't set errno after calling math functions that run as a single instruction, like sqrt. A program that relies on IEEE exceptions for math error handling might want this flag for speed while keeping IEEE arithmetic compatibility ๐Ÿซถ

Just like before, this isn't turned on by any -O option besides -Ofast, for the same reason: it can give incorrect output for programs that depend on an exact implementation of IEEE or ISO math rules. It might yield faster code for programs that don't need those guarantees.

The default is -fmath-errno.

On Darwin systems, the math library never sets errno. So there's no reason for the compiler to consider that it might, and -fno-math-errno is the default there ๐Ÿ“

  • -funsafe-math-optimizations โ€” Allows optimizations for floating-point arithmetic that (a) assume arguments and results are valid and (b) may violate IEEE or ANSI standards. Used at link time, it may include libraries or startup files that change the default FPU control word or make other similar optimizations ๐Ÿ˜ญ

Same deal as before: this isn't turned on by any -O option besides -Ofast, because it can produce incorrect output for programs that depend on an exact implementation of IEEE or ISO rules and specifications for math functions. It may still yield faster code for programs that don't need those guarantees. It enables -fno-signed-zeros, -fno-trapping-math, -fassociative-math and -freciprocal-math.

The default is -fno-unsafe-math-optimizations.

  • -fassociative-math โ€” Allows re-association of operands in series of floating-point operations. This violates the ISO C and C++ language standard by possibly changing the computation result. โš ๏ธ Heads up: re-ordering may change the sign of zero, ignore NaNs, and inhibit or create underflow or overflow. So it can't be used on code that relies on rounding behaviour like (x + 2**52) - 2**52. It may also reorder floating-point comparisons, so don't use it when ordered comparisons are required ๐Ÿฅ€

This option requires that both -fno-signed-zeros and -fno-trapping-math be in effect. Also, it doesn't make much sense with -frounding-math. For Fortran, the option is automatically enabled when both -fno-signed-zeros and -fno-trapping-math are in effect.

The default is -fno-associative-math.

  • -freciprocal-math โ€” Lets the reciprocal of a value be used instead of dividing by the value, if that enables optimizations. For example, x / y can be replaced with x * (1/y), which is handy if (1/y) is subject to common subexpression elimination. Just know this loses precision and increases the number of flops operating on the value ๐Ÿงฎ

The default is -fno-reciprocal-math.

  • -ffinite-math-only โ€” Allows optimizations for floating-point arithmetic that assume arguments and results are not NaNs or +-Infs โ™พ๏ธ

This one also isn't turned on by any -O option besides -Ofast, since it can give incorrect output for programs that depend on an exact implementation of IEEE or ISO rules and specifications for math functions. It may, however, yield faster code for programs that don't need those guarantees.

The default is -fno-finite-math-only.

  • -fno-signed-zeros โ€” Allows optimizations for floating-point arithmetic that ignore the signedness of zero. IEEE arithmetic specifies the behaviour of distinct +0.0 and -0.0 values, which then prohibits simplifying expressions like x+0.0 or 0.0*x, even with -ffinite-math-only. This option implies that the sign of a zero result isn't significant ๐ŸŒˆ

The default is -fsigned-zeros.

  • -fno-trapping-math โ€” Compiles code assuming that floating-point operations can't generate user-visible traps. These traps include division by zero, overflow, underflow, inexact result and invalid operation. This option requires that -fno-signaling-nans be in effect. Setting it may allow faster code if you rely on "non-stop" IEEE arithmetic, for example โœจ

This option isn't turned on by any -O option besides -Ofast, since it can give incorrect output for programs that depend on an exact implementation of IEEE or ISO rules and specifications for math functions.

The default is -ftrapping-math.

Future versions of GCC may offer finer control of this setting using C99's FENV_ACCESS pragma. This command-line option will be used along with -frounding-math to specify the default state for FENV_ACCESS.
  • -frounding-math โ€” Disables transformations and optimizations that assume the default floating-point rounding behaviour (round-to-nearest). Use this for programs that change the FP rounding mode dynamically, or that may run with a non-default rounding mode ๐ŸŽ€ It disables constant folding of floating-point expressions at compile time, since that may be affected by rounding mode, and it disables arithmetic transformations that are unsafe in the presence of sign-dependent rounding modes.

The default is -fno-rounding-math.

This option is experimental, and it doesn't currently guarantee that all GCC optimizations affected by rounding mode get disabled. Future versions of GCC may offer finer control of this setting using C99's FENV_ACCESS pragma. This command-line option will be used along with -ftrapping-math to specify the default state for FENV_ACCESS.
  • -fsignaling-nans โ€” Compiles code assuming that IEEE signaling NaNs may generate user-visible traps during floating-point operations. Setting this option disables optimizations that may change the number of exceptions visible with signaling NaNs. This option implies -ftrapping-math ๐Ÿšจ

This option causes the preprocessor macro __SUPPORT_SNAN__ to be defined.

The default is -fno-signaling-nans.

This option is experimental, and it doesn't currently guarantee that all GCC optimizations affecting signaling NaN behaviour get disabled.
  • -fno-fp-int-builtin-inexact โ€” Doesn't allow the built-in functions ceil, floor, round and trunc, plus their float and long double variants, to generate code that raises the inexact floating-point exception for noninteger arguments. ISO C99 and C11 let these functions raise the inexact exception, but ISO/IEC TS 18661-1:2014, the C bindings to IEEE 754-2008 as integrated into ISO C23, doesn't allow them to do so ๐Ÿ™…โ€โ™€๏ธ

The default is -ffp-int-builtin-inexact, which allows the exception to be raised, unless C23 or a later C standard is selected. This option does nothing unless -ftrapping-math is in effect.

Even if -fno-fp-int-builtin-inexact is used, if the functions generate a call to a library function, the inexact exception may still be raised if the library implementation doesn't follow TS 18661.

  • -fsingle-precision-constant โ€” Treats floating-point constants as single precision, instead of implicitly converting them to double-precision constants ๐Ÿญ
  • -fcx-limited-range โ€” When enabled, this option says a range reduction step isn't needed when performing complex division. There's also no checking for whether the result of a complex multiplication or division is NaN + I*NaN, and no attempt to rescue the situation in that case ๐Ÿซ  The default is -fno-cx-limited-range, but -ffast-math enables it.

This option controls the default setting of the ISO C99 CX_LIMITED_RANGE pragma. Even so, the option applies to all languages.

  • -fcx-fortran-rules โ€” Complex multiplication and division follow Fortran rules. Range reduction is done as part of complex division, but there's no checking for whether the result of a complex multiplication or division is NaN + I*NaN, and no attempt to rescue the situation in that case ๐Ÿ‘‘

The default is -fno-cx-fortran-rules.

The following options control optimizations that may improve performance, but are not enabled by any -O options. โœจ This section includes experimental options that may produce broken code, so ๐Ÿ‘€ tread carefully bestie ๐Ÿ’€

  • -fbranch-probabilities ๐ŸŒธ โ€” okay so after you run a program built with -fprofile-arcs, you compile it again with -fbranch-probabilities and the optimizations get smarter about how many times each branch was actually taken. When the -fprofile-arcs program exits, it saves arc execution counts to a file called sourcename.gcda for each source file. That data is super dependent on the structure of the generated code, so you must use the same source code and the same optimization options for both compilations. See the file naming details under -fprofile-arcs.
    • With -fbranch-probabilities, GCC puts a REG_BR_PROB note on each JUMP_INSN and CALL_INSN ๐Ÿง  These can be used to improve optimization. Right now they're only used in one place: reorg.cc. Instead of guessing which path a branch is most likely to take, the REG_BR_PROB values are used to exactly figure out which path gets taken more often. Iconic.
    • Enabled by -fprofile-use and -fauto-profile โœจ
  • -fprofile-values ๐Ÿฆ‹ โ€” if you combine it with -fprofile-arcs, it adds code so data about the values of expressions in your program gets gathered.
    • With -fbranch-probabilities, it reads back that value-profiling data and uses it for optimizations.
    • Enabled by -fprofile-generate, -fprofile-use, and -fauto-profile ๐Ÿ’…
  • -fprofile-reorder-functions ๐Ÿš€ โ€” function reordering based on profile instrumentation records the first time each function runs, then orders those functions in ascending order. The goal is faster program startup, since the text segments load more efficiently.
    • Enabled with -fprofile-use ๐Ÿซถ
  • -fvpt ๐Ÿ”ฎ โ€” if combined with -fprofile-arcs, this tells the compiler to add code that gathers info about the values of expressions.
    • With -fbranch-probabilities, it reads back that data and actually performs optimizations based on it. Right now that includes specializing division operations using what it knows about the value of the denominator. Main character energy ๐Ÿ‘‘
    • Enabled with -fprofile-use and -fauto-profile
  • -frename-registers ๐ŸŽ€ โ€” attempts to avoid false dependencies in scheduled code by using registers left over after register allocation. This is most helpful for processors with lots of registers.
    • > โš ๏ธ Depending on the debug information format the target uses, this can make debugging impossible, since variables no longer stay in a "home register" ๐Ÿ˜ญ
    • Enabled by default with -funroll-loops
  • -fschedule-fusion ๐Ÿ“ โ€” a target dependent pass over the instruction stream that schedules instructions of the same type together, since the target machine can run them more efficiently when they sit next to each other in the instruction flow.
    • Enabled at levels -O2, -O3, -Os
  • -ftracer ๐ŸŒˆ โ€” performs tail duplication to enlarge superblock size. This simplifies the control flow of the function so other optimizations can do a better job.
    • Enabled by -fprofile-use and -fauto-profile
  • -funroll-loops ๐Ÿ” โ€” unrolls loops whose number of iterations can be determined at compile time or when the loop is entered. -funroll-loops implies -frerun-cse-after-loop, -fweb and -frename-registers. It also turns on complete loop peeling, which means fully removing loops with a small constant number of iterations.
    • > โš ๏ธ This option makes your code bigger, and it may or may not make it run faster. Vibe check: it's a gamble ๐ŸŽฒ
    • Enabled by -fprofile-use and -fauto-profile
  • -funroll-all-loops ๐Ÿ’€ โ€” unrolls all loops, even if the number of iterations is uncertain when the loop is entered. This usually makes programs run more slowly ๐Ÿ™ˆ -funroll-all-loops implies the same options as -funroll-loops.
  • -fpeel-loops ๐Ÿง โ€” peels loops that don't roll much, based on enough info from profile feedback or static analysis. It also turns on complete loop peeling (fully removing loops with a small constant number of iterations).
    • Enabled by -O3, -fprofile-use, and -fauto-profile
  • -fmalloc-dce ๐Ÿ—‘๏ธ โ€” controls whether "malloc" (and its variants like "calloc" or "strdup") can be optimized away, as long as its return value is only used as a parameter of a "free" call or compared with "NULL". With -fmalloc-dce=1, only calls to "free" are allowed. With -fmalloc-dce=2, comparisons with the "NULL" pointer are also considered safe to remove.
    • The default is -fmalloc-dce=2. See also -fallocation-dce โœจ
  • -fmove-loop-invariants ๐ŸŒท โ€” enables the loop invariant motion pass in the RTL loop optimizer.
    • Enabled at level -O1 and higher, except for -Og
  • -fmove-loop-stores ๐Ÿ“ฆ โ€” enables the loop store motion pass in the GIMPLE loop optimizer. This moves invariant stores to after the end of the loop, in exchange for carrying the stored value in a register across the iteration.
    • Note: for this option to have an effect, -ftree-loop-im has to be enabled as well ๐Ÿซ 
    • Enabled at level -O1 and higher, except for -Og
  • -fsplit-loops โœ‚๏ธ โ€” splits a loop into two if it contains a condition that's always true for one side of the iteration space and false for the other.
    • Enabled by -fprofile-use and -fauto-profile
  • -funswitch-loops ๐Ÿ”€ โ€” moves branches with loop invariant conditions out of the loop, with duplicates of the loop on both branches (modified according to the result of the condition).
    • Enabled by -fprofile-use and -fauto-profile
  • -fversion-loops-for-strides ๐Ÿงต โ€” if a loop iterates over an array with a variable stride, GCC creates another version of the loop that assumes the stride is always one. For example:
for (int i = 0; i < n; ++i)
  x[i * stride] = ...;

becomes:

if (stride == 1)
  for (int i = 0; i < n; ++i)
    x[i] = ...;
else
  for (int i = 0; i < n; ++i)
    x[i * stride] = ...;

This is especially useful for assumed-shape arrays in Fortran, where (for example) it allows better vectorization by assuming contiguous accesses ๐Ÿซถ This flag is enabled by default at -O3. It's also enabled by -fprofile-use and -fauto-profile.


  • -ffunction-sections, -fdata-sections ๐Ÿ—‚๏ธ โ€” places each function or data item into its own section in the output file, if the target supports arbitrary sections. The name of the function or data item determines the section's name in the output file.
    • Use these on systems where the linker can perform optimizations to improve locality of reference in the instruction space. Most systems using the ELF object format have linkers with such optimizations. On AIX, the linker rearranges sections (CSECTs) based on the call graph. The performance impact varies.
    • Together with linker garbage collection (the linker --gc-sections option), these options may lead to smaller statically-linked executables (after stripping) ๐ŸŒฑ
    • On ELF/DWARF systems, these options do not degrade the quality of the debug information. There could be issues with other object file or debug info formats.
    • > โš ๏ธ Only use these options when there are significant benefits from doing so. When you specify them, the assembler and linker create larger object and executable files and are also slower. They also affect code generation: they prevent optimizations by the compiler and assembler that use relative locations inside a translation unit, since those locations aren't known until link time. An example of such an optimization is relaxing calls to short call instructions. Be careful, bestie ๐Ÿฅบ
  • -fstdarg-opt ๐Ÿช„ โ€” optimizes the prologue of variadic argument functions with respect to how those arguments are used.
  • -fsection-anchors โš“ โ€” tries to reduce the number of symbolic address calculations by using shared "anchor" symbols to address nearby objects. This transformation can help reduce the number of GOT entries and GOT accesses on some targets.
    • For example, the implementation of the following function foo:
static int a, b, c;
int foo (void) { return a + b + c; }

usually calculates the addresses of all three variables. But if you compile it with -fsection-anchors, it accesses the variables from a common anchor point instead. The effect is similar to this pseudocode (which isn't valid C) ๐Ÿค“:

int foo (void)
{
  register int *xr = &x;
  return xr[&a - &x] + xr[&b - &x] + xr[&c - &x];
}

Not all targets support this option ๐Ÿ’…

  • -fzero-call-used-regs=choice ๐Ÿ›ก๏ธ โ€” zeros the call-used registers at function return to boost program security, either by mitigating Return-Oriented Programming (ROP) attacks or by preventing information leakage through registers.
    • The possible values of choice are the same as for the "zero_call_used_regs" attribute. The default is skip.
    • You can control this behavior for a specific function by using the function attribute "zero_call_used_regs" ๐Ÿ”
  • --param name=value ๐ŸŽ›๏ธ โ€” okay so GCC uses various constants to control how much optimization gets done. For example, GCC won't inline functions that contain more than a certain number of instructions. You can control some of these constants on the command line using the --param option.
    • > โš ๏ธ The names of specific parameters, and what the values mean, are tied to the compiler's internals. They're subject to change without notice in future releases, so don't get too attached ๐Ÿ˜ญ
    • To get the minimal, maximal and default values of a parameter, use --help=param -Q.
    • In each case, the value is an integer. The following choices of name are recognized for all targets:
      • phiopt-factor-max-stmts-live โ€” when factoring statements out of an if/then/else, this is the max number of statements after the defining statement that are allowed to extend the lifetime of a name ๐ŸŒ™
      • predictable-branch-outcome โ€” when a branch is predicted to be taken with a probability lower than this threshold (in percent), it's considered well predictable โœจ
  • max-rtl-if-conversion-insns โ€” okay so RTL if-conversion ๐Ÿ’… tries to yeet conditional branches around a block and swap them for conditionally executed instructions โœจ. This param sets the max number of instructions in a block that get considered for if-conversion. GCC also runs its own heuristics to figure out whether if-conversion is actually worth it ๐Ÿง .
  • file-cache-files โ€” the max number of files kept in the file cache ๐Ÿ“. The file cache is what GCC uses to print source lines in diagnostics and to run some source checks, like -Wmisleading-indentation ๐Ÿ‘€.
  • file-cache-lines โ€” the max number of lines that get indexed into the file cache ๐Ÿงพ. When this is 0, it gets sized automatically ๐Ÿช„. Same deal as above: the file cache powers the source lines in diagnostics and checks like -Wmisleading-indentation ๐Ÿ’–.
  • max-rtl-if-conversion-predictable-cost โ€” same RTL if-conversion idea, where it tries to remove conditional branches and replace them with conditionally executed instructions ๐ŸŒธ. These params cap the cost of the sequence if-conversion would generate. There are two versions, depending on whether the branch is statically determined to be predictable or not ๐Ÿ”ฎ. The units match GCC's internal seq_cost metric ๐Ÿ“. GCC tries to pick a sensible default using the BRANCH_COST target macro ๐ŸŽฏ.
  • max-crossjump-edges โ€” the max number of incoming edges to consider for cross-jumping ๐Ÿฆ‹. Heads up, the algorithm behind -fcrossjumping is O(N^2) in the number of incoming edges to each block ๐Ÿ˜ญ. Cranking this up means more aggressive optimization, but compile times go up for probably a small win in executable size ๐Ÿข.
  • min-crossjump-insns โ€” the minimum number of instructions that have to match at the end of two blocks before cross-jumping happens on them ๐ŸŽ€. This value gets ignored when every instruction in the block being cross-jumped from is already matched โœจ.
  • max-grow-copy-bb-insns โ€” the max code size expansion factor when copying basic blocks instead of jumping ๐Ÿงฉ. The expansion is measured relative to a jump instruction ๐Ÿฆ˜.
  • max-goto-duplication-insns โ€” the max number of instructions that can be duplicated into a block that jumps to a computed goto ๐ŸŒˆ. To dodge O(N^2) behaviour in a bunch of passes, GCC factors computed gotos early on and unfactors them as late as possible ๐Ÿซ . Only computed jumps at the end of a basic block with no more than max-goto-duplication-insns get unfactored ๐Ÿ™ˆ.
  • max-delay-slot-insn-search โ€” the max number of instructions to look at when hunting for something to fill a delay slot ๐Ÿ”. Past this arbitrary number, the time saved by filling the slot is basically nothing, so GCC stops searching ๐Ÿ’€. Higher values mean more aggressive optimization and longer compile times, for probably a small gain in execution time ๐Ÿƒโ€โ™€๏ธ.
  • max-delay-slot-live-search โ€” when filling delay slots, the max number of instructions to check while searching for a block with valid live register information ๐Ÿ”Ž. Raising this arbitrarily chosen number means more aggressive optimization but slower compiles ๐ŸŒ. This param should get removed once the delay slot code is rewritten to maintain the control-flow graph ๐Ÿšง.
  • max-gcse-memory โ€” the approximate max amount of memory, in "kB", that can be allocated for the global common subexpression elimination optimization ๐Ÿง . If the optimization would need more memory than this, it just doesn't happen ๐Ÿ™…โ€โ™€๏ธ.
  • max-gcse-insertion-ratio โ€” if the ratio of expression insertions to deletions goes above this value for any expression, RTL PRE inserts or removes the expression instead ๐Ÿ”„. The catch is that it leaves partially redundant computations sitting in the instruction stream ๐Ÿฅฒ.
  • max-pending-list-length โ€” the max number of pending dependencies the scheduler allows before it flushes the current state and starts fresh ๐Ÿงน. Big functions with few branches or calls can build up huge lists that eat memory and resources for no reason ๐Ÿซง.
  • max-modulo-backtrack-attempts โ€” the max number of backtrack attempts the scheduler makes when modulo scheduling a loop ๐Ÿ”. Bigger values can blow up compilation time exponentially ๐Ÿ“ˆ๐Ÿ’€.
  • max-inline-functions-called-once-loop-depth โ€” the max loop depth of a call that the inline heuristics will still consider, for the heuristics that try to inline every function called once ๐ŸŒ€.
  • max-inline-functions-called-once-insns โ€” the max estimated size of functions produced while inlining functions called once ๐Ÿ“.
  • max-inline-insns-single โ€” a bunch of params control GCC's tree inliner, and this one sets the max number of instructions (counted in GCC's internal representation) in a single function that the tree inliner will consider for inlining ๐Ÿ’ซ. It only affects functions declared inline and methods implemented in a class declaration (C++) ๐Ÿ’œ.
  • max-inline-insns-auto โ€” when you use -finline-functions (which is included in -O3), a whole lot of functions that wouldn't normally get considered for inlining get checked out anyway ๐Ÿ‘€. For those functions, a different, more restrictive limit applies than for functions declared inline ๐Ÿ™ƒ. You can set it with --param max-inline-insns-auto โœจ.
  • max-inline-insns-small โ€” the bound applied to calls that count as relevant under -finline-small-functions ๐Ÿฆ‹.
  • max-inline-insns-size โ€” the bound applied to calls that are optimized for size ๐Ÿ“ฆ. A little growth can be worth it, since it can expose optimization opportunities that inlining unlocks ๐ŸŒฑ.
  • uninlined-function-insns โ€” the number of instructions the inliner counts as function overhead, like the function prologue and epilogue ๐Ÿงฎ.
  • uninlined-function-time โ€” the extra time the inliner counts as function overhead, like the time it takes to run the prologue and epilogue โฑ๏ธ.
  • inline-heuristics-hint-percent โ€” the scale, in percent, applied to inline-insns-single, inline-insns-single-O2, and inline-insns-auto when the inline heuristics hint that inlining is super profitable (it'll enable later optimizations) ๐Ÿ’…๐Ÿ”ฅ.
  • uninlined-thunk-insns, uninlined-thunk-time โ€” same as --param uninlined-function-insns and --param uninlined-function-time, but these apply to function thunks ๐Ÿงต.
  • inline-min-speedup โ€” when the estimated performance improvement of caller + callee runtime goes over this threshold (in percent), the function can be inlined no matter what the limits on --param max-inline-insns-single and --param max-inline-insns-auto say ๐Ÿš€๐Ÿ‘‘.
  • large-function-insns โ€” the limit that defines really large functions ๐Ÿ˜. For functions bigger than this after inlining, inlining gets constrained by --param large-function-growth ๐Ÿ”’. This param mainly exists to avoid extreme compile times caused by the non-linear algorithms the back end uses ๐Ÿซ .
  • large-function-growth โ€” the max growth of large functions caused by inlining, in percent ๐Ÿ“ˆ. For example, a value of 100 limits large function growth to 2.0 times the original size โœŒ๏ธ.
  • large-unit-insns โ€” the limit that defines a large translation unit ๐Ÿ—‚๏ธ. Growth from inlining into units bigger than this is limited by --param inline-unit-growth ๐Ÿงฑ. For small units that might be too tight ๐Ÿฅบ. Here's the example: a unit with function A (inline) and B, which just calls A three times. If B is small compared to A, the unit growth is 300% and yet that inlining is totally sane ๐Ÿ’…. But for really big units full of small inlineable functions, the overall unit growth limit is needed to stop exponential code size explosions ๐Ÿ’ฅ. So for smaller units, the size gets bumped up to --param large-unit-insns before --param inline-unit-growth is applied ๐Ÿช„.
  • lazy-modules โ€” the max number of C++ module files that can be open at once when lazy loading ๐Ÿ“‚.
  • inline-unit-growth โ€” the max overall growth of the compilation unit caused by inlining ๐ŸŒฟ. For example, a value of 20 limits unit growth to 1.2 times the original size ๐Ÿซง. Cold functions (marked cold with an attribute, or by profile feedback) don't count toward the unit size ๐Ÿฅถ.
  • ipa-cp-unit-growth โ€” the max overall growth of the compilation unit caused by interprocedural constant propagation ๐ŸงŠ. For example, a value of 10 limits unit growth to 1.1 times the original size ๐ŸŒŸ.
  • ipa-cp-large-unit-insns โ€” the size of a translation unit that the IPA-CP pass considers large ๐Ÿ“.
  • large-stack-frame โ€” the limit that defines large stack frames ๐Ÿฅž. While inlining, the algorithm tries not to grow past this limit too much ๐Ÿ™.
  • large-stack-frame-growth โ€” the max growth of large stack frames caused by inlining, in percent ๐Ÿ—๏ธ. For example, a value of 1000 limits large stack frame growth to 11 times the original size ๐Ÿคฏ.
  • max-inline-insns-recursive, max-inline-insns-recursive-auto โ€” the max number of instructions an out-of-line copy of a self-recursive inline function can grow into by doing recursive inlining ๐Ÿ”„๐ŸŒธ.

--param max-inline-insns-recursive applies to functions declared inline. For functions not declared inline, recursive inlining only happens when -finline-functions (included in -O3) is enabled, and then --param max-inline-insns-recursive-auto applies instead ๐Ÿ’œ.

  • max-inline-recursive-depth, max-inline-recursive-depth-auto โ€” the max recursion depth used for recursive inlining ๐Ÿชžโœจ.

Okay so --param max-inline-recursive-depth only handles functions you've marked inline ๐Ÿ’…. For functions that aren't declared inline, recursive inlining only kicks in when -finline-functions is on (it's part of -O3) โœจ. In that case, --param max-inline-recursive-depth-auto takes over instead ๐Ÿฆ‹.

  • min-inline-recursive-probability โ€” Recursive inlining is only worth it for functions that recurse deeply on average ๐ŸŒธ. For functions with shallow recursion, it can backfire by bloating the prologue or making the function body harder for other optimizers to handle ๐Ÿ™ˆ. When profile feedback is available (see -fprofile-generate), the real recursion depth can be guessed from how likely a function is to recurse through a given call expression. This parameter only allows inlining for call expressions whose probability is above the threshold, in percents.
  • early-inlining-insns โ€” Controls how much growth the early inliner is allowed to cause. Basically, it lets the early inliner do more inlining for code with a large abstraction penalty ๐Ÿ”ฅ.
  • max-early-inliner-iterations โ€” Caps how many iterations the early inliner runs. This bounds how many nested indirect calls it can resolve, and deeper chains get handled by late inlining instead ๐Ÿ’œ.
  • comdat-sharing-probability โ€” The probability (in percent) that C++ inline functions with comdat visibility get shared across multiple compilation units โœจ.
  • modref-max-bases, modref-max-refs, modref-max-accesses โ€” The maximum number of base pointers, references and accesses that mod/ref analysis stores for a single function ๐ŸŽ€.
  • modref-max-tests โ€” The maximum number of tests the alias oracle can run to tell memory locations apart using mod/ref information. This should be bigger than --param modref-max-bases and --param modref-max-refs ๐Ÿงš.
  • modref-max-depth โ€” The maximum depth of the DFS walk used by modref escape analysis. Set it to 0 and the analysis is switched off completely ๐Ÿ’€.
  • modref-max-escape-points โ€” The maximum number of escape points modref tracks per SSA-name ๐Ÿ“.
  • modref-max-adjustments โ€” The maximum amount the access range can be enlarged during modref dataflow analysis ๐ŸŒˆ.
  • profile-func-internal-id โ€” Controls whether the function's internal id is used when looking things up in the profile database. If the value is 0, the compiler uses an id based on the function's assembler name and filename instead. That makes old profile data more forgiving of source changes, like function reordering ๐Ÿซถ.
  • min-vect-loop-bound โ€” The minimum number of iterations below which loops are not vectorized when -ftree-vectorize is used. The number of iterations after vectorization has to be greater than this value for vectorization to happen ๐Ÿ‘‘.
  • gcse-cost-distance-ratio โ€” A scaling factor for the maximum distance an expression can travel during GCSE optimizations. It's only supported in the code hoisting pass ๐Ÿฆ‹. The bigger the ratio, the more aggressively simple expressions (ones costing less than gcse-unrestricted-cost) get hoisted. Setting it to 0 disables hoisting of simple expressions ๐Ÿ™…โ€โ™€๏ธ.
  • gcse-unrestricted-cost โ€” The cost, roughly measured as a single typical machine instruction, at which GCSE stops constraining how far an expression can travel. This is also only supported in the code hoisting pass ๐Ÿ’…. The lower the cost, the more aggressive the hoisting. Setting it to 0 lets every expression travel unrestricted distances โœจ.
  • max-hoist-depth โ€” The depth of search in the dominator tree for expressions to hoist. This keeps the hoisting algorithm from going quadratic. A value of 0 means no limit on the search, but it may slow down compilation of huge functions ๐Ÿข.
  • max-tail-merge-comparisons โ€” The maximum number of similar bbs a bb gets compared with. This keeps tree tail merging from going quadratic ๐ŸŒธ.
  • max-tail-merge-iterations โ€” The maximum number of iterations of the pass over the function. It's there to limit compilation time in tree tail merging โฑ๏ธ.
  • store-merging-allow-unaligned โ€” Lets the store merging pass introduce unaligned stores, but only if that's legal to do ๐ŸŽ€.
  • max-stores-to-merge โ€” The maximum number of stores the store merging pass will try to merge into wider stores ๐Ÿง.
  • max-store-chains-to-track โ€” The maximum number of store chains tracked at the same time while trying to merge them into wider stores in the store merging pass ๐Ÿฆ„.
  • max-stores-to-track โ€” The maximum number of stores tracked at the same time while trying to merge them into wider stores in the store merging pass ๐Ÿ’–.
  • max-unrolled-insns โ€” The maximum number of instructions a loop can have and still be unrolled. If a loop does get unrolled, this parameter also decides how many times the loop code is unrolled ๐Ÿ”.
  • max-average-unrolled-insns โ€” The maximum number of instructions, biased by how likely they are to execute, that a loop can have and still be unrolled. If it gets unrolled, this parameter also decides how many times the loop code is unrolled ๐ŸŒˆ.
  • max-unroll-times โ€” The maximum number of unrollings of a single loop ๐Ÿ”ข.
  • max-peeled-insns โ€” The maximum number of instructions a loop can have and still be peeled. If a loop is peeled, this parameter also decides how many times the loop code is peeled ๐Ÿ‘.
  • max-peel-times โ€” The maximum number of peelings of a single loop ๐Ÿ‘.
  • max-peel-branches โ€” The maximum number of branches on the hot path through the peeled sequence ๐Ÿ”ฅ.
  • max-completely-peeled-insns โ€” The maximum number of insns in a completely peeled loop โœจ.
  • max-completely-peel-times โ€” The maximum number of iterations a loop can have and still be suitable for complete peeling ๐Ÿงš.
  • max-completely-peel-loop-nest-depth โ€” The maximum depth of a loop nest suitable for complete peeling ๐Ÿช†.
  • max-unswitch-insns โ€” The maximum number of insns in an unswitched loop ๐Ÿ”€.
  • max-unswitch-depth โ€” The maximum depth of a loop nest that can be unswitched ๐ŸŒŠ.
  • lim-expensive โ€” The minimum cost for an expression to count as expensive during loop invariant motion ๐Ÿ’ธ.
  • min-loop-cond-split-prob โ€” When FDO profile information is available, this specifies the minimum probability a semi-invariant condition statement needs before it triggers a loop split ๐Ÿช„.
  • iv-consider-all-candidates-bound โ€” The bound on the number of candidates for induction variables. Below this number, every candidate is considered for each use in induction variable optimizations. Above it, only the most relevant ones get considered, so the time complexity doesn't go quadratic ๐Ÿงฎ.
  • iv-max-considered-uses โ€” Induction variable optimizations give up on any loop that has more induction variable uses than this ๐Ÿ™ƒ.
  • iv-always-prune-cand-set-bound โ€” If the candidate set has fewer members than this value, the compiler always tries to remove unnecessary ivs from the set whenever it adds a new one ๐Ÿงน.
  • avg-loop-niter โ€” The average number of iterations of a loop ๐Ÿ”„.
  • dse-max-object-size โ€” The maximum size (in bytes) of objects that dead store elimination tracks bytewise. Bigger values can mean bigger compilation times ๐Ÿ“.
  • dse-max-alias-queries-per-store โ€” The maximum number of queries into the alias oracle per store. Bigger values mean longer compilation times, but they may remove more dead stores ๐Ÿ—‘๏ธ.
  • scev-max-expr-size โ€” The bound on the size of expressions used in the scalar evolutions analyzer. Large expressions slow the analyzer down ๐ŸŒ.
  • scev-max-expr-complexity โ€” The bound on how complex the expressions in the scalar evolutions analyzer can be. Complex expressions slow the analyzer down too ๐Ÿคฏ.
  • max-tree-if-conversion-phi-args โ€” The maximum number of arguments a PHI can have and still be supported by TREE if conversion, unless the loop is marked with a simd pragma ๐ŸŽ€.
  • vect-max-layout-candidates โ€” The maximum number of possible vector layouts (such as permutations) to consider when optimizing code that is going to be vectorized ๐Ÿงฉ.
  • vect-max-version-for-alignment-checks โ€” The maximum number of run-time checks that can be done when the vectorizer does loop versioning for alignment ๐Ÿ“.
  • vect-max-version-for-alias-checks โ€” The maximum number of run-time checks that can be done when the vectorizer does loop versioning for alias ๐Ÿ”.
  • vect-max-peeling-for-alignment โ€” The maximum number of loop peels the vectorizer can use to improve access alignment. A value of -1 means there's no limit ๐Ÿซง.
  • max-iterations-to-track โ€” The maximum number of loop iterations the brute-force algorithm for analyzing a loop's iteration count will try to evaluate ๐Ÿ•ต๏ธโ€โ™€๏ธ.
  • hot-bb-count-fraction โ€” The denominator n of the fraction 1/n of the maximal execution count of a basic block in the entire program. A basic block needs at least that much execution to be considered hot. The default is 10000, which means a basic block counts as hot if its execution count is greater than 1/10000 of the maximal execution count. A value of 0 means it's never considered hot. Used in non-LTO mode ๐Ÿ”ฅ.
  • hot-bb-count-ws-permille โ€” The number of most-executed permilles, ranging from 0 to 1000, of the profiled execution of the entire program. A basic block's execution count has to be part of that slice to count as hot. The default is 990, which means a basic block is hot if its execution count contributes to the upper 990 permilles, or 99.0%, of the profiled execution of the entire program. A value of 0 means it's never considered hot. Used in LTO mode ๐Ÿ‘‘.
  • hot-bb-frequency-fraction โ€” This one is the denominator n of the fraction 1/n of the entry block's execution frequency that a basic block in a function needs to reach before it counts as hot ๐Ÿ”ฅ. The default is 1000, so a basic block is hot if it runs more often than 1/1000 of the entry block's frequency. Setting it to 0 means nothing is ever considered hot ๐ŸงŠ.
  • unlikely-bb-count-fraction โ€” Here the denominator n is for the fraction 1/n of the total profiled runs of the whole program. If a basic block's execution count falls below that, it's considered unlikely to run ๐Ÿ™ˆ. The default is 20, which means a block is unlikely if it runs in fewer than 1/20 (that's 5%) of the program's runs. Setting it to 0 means every block is always considered unlikely.
  • max-predicted-iterations โ€” The most loop iterations GCC will predict statically โœจ. This comes in clutch when a function has one loop with a known bound and another with an unknown bound. The known count gets predicted correctly, but the unknown one averages out to roughly 10, so the unbounded loop looks artificially cold next to the other one ๐Ÿฅถ.
  • builtin-expect-probability โ€” Controls how likely the expression is to have the specified value ๐ŸŽฏ. It takes a percentage, so anywhere from 0 to 100.
  • builtin-string-cmp-inline-length โ€” The maximum length of a constant string for a builtin string comparison call that's eligible to be inlined ๐Ÿงต.
  • align-threshold โ€” Picks a fraction of the maximum execution frequency of a basic block within a function, and uses it to decide whether that block gets aligned ๐Ÿ“.
  • align-loop-iterations โ€” Any loop that's expected to iterate at least this many times gets aligned ๐ŸŒ€.
  • tracer-dynamic-coverage, tracer-dynamic-coverage-feedback โ€” These cap superblock formation once a given percentage of the executed instructions is covered ๐Ÿง. It stops the code from bloating for no reason. The -feedback version only matters when profile feedback is available. Real profiles are way less balanced than statically estimated ones, so that threshold can be a bit higher ๐Ÿ’….
  • tracer-max-code-growth โ€” Tail duplication stops once code growth hits this percentage ๐Ÿ“ˆ. It's kind of an artificial limit, since most duplicates get cleaned up later in cross jumping, so you can set it much higher than the growth you actually want ๐Ÿคทโ€โ™€๏ธ.
  • tracer-min-branch-ratio โ€” Reverse growth stops when the reverse probability of the best edge drops below this threshold, given as a percentage ๐Ÿ›‘.
  • tracer-min-branch-probability, tracer-min-branch-probability-feedback โ€” Forward growth stops if the best edge's probability is lower than this threshold ๐Ÿšฆ. Same deal as the dynamic coverage pair: tracer-min-branch-probability-feedback is for compiling with profile feedback, and tracer-min-branch-probability is for compiling without it. The feedback one needs to be more conservative (higher) so the tracer actually does its thing ๐ŸŽ€.
  • stack-clash-protection-guard-size โ€” Sets the size of the operating system's stack guard, written as 2 raised to num bytes ๐Ÿ›ก๏ธ. Bigger values can mean fewer explicit probes.
โš ๏ธ If this value is larger than the operating system's guard, your code is left open to stack clash style attacks. Yikes ๐Ÿ˜ญ.
  • stack-clash-protection-probe-interval โ€” Stack clash protection probes the stack as space gets allocated ๐Ÿ”. This param controls the maximum distance between those probes, written as 2 raised to num bytes. Bigger values can cut down on explicit probes too.
โš ๏ธ Same warning here: if the value is larger than the operating system's guard, the code stays vulnerable to stack clash style attacks ๐Ÿ’€.
  • max-cse-path-length โ€” The maximum number of basic blocks on a path that CSE looks at ๐Ÿ›ค๏ธ.
  • max-cse-insns โ€” The maximum number of instructions CSE processes before it flushes ๐Ÿšฟ.
  • ggc-min-expand โ€” GCC has its own garbage collector for managing memory ๐Ÿ—‘๏ธ. This sets the minimum percentage the collector's heap can grow between collections. Tuning it might speed up compilation, but it has zero effect on code generation ๐Ÿ™Œ.

The default is 30% + 70% * (RAM/1GB), with an upper bound of 100% when RAM is at least 1GB. If "getrlimit" is available, "RAM" means the smaller of actual RAM and RLIMIT_DATA or RLIMIT_AS. If GCC can't work out RAM on a platform, it falls back to the lower bound of 30%. Setting this and ggc-min-heapsize both to zero forces a full collection every single chance it gets. That's extremely slow, but it can be useful for debugging ๐Ÿข.

  • ggc-min-heapsize โ€” The minimum heap size the garbage collector lets build up before it starts collecting ๐Ÿงน. The first collection happens once the heap grows by ggc-min-expand% past ggc-min-heapsize. Again, this can speed up compilation but doesn't touch code generation.

The default is the smaller of RAM/8, RLIMIT_RSS, or a limit meant to keep RLIMIT_DATA or RLIMIT_AS from being exceeded. The lower bound is 4096 (four megabytes) and the upper bound is 131072 (128 megabytes). If GCC can't calculate RAM on a platform, the lower bound gets used. Setting this super large effectively turns garbage collection off ๐Ÿšซ. Setting it and ggc-min-expand to zero triggers a full collection at every opportunity.

  • max-reload-search-insns โ€” How far back reload looks for an equivalent register, counted in instructions ๐Ÿ”™. Higher values mean more aggressive optimization, and compilation takes longer for probably slightly better performance ๐Ÿƒโ€โ™€๏ธ.
  • max-cselib-memory-locations โ€” The maximum number of memory locations cselib considers ๐Ÿง . Higher values mean more aggressive optimization, which slows compilation down for probably slightly better performance ๐Ÿ“š.
  • max-sched-ready-insns โ€” The maximum number of ready-to-issue instructions the scheduler looks at any given time during the first scheduling pass ๐Ÿ“‹. Higher values mean more thorough searches, which makes compilation slower for probably very little benefit ๐ŸŒ.
  • max-sched-region-blocks โ€” The maximum number of blocks in a region that can be considered for interblock scheduling ๐Ÿงฉ.
  • max-pipeline-region-blocks โ€” The maximum number of blocks in a region that can be considered for pipelining in the selective scheduler ๐ŸŽข.
  • max-sched-region-insns โ€” The maximum number of insns in a region that can be considered for interblock scheduling ๐Ÿงƒ.
  • max-pipeline-region-insns โ€” The maximum number of insns in a region that can be considered for pipelining in the selective scheduler ๐ŸŒŠ.
  • min-spec-prob โ€” The minimum probability, in percent, of reaching a source block for interblock speculative scheduling ๐ŸŽฒ.
  • max-sched-extend-regions-iters โ€” The maximum number of trips through the CFG when extending regions ๐Ÿ”. Setting this to 0 disables region extensions ๐Ÿ™…โ€โ™€๏ธ.
  • max-sched-insn-conflict-delay โ€” The maximum conflict delay an insn can have and still be considered for speculative motion ๐Ÿ•ฐ๏ธ.
  • sched-spec-prob-cutoff โ€” The minimum probability of speculation success, in percent, needed before speculative insns get scheduled ๐ŸŽฏ.
  • sched-state-edge-prob-cutoff โ€” The minimum probability an edge needs to have before the scheduler saves its state across it ๐Ÿ’พ.
  • sched-mem-true-dep-cost โ€” The minimum distance, in CPU cycles, between a store and a load that target the same memory locations ๐Ÿง โฑ๏ธ.
  • selsched-max-lookahead โ€” The maximum size of the lookahead window in selective scheduling ๐Ÿ‘€. It's the depth of the search for available instructions.
  • selsched-max-sched-times โ€” The maximum number of times an instruction gets scheduled during selective scheduling ๐Ÿ”„. This caps how many iterations the instruction can be pipelined through.
  • selsched-insns-to-rename โ€” The maximum number of best instructions in the ready list that get considered for renaming in the selective scheduler โœ๏ธ.
  • sms-min-sc โ€” The minimum stage count that the swing modulo scheduler generates ๐ŸŽก.
  • max-last-value-rtl โ€” The maximum size, measured in RTLs, that the combiner can record in an expression as the last known value of a pseudo register ๐Ÿ“.
  • max-combine-insns โ€” The maximum number of instructions the RTL combiner tries to combine ๐Ÿงท.
  • max-combine-search-insns โ€” The maximum number of instructions the RTL combiner searches to find the next use of a given register definition ๐Ÿ”Ž. If it hits this limit without finding a use, the combiner stops trying to optimize that definition ๐Ÿ›‘.

Right now this limit only kicks in after certain successful combination attempts, but it could get extended to other cases in the future ๐Ÿ”ฎ.

  • integer-share-limit โ€” Small integer constants can use a shared data structure, which saves memory and speeds things up ๐ŸŽ๏ธ. This sets the maximum value of a shared integer constant.
  • ssp-buffer-size โ€” The minimum size of buffers (arrays, basically) that get stack smashing protection when -fstack-protector is used ๐Ÿ›ก๏ธ.

Before Ubuntu 10.10 this default was "8". Currently it's "4", so more functions get protected by the stack protector ๐Ÿ’–.

  • min-size-for-stack-sharing โ€” ๐Ÿงš The smallest size a variable needs to be before it can join in on stack slot sharing when you're not optimizing. Tiny variables are left out. ๐ŸŒธ
  • max-jump-thread-duplication-stmts โ€” ๐Ÿ’… The max number of statements a block can have when it needs to get duplicated during jump threading. Any bigger and it's a no from the compiler. ๐Ÿ™…โ€โ™€๏ธ
  • max-jump-thread-paths โ€” ๐Ÿฆ‹ The max number of paths the compiler will check while hunting for jump threading chances. When it reaches a block, it only looks at incoming edges if (paths searched so far) ร— (incoming edges) doesn't blow past this limit. Not every edge gets a turn, bestie. ๐Ÿ’œ
  • max-fields-for-field-sensitive โ€” โœจ The max number of fields in a structure that get treated in a field sensitive way during pointer analysis. ๐Ÿซถ
  • prefetch-latency โ€” โฐ An estimate of how many instructions run on average before a prefetch finishes. The distance the prefetch reaches ahead is proportional to this number. Crank it up and you may end up with fewer streams getting prefetched (see simultaneous-prefetches). ๐Ÿ”ฅ
  • simultaneous-prefetches โ€” ๐Ÿƒโ€โ™€๏ธ The max number of prefetches that can run at the same time. ๐ŸŒˆ
  • l1-cache-line-size โ€” ๐Ÿ“ How big one cache line is in the L1 data cache, in bytes. ๐Ÿ“
  • l1-cache-size โ€” ๐ŸŽ€ How big the L1 data cache is, in kilobytes. ๐Ÿง
  • l2-cache-size โ€” ๐ŸŽ€ How big the L2 data cache is, in kilobytes. ๐Ÿ’–
  • prefetch-dynamic-strides โ€” ๐ŸŒธ Decides whether the loop array prefetch pass should slap on software prefetch hints for strides that aren't constant. Sometimes that's a total win, but a non-constant stride makes it hard to tell when the hints are actually worth it, so it's a bit of a gamble. ๐ŸŽฒ

Set it to 1 if you want prefetch hints issued for non-constant strides. Set it to 0 if you only want hints for strides that are known to be constant and below prefetch-minimum-stride. ๐Ÿซถ

  • prefetch-minimum-stride โ€” ๐Ÿฆ„ The smallest constant stride, in bytes, where prefetch hints start kicking in. If the stride is below this threshold, no prefetch hints get issued. ๐Ÿ™ˆ

This one is a big deal for processors with hardware prefetchers, since the hardware and software prefetchers can end up fighting each other. If the hardware prefetchers have a max stride they can handle, put that number here so the software prefetchers don't step on their toes. ๐Ÿ’…

A value of -1 means there's no threshold at all, so prefetch hints can be issued for any constant stride. โœจ

Heads up: this setting only matters for strides that are known and constant. ๐Ÿง

  • destructive-interference-size, constructive-interference-size โ€” ๐Ÿ‘‘ These are the values behind the C++17 variables std::hardware_destructive_interference_size and std::hardware_constructive_interference_size. ๐Ÿคฏ

The destructive interference size is the minimum recommended offset between two independent objects that get accessed concurrently. The constructive interference size is the maximum recommended size of contiguous memory that gets accessed together. Usually both match the size of an L1 cache line for the target, in bytes. For a generic target that covers a whole range of L1 cache line sizes, the constructive size is typically the small end of the range and the destructive size is the large end. ๐ŸŒˆ

The destructive interference size is meant for layout, so it has ABI impact. Its default isn't expected to stay stable, and on some targets it changes with -mtune. So using it somewhere ABI stability really matters, like a library's public interface, is strongly discouraged. If you do use it there anyway, you can lock the value in with this option. ๐Ÿšจ

The constructive interference size is way less sensitive, since it's usually only used in a static_assert to make sure a type fits inside a cache line. ๐Ÿ’–

See also -Winterference-size. ๐Ÿ‘€

  • loop-interchange-max-num-stmts โ€” ๐Ÿ” The max number of statements a loop can have and still get interchanged. ๐ŸŒธ
  • loop-interchange-stride-ratio โ€” ๐Ÿ“ The minimum ratio between the strides of two loops for interchange to be worth it. ๐Ÿ’œ
  • min-insn-to-prefetch-ratio โ€” ๐Ÿงฎ The minimum ratio between the number of instructions and the number of prefetches needed to turn prefetching on in a loop. ๐Ÿฆ‹
  • prefetch-min-insn-to-mem-ratio โ€” ๐Ÿงฎ The minimum ratio between the number of instructions and the number of memory references needed to turn prefetching on in a loop. ๐Ÿ“
  • use-canonical-types โ€” ๐Ÿง  Decides whether the compiler uses the "canonical" type system. This should always be 1, since it's a much more efficient internal way to compare types in C++ and Objective-C++. ๐Ÿ’…

But if bugs in the canonical type system are breaking your compilation, set it to 0 to turn canonical types off. ๐Ÿ™

  • switch-conversion-max-branch-ratio โ€” ๐Ÿ”€ Switch initialization conversion won't build arrays bigger than switch-conversion-max-branch-ratio times the number of branches in the switch. Too big and it just says no. ๐Ÿ™…โ€โ™€๏ธ
  • switch-lower-slow-alg-max-cases โ€” ๐Ÿข The max number of cases that the slow switch lowering algorithms are allowed to handle. ๐Ÿซ 
  • max-partial-antic-length โ€” ๐ŸŒŠ The max length of the partial antic set computed during tree partial redundancy elimination (-ftree-pre) when you're optimizing at -O3 and up. For some kinds of source code, the enhanced partial redundancy elimination can totally run away and eat all the memory on the host machine. This param caps the length of those sets so it can't go wild. ๐Ÿ’€

Setting it to 0 lets the set length be unlimited, so no cap at all. ๐Ÿ”“

  • rpo-vn-max-loop-depth โ€” ๐Ÿช† The max loop depth that gets value-numbered optimistically. When the limit gets hit, the innermost rpo-vn-max-loop-depth loops and the outermost loop in the loop nest get value-numbered optimistically, and the remaining ones don't. โœจ
  • sccvn-max-alias-queries-per-access โ€” ๐Ÿ” The max number of alias-oracle queries the compiler will run while hunting for redundancies in loads and stores. If the limit gets hit, the search is aborted and that load or store doesn't count as redundant. The number of queries is algorithmically capped at the number of stores on all paths from the load to the function entry. ๐Ÿงต
  • ira-max-loops-num โ€” ๐Ÿ”„ IRA uses regional register allocation by default. If a function has more loops than this number, only the given number of the most frequently executed loops end up forming regions for regional register allocation. ๐Ÿ†
  • ira-max-conflict-table-size โ€” ๐Ÿ—‚๏ธ IRA has a fancy algorithm for compressing the conflict table, but huge functions can still need a ton of memory for it. If the conflict table for a function could be bigger than this many MB, the register allocator switches to a faster, simpler, and lower-quality algorithm that doesn't build a pseudo-register conflict table at all. ๐Ÿฅฒ
  • ira-loop-reserved-regs โ€” ๐Ÿงบ IRA can estimate register pressure more accurately inside loops, which helps when deciding whether to move loop invariants (see -O3). This param sets how many available registers are reserved for other purposes. The default is the best value found from loads of experiments. ๐Ÿงช
  • ira-consider-dup-in-all-alts โ€” ๐ŸŽฏ Tells IRA to really weigh a matching constraint (a duplicated operand number) across every available alternative for the preferred register class. If it's set to zero, IRA only respects the matching constraint when it's the only available alternative with a suitable register class. Otherwise, IRA checks every available alternative for the preferred register class, even after it's found one that works, and still respects the qualified matching constraint it found. ๐Ÿงฉ
  • ira-simple-lra-insn-threshold โ€” ๐Ÿ“Š The approximate number of function insns, in units of 1K, that triggers simple local RA. โœ…
  • lra-inheritance-ebb-probability-cutoff โ€” ๐Ÿงฌ LRA tries to reuse values that were reloaded into registers in later insns. This is called inheritance, and it uses the EBB as the region for the optimization. This param sets the minimum fall-through edge probability, as a percentage, for adding a BB to an inheritance EBB in LRA. The default was picked from loads of SPEC2000 runs on x86-64. ๐Ÿ‹๏ธโ€โ™€๏ธ
  • loop-invariant-max-bbs-in-loop โ€” ๐Ÿ—๏ธ Loop invariant motion can get super expensive, both in compile time and in compile-time memory, when loops are huge. Any loop with more basic blocks than this param won't get loop invariant motion done on it. ๐Ÿšซ
  • loop-max-datarefs-for-datadeps โ€” ๐Ÿ”— Building data dependencies is expensive for really big loops. This param limits how many data references in a loop get considered for data dependence analysis. Loops that are too large for that aren't handled by the optimizations that use loop data dependencies. ๐Ÿ™…โ€โ™€๏ธ
  • max-vartrack-size โ€” ๐Ÿ•ต๏ธโ€โ™€๏ธ Sets the max number of hash table slots that can be used during variable tracking dataflow analysis for any function. If that limit gets exceeded while variable tracking at assignments is enabled, the analysis for that function gets retried without it, after all debug insns are removed from the function. If it's still too big even without debug insns, var tracking analysis is completely disabled for that function. Setting this to zero makes it unlimited. ๐ŸŒŸ
โš ๏ธ Heads up: the destructive interference size's default can shift between targets and with -mtune, so don't lean on it for ABI-sensitive stuff. Pin it down with the option instead. ๐Ÿ’–
  • max-vartrack-expr-depth โ€” ๐Ÿ’– okay so this one sets how many recursion levels the compiler can dig through when it tries to map variable names or debug temporaries to value expressions. It's a trade: more compile time for more complete debug info โœจ. Set it too low and value expressions that could've made it into the debug info just get skipped ๐Ÿ˜ญ. Set it higher and the compiler can find fancier debug expressions, but compile time and memory use may grow ๐Ÿซ .
  • max-debug-marker-count โ€” ๐ŸŽ€ this is a cap on how many debug markers (like begin stmt markers) you're allowed to have, so things don't blow up during inlining or expansion to RTL ๐Ÿ’€. If a function has more of these gimple stmts than the limit, they get dropped from the inlined copy of the function and from its RTL expansion. Main character energy, but make it reasonable ๐Ÿ’….
  • min-nondebug-insn-uid โ€” ๐Ÿงš uids start counting from this number for nondebug insns. Everything below it is reserved exclusively for debug insns created by -fvar-tracking-assignments. BUT if that reserved range runs out, debug insns can still get (non-overlapping) uids above the line ๐Ÿ™ˆ.
  • ipa-sra-deref-prob-threshold โ€” ๐Ÿฆ‹ IPA-SRA only swaps a pointer that's known not to be NULL for one or more new parameters when the probability of it being dereferenced is higher than this value. The probability is in percent, relative to function entry ๐Ÿ“ˆ.
  • ipa-sra-ptr-growth-factor โ€” ๐ŸŒธ IPA-SRA only replaces a pointer to an aggregate with one or more new parameters when their cumulative size is less than or equal to ipa-sra-ptr-growth-factor times the size of the original pointer parameter. No bloating allowed, bestie ๐Ÿ™…โ€โ™€๏ธ.
  • ipa-sra-ptrwrap-growth-factor โ€” ๐Ÿ“ this is the extra growth allowed on top of that, for total size of the new parameters. It applies when IPA-SRA replaces a pointer to an aggregate that points to a local variable the caller only writes to and passes along as an argument to other functions ๐Ÿซถ.
  • ipa-sra-max-replacements โ€” ๐Ÿ’œ the max number of pieces of an aggregate that IPA-SRA tracks. Because of that, it's also the max number of replacements of a formal parameter โœจ.
  • sra-max-scalarization-size-Ospeed, sra-max-scalarization-size-Osize โ€” ๐Ÿ”ฅ so the two Scalar Reduction of Aggregates passes (SRA and IPA-SRA) are obsessed with swapping scalar parts of aggregates for uses of independent scalar variables. These two params control the max size, in storage units, of an aggregate that's even considered for replacement. The first one is for when you're compiling for speed, the second is for when you're compiling for size ๐ŸŒˆ.
  • sra-max-propagations โ€” ๐Ÿ‘‘ the max number of artificial accesses that Scalar Replacement of Aggregates (SRA) will track per local variable, so it can do copy propagation ๐Ÿง .
  • tm-max-aggregate-size โ€” ๐ŸŒท when copying thread-local variables inside a transaction, this sets the size in bytes after which variables get saved with the logging functions instead of save/restore code sequence pairs. This only matters when using -fgnu-tm ๐ŸŽ€.
  • graphite-max-nb-scop-params โ€” ๐Ÿง to avoid exponential effects in the Graphite loop transforms, the number of parameters in a Static Control Part (SCoP) is bounded. Set it to 0 if you want to lift the bound ๐Ÿ”“. A variable whose value is unknown at compile time and defined outside a SCoP counts as a parameter of the SCoP โœจ.
  • hardcfr-max-blocks โ€” ๐Ÿ›ก๏ธ this one turns off -fharden-control-flow-redundancy for functions with more blocks than the value you set. 0 removes any limit at all ๐Ÿซก.
  • hardcfr-max-inline-blocks โ€” โšก this forces -fharden-control-flow-redundancy to use out-of-line checking for functions with more basic blocks than the value you set ๐ŸŒธ.
  • loop-block-tile-size โ€” ๐Ÿญ loop blocking or strip mining transforms (enabled with -floop-block or -floop-strip-mine) strip mine each loop in the loop nest by a given number of iterations. You can change that strip length using this parameter ๐Ÿ’….
  • ipa-jump-function-lookups โ€” ๐Ÿ” this specifies the number of statements visited during jump function offset discovery. Very detective behavior ๐Ÿ•ต๏ธโ€โ™€๏ธ.
  • ipa-cp-value-list-size โ€” ๐Ÿ“š IPA-CP tries to track every possible value and type passed to a function's parameter so it can propagate them and perform devirtualization. This is the max number of values and types it stores per one formal parameter of a function ๐Ÿ’–.
  • ipa-cp-eval-threshold โ€” ๐Ÿงฎ IPA-CP calculates its own score for cloning profitability heuristics, then performs the cloning opportunities whose scores exceed ipa-cp-eval-threshold ๐ŸŽฏ.
  • ipa-cp-max-recursive-depth โ€” ๐Ÿ” the max depth of recursive cloning for a self-recursive function. Don't spiral ๐ŸŒ€.
  • ipa-cp-min-recursive-probability โ€” ๐ŸŽฒ recursive cloning only happens when the probability of the call being executed exceeds this parameter ๐Ÿซง.
  • ipa-cp-profile-count-base โ€” ๐Ÿ“Š when you use -fprofile-use, IPA-CP looks at the measured execution count of a call graph edge at this percentage position in its histogram, and uses that as the basis for its heuristics calculation ๐Ÿฐ.
  • ipa-cp-recursive-freq-factor โ€” ๐Ÿ”„ the number of times interprocedural copy propagation expects recursive functions to call themselves. Iconic self-reference behavior ๐Ÿ™ˆ.
  • ipa-cp-recursion-penalty โ€” ๐Ÿ˜ค a percentage penalty that recursive functions get when they're evaluated for cloning. Not it ๐Ÿ’€.
  • ipa-cp-single-call-penalty โ€” ๐Ÿ“ž a percentage penalty for functions containing a single call to another function, applied when they're evaluated for cloning ๐Ÿซ .
  • ipa-max-agg-items โ€” ๐ŸŽ IPA-CP can also propagate a number of scalar values passed in an aggregate. This one controls the max number of those values per one parameter ๐Ÿ’œ.
  • ipa-cp-loop-hint-bonus โ€” ๐Ÿ† when IPA-CP figures out that a cloning candidate would make the number of loop iterations known, it adds a bonus of ipa-cp-loop-hint-bonus to that candidate's profitability score ๐ŸŒŸ.
  • ipa-max-loop-predicates โ€” ๐Ÿ”ฎ the max number of different predicates IPA uses to describe when loops in a function have known properties โœจ.
  • ipa-max-aa-steps โ€” ๐Ÿงญ while analyzing function bodies, IPA-CP uses alias analysis to track values pointed to by function parameters. So it doesn't spend forever on huge functions, it gives up and treats all memory as clobbered after examining ipa-max-aa-steps statements that modify memory ๐Ÿฅฑ.
  • ipa-max-switch-predicate-bounds โ€” ๐Ÿ”€ the max number of boundary endpoints of case ranges in a switch statement. For a switch that goes over this limit, IPA-CP won't build the cloning cost predicate used to estimate cloning benefit for the switch's default case ๐Ÿ™…โ€โ™€๏ธ.
  • ipa-max-param-expr-ops โ€” ๐Ÿงฉ IPA-CP analyzes conditional statements that reference a function parameter to estimate cloning benefit for a certain constant value. But if a parameter expression has more operations than ipa-max-param-expr-ops, it's considered too complicated and isn't handled by IPA analysis ๐Ÿ˜ตโ€๐Ÿ’ซ.
  • lto-partitions โ€” ๐Ÿงฑ the desired number of partitions produced during WHOPR compilation. The number of partitions should be more than the number of CPUs used for compilation ๐Ÿ–ฅ๏ธ.
  • lto-min-partition โ€” ๐Ÿชถ the size of the minimal partition for WHOPR (in estimated instructions). This keeps you from splitting very small programs into way too many partitions ๐Ÿ™„.
  • lto-max-partition โ€” ๐Ÿฐ the size of the max partition for WHOPR (in estimated instructions), giving an upper bound for the individual size of a partition. It's only meant for use with balanced partitioning โš–๏ธ.
  • lto-partition-locality-frequency-cutoff โ€” โœ‚๏ธ the denominator n of the fraction 1/n of the execution frequency of a callee to be cloned for a particular caller. The special value 0 means always clone, no cut-off ๐Ÿซถ.
  • lto-partition-locality-size-cutoff โ€” ๐Ÿ“ the size cut-off for a callee, including inlined calls, to be cloned for a particular caller ๐Ÿ’….
  • lto-max-locality-partition โ€” ๐Ÿ—บ๏ธ the maximal size of a locality partition for LTO (in estimated instructions). A value of 0 means the default value gets used ๐ŸŒท.
  • lto-max-streaming-parallelism โ€” ๐ŸŒŠ the maximal number of parallel processes used for LTO streaming. Teamwork makes the dream work ๐Ÿ‘ฏโ€โ™€๏ธ.
  • cxx-max-namespaces-for-diagnostic-help โ€” ๐Ÿ“– the max number of namespaces to check for suggestions when C++ name lookup fails for an identifier. Helpful but please don't overdo it ๐Ÿ™ˆ.
  • sink-frequency-threshold โ€” ๐ŸŒŠ the max relative execution frequency (in percent) of the target block, compared to a statement's original block, that still allows that statement to be sunk. Bigger numbers mean more aggressive statement sinking. Statements with memory operands get a small positive adjustment, because they're even more profitable to sink ๐Ÿ’ฐ.
  • max-stores-to-sink โ€” ๐Ÿ› the max number of conditional store pairs that can be sunk. Set it to 0 if either vectorization (-ftree-vectorize) or if-conversion (-ftree-loop-if-convert) is disabled ๐Ÿšซ.
  • case-values-threshold โ€” ๐Ÿชœ the smallest number of different values for which it's best to use a jump-table instead of a tree of conditional branches. If the value is 0, the default for the machine is used ๐ŸŽฏ.
  • jump-table-max-growth-ratio-for-size โ€” ๐Ÿ“ฆ the max code size growth ratio when expanding into a jump table (in percent). This param is used when optimizing for size ๐Ÿค.
  • jump-table-max-growth-ratio-for-speed โ€” ๐ŸŽ๏ธ the max code size growth ratio when expanding into a jump table (in percent). This param is used when optimizing for speed ๐Ÿ’จ.
  • tree-reassoc-width โ€” ๐ŸŽถ sets the max number of instructions executed in parallel in a reassociated tree. If it has a non-zero value, this overrides the target-dependent heuristics that are used by default โœจ.
  • sched-pressure-algorithm: okay so this one lets you pick between the two versions of -fsched-pressure ๐Ÿ’… Algorithm 1 is the OG and is the more likely to stop instructions from getting reordered ๐Ÿ™ˆ Algorithm 2 was built as a compromise between algorithm 1's pretty cautious energy and the rather aggressive default scheduler ๐Ÿ”ฅ It leans harder on having a regular register file and accurate register pressure classes โœจ See haifa-sched.cc in the GCC sources for the full tea. The default choice depends on the target ๐ŸŽ€
  • max-slsr-cand-scan: caps how many existing candidates get checked when hunting for a basis for a new straight-line strength reduction candidate ๐Ÿงš
  • asan-globals: turns on buffer overflow detection for global objects ๐ŸŒธ This is on by default when you use -fsanitize=address. To turn off global objects protection, use --param asan-globals=0.
  • asan-stack: buffer overflow detection for stack objects, and it's on by default with -fsanitize=address ๐Ÿ’– To disable stack protection, use --param asan-stack=0 option.
  • asan-instrument-reads: buffer overflow detection for memory reads ๐Ÿ‘€ Also on by default with -fsanitize=address. To turn off memory reads protection, use --param asan-instrument-reads=0.
  • asan-instrument-writes: buffer overflow detection for memory writes โœ๏ธ On by default with -fsanitize=address. To turn off memory writes protection, use --param asan-instrument-writes=0 option.
  • asan-memintrin: detection for built-in functions ๐Ÿฆ‹ On by default with -fsanitize=address. To turn off built-in functions protection, use --param asan-memintrin=0.
  • asan-use-after-return: detection of use-after-return ๐Ÿซถ On by default with the -fsanitize=address option. To turn it off, use --param asan-use-after-return=0.
Note: By default the check is disabled at run time ๐Ÿ˜ญ To turn it on, add "detect_stack_use_after_return=1" to the ASAN_OPTIONS environment variable.
  • asan-instrumentation-with-call-threshold: if the number of memory accesses in the function being instrumented is greater than or equal to this number, ASan uses callbacks instead of inline checks ๐Ÿ’… E.g. to skip the inline code entirely, use --param asan-instrumentation-with-call-threshold=0.
  • asan-kernel-mem-intrinsic-prefix: if nonzero, calls to "memcpy", "memset" and "memmove" get prefixed with __asan_ or __hwasan_ for -fsanitize=kernel-address or -fsanitize=kernel-hwaddress, respectively ๐Ÿงš
  • hwasan-instrument-stack: hwasan instrumentation for statically sized stack-allocated variables โœจ It's on by default with -fsanitize=hwaddress and off by default with -fsanitize=kernel-hwaddress. To turn it off, use --param hwasan-instrument-stack=0, and to turn it on, use --param hwasan-instrument-stack=1.
  • hwasan-random-frame-tag: when you're using stack instrumentation, tags for stack variables come from a deterministic sequence that starts at a random tag for each frame ๐ŸŒˆ With this unset, the tags use the same sequence but start from 1. It's enabled by default for -fsanitize=hwaddress and unavailable for -fsanitize=kernel-hwaddress. To disable it, use --param hwasan-random-frame-tag=0.
  • hwasan-instrument-allocas: hwasan instrumentation for dynamically sized stack-allocated variables ๐Ÿ“ It's on by default with -fsanitize=hwaddress and off by default with -fsanitize=kernel-hwaddress. To turn it off, use --param hwasan-instrument-allocas=0, and to turn it on, use --param hwasan-instrument-allocas=1.
  • hwasan-instrument-reads: hwasan checks on memory reads ๐Ÿ‘‘ Enabled by default for both -fsanitize=hwaddress and -fsanitize=kernel-hwaddress. To stop checking memory reads, use --param hwasan-instrument-reads=0.
  • hwasan-instrument-writes: hwasan checks on memory writes โœจ Enabled by default for both -fsanitize=hwaddress and -fsanitize=kernel-hwaddress. To stop checking memory writes, use --param hwasan-instrument-writes=0.
  • hwasan-instrument-mem-intrinsics: hwasan instrumentation for builtin functions ๐Ÿ’œ Enabled by default for both -fsanitize=hwaddress and -fsanitize=kernel-hwaddress. To turn off instrumentation of builtin functions, use --param hwasan-instrument-mem-intrinsics=0.
  • use-after-scope-direct-emission-threshold: if a local variable's size in bytes is smaller than or equal to this number, shadow memory gets poisoned (or unpoisoned) directly instead of going through run-time callbacks ๐ŸŽ€
  • tsan-distinguish-volatile: emits special instrumentation for accesses to volatiles ๐Ÿ”ฅ
  • tsan-instrument-func-entry-exit: emits instrumentation calls to __tsan_func_entry() and __tsan_func_exit() ๐Ÿฆ‹
  • max-fsm-thread-path-insns: the maximum number of instructions to copy when duplicating blocks on a finite state automaton jump thread path ๐Ÿ’…
  • threader-debug: threader-debug=[none|all] turns on verbose dumping of the threader solver ๐Ÿง 
  • parloops-chunk-size: chunk size of the omp schedule for loops parallelized by parloops ๐ŸŒธ
  • parloops-schedule: schedule type of the omp schedule for loops parallelized by parloops, which can be static, dynamic, guided, auto, or runtime ๐Ÿ“
  • parloops-min-per-thread: the minimum number of iterations per thread of an innermost parallelized loop where the parallel version beats the single-threaded one ๐Ÿงš Heads up that for a parallelized loop nest, the minimum number of iterations of the outermost loop per thread is two.
  • max-ssa-name-query-depth: the maximum recursion depth when querying properties of SSA names in things like fold routines ๐Ÿ’– One level of recursion means following one use-def chain.
  • max-speculative-devirt-maydefs: the maximum number of may-defs we analyze while looking for a must-def that specifies the dynamic type of an object invoking a virtual call we might be able to speculatively devirtualize ๐Ÿ‘€
  • ranger-debug: sets the type of debug output issued for ranges ๐Ÿซถ
  • unroll-jam-min-percent: the minimum percentage of memory references that must be optimized away for unroll-and-jam to count as profitable โœจ
  • unroll-jam-max-unroll: the maximum number of times the outer loop gets unrolled by unroll-and-jam ๐ŸŒˆ
  • max-rtl-if-conversion-unpredictable-cost: the maximum permissible cost for the sequence the RTL if-conversion pass would generate for a branch that's considered unpredictable ๐Ÿ’€
  • max-variable-expansions-in-unroller: if -fvariable-expansion-in-unroller is used, this is the maximum number of times an individual variable gets expanded during loop unrolling ๐Ÿ“
  • partial-inlining-entry-probability: the maximum probability of the entry BB of a split region (as a percent relative to the function's entry BB) that still lets partial inlining happen ๐ŸŽ€
  • max-tracked-strlens: the maximum number of strings the strlen optimization pass will track string lengths for ๐Ÿงš
  • gcse-after-reload-partial-fraction: the threshold ratio for performing partial redundancy elimination after reload ๐Ÿ”ฅ
  • gcse-after-reload-critical-fraction: the threshold ratio of critical edges' execution count that allows redundancy elimination after reload ๐Ÿ’œ
  • max-loop-header-insns: the maximum number of insns in a loop header duplicated by the copy loop headers pass ๐Ÿ‘‘
  • vect-epilogues-nomask: enables loop epilogue vectorization using a smaller vector size ๐Ÿฆ‹
  • vect-partial-vector-usage: controls when the loop vectorizer considers partial vector loads and stores as an alternative to falling back to scalar code ๐Ÿ’… The parameter only has an effect on targets that support partial vector loads and stores. The values mean:
    • 0: the vectorizer never uses partial vector loads and stores โŒ
    • 1: partial vector loads and stores are allowed if vectorization removes the need for the code to iterate โœจ
    • 2: partial vector loads and stores are allowed in all loops ๐ŸŒธ
  • vect-inner-loop-cost-factor: the maximum factor the loop vectorizer applies to the cost of statements in an inner loop, relative to the loop being vectorized ๐Ÿง  The factor applied is the maximum of the estimated number of iterations of the inner loop and this parameter. The default value of this parameter is 50.
  • vect-induction-float: enables loop vectorization of floating point inductions ๐ŸŒˆ
  • vect-force-slp: forces the use of SLP when vectorizing, and fails if that isn't possible ๐Ÿ”ฅ
  • vrp-block-limit: the maximum number of basic blocks before VRP switches to a lower-memory algorithm ๐Ÿงš
  • vrp-sparse-threshold: the maximum number of basic blocks before VRP uses a sparse bitmap cache ๐Ÿซถ
  • vrp-switch-limit: the maximum number of outgoing edges in a switch before VRP won't process it ๐Ÿ’€
  • vrp-vector-threshold: the maximum number of basic blocks for VRP to use a basic cache vector ๐Ÿ“
  • avoid-fma-max-bits: the maximum number of bits for which we avoid creating FMAs ๐Ÿ’–

Okay so bestie, we're picking up right where the --param list was getting spicy ๐ŸŒถ๏ธโœจ

  • fully-pipelined-fma โ€” ๐Ÿฆ‹ This is a target setting that says whether the chip fully pipelines FMA instructions. If it's non-zero, reassociation weighs the perks of splitting FMA's multiplication part and addition part apart, assuming FMUL and FMA share the same units that can also handle FADD. Main character energy ๐Ÿ‘‘
  • sms-loop-average-count-threshold โ€” ๐ŸŒธ A threshold on the average loop count that the swing modulo scheduler looks at.
  • sms-dfa-history โ€” ๐Ÿ’œ The number of cycles the swing modulo scheduler peeks at when it checks for conflicts using DFA.
  • graphite-allow-codegen-errors โ€” ๐Ÿ™ˆ Decides whether codegen errors should be ICEs when -fchecking is on. ICE alert ๐ŸงŠ
  • sms-max-ii-factor โ€” โœจ A factor for tuning the upper bound the swing modulo scheduler uses when it schedules a loop.
  • lra-max-considered-reload-pseudos โ€” ๐ŸŽ€ The max number of reload pseudos that get considered while spilling a non-reload pseudo. So many pseudos, so little time ๐Ÿ’€
  • max-pow-sqrt-depth โ€” ๐Ÿงš The maximum depth of sqrt chains allowed when synthesizing exponentiation by a real constant.
  • max-dse-active-local-stores โ€” ๐Ÿ“ The maximum number of active local stores in RTL dead store elimination. Bye bye, useless stores ๐Ÿ‘‹
  • asan-instrument-allocas โ€” ๐Ÿ›ก๏ธ Turns on asan protection for allocas and VLAs. Stay safe, bestie ๐Ÿซถ
  • max-iterations-computation-cost โ€” ๐ŸŒˆ The bound on how costly an expression can be when computing the number of iterations.
  • max-isl-operations โ€” ๐Ÿ’… The maximum number of isl operations. 0 means unlimited, which is giving no limits energy ๐Ÿ”ฅ
  • graphite-max-arrays-per-scop โ€” ๐Ÿฆ‹ The maximum number of arrays per scop.
  • max-vartrack-reverse-op-size โ€” โญ The max size of a loc list for which reverse ops should be added.
  • fsm-scale-path-stmts โ€” ๐Ÿ’– A scale factor applied to the number of statements in a threading path that crosses a loop backedge, when it gets compared against --param=max-jump-thread-duplication-stmts. Math-heavy but iconic ๐Ÿงฎ
  • uninit-control-dep-attempts โ€” ๐ŸŒธ The maximum number of nested calls used to hunt for control dependencies during uninitialized variable analysis.
  • uninit-max-chain-len โ€” ๐Ÿ’œ The maximum number of predicates anded together for each predicate ored in the normalized predicate chain.
  • uninit-max-num-chains โ€” โœจ The maximum number of predicates ored together in the normalized predicate chain.
  • uninit-max-prune-work โ€” ๐ŸŽ€ The maximum amount of work spent pruning paths where the variable is always initialized. Don't overdo it, queen ๐Ÿ‘‘
  • sched-autopref-queue-depth โ€” ๐Ÿงš A control flag for the hardware autoprefetcher scheduler model. It's the number of lookahead cycles the model peeks into. At ' ', it only enables the instruction sorting heuristic ๐Ÿ“
  • loop-versioning-max-inner-insns โ€” ๐ŸŒˆ The maximum number of instructions an inner loop can have before the loop versioning pass says "nope, too big to copy" ๐Ÿ™…โ€โ™€๏ธ
  • loop-versioning-max-outer-insns โ€” ๐Ÿฆ‹ The maximum number of instructions an outer loop can have before the loop versioning pass calls it too big to copy. Any instructions in inner loops that directly benefit from versioning get discounted, so they don't count against it โœจ
  • ssa-name-def-chain-limit โ€” โญ The maximum number of SSA_NAME assignments to follow when figuring out a property of a variable, like its value. This caps how many iterations or recursive calls GCC makes while optimizing certain statements or checking whether they're valid before it issues diagnostics. Boundaries are self-care ๐Ÿ’–
  • store-merging-max-size โ€” ๐Ÿ’œ The maximum size of a single store merging region, in bytes.
  • store-forwarding-max-distance โ€” ๐ŸŒธ The maximum instruction distance that a small store forwarded to a larger load may stall. Setting '0' disables the cost checks for the avoid-store-forwarding pass ๐Ÿ™ˆ
  • hash-table-verification-limit โ€” ๐ŸŽ€ The number of elements that get hash table verification for each searched element.
  • max-find-base-term-values โ€” ๐Ÿงš The maximum number of VALUEs handled during a single find_base_term call.
  • analyzer-max-enodes-per-program-point โ€” ๐Ÿ”ฅ The maximum number of exploded nodes per program point within the analyzer. Once that's hit, analysis of that point wraps up ๐Ÿ›‘
  • analyzer-max-constraints โ€” ๐Ÿ’… The maximum number of constraints per state.
  • analyzer-min-snodes-for-call-summary โ€” ๐Ÿ“ The minimum number of supernodes within a function before the analyzer even considers summarizing its effects at call sites.
  • analyzer-max-enodes-for-full-dump โ€” ๐ŸŒˆ The maximum depth of exploded nodes that should show up in a dot dump before it switches to a less verbose format ๐Ÿ“‰
  • analyzer-max-recursion-depth โ€” ๐Ÿฆ‹ The maximum number of times a callsite can show up in a call stack within the analyzer. Once that's reached, analysis of a call that would recurse deeper gets terminated. Not today, recursion ๐Ÿ”
  • analyzer-max-svalue-depth โ€” โœจ The maximum depth of a symbolic value. Past that, the value gets approximated as unknown ๐Ÿคทโ€โ™€๏ธ
  • analyzer-max-infeasible-edges โ€” โญ The maximum number of infeasible edges to reject before a diagnostic gets declared infeasible.
  • gimple-fe-computed-hot-bb-threshold โ€” ๐Ÿ’– The number of executions a basic block needs before it counts as hot. This parameter is only used in the GIMPLE FE ๐Ÿงช
  • analyzer-bb-explosion-factor โ€” ๐Ÿ’œ The maximum number of after supernode exploded nodes within the analyzer per supernode, before analysis terminates ๐Ÿ’ฅ
  • analyzer-text-art-string-ellipsis-threshold โ€” ๐ŸŽ€ The number of bytes at which string literals get ellipsized in analyzer text art diagrams ๐ŸŽจ
  • analyzer-text-art-ideal-canvas-width โ€” ๐ŸŒธ The ideal width, in characters, of text art diagrams generated by the analyzer ๐Ÿ“
  • analyzer-text-art-string-ellipsis-head-len โ€” ๐Ÿงš The number of literal bytes shown at the head of a string literal in text art when it gets ellipsized โœ‚๏ธ
  • analyzer-text-art-string-ellipsis-tail-len โ€” ๐Ÿ“ The number of literal bytes shown at the tail of a string literal in text art when it gets ellipsized โœ‚๏ธ
  • ranger-logical-depth โ€” ๐ŸŒˆ The maximum depth of logical expression evaluation that ranger looks through when it evaluates outgoing edge ranges ๐Ÿง 
  • ranger-recompute-depth โ€” ๐Ÿฆ‹ The maximum depth of instruction chains to consider for recomputation in the outgoing range calculator โ™ป๏ธ
  • relation-block-limit โ€” โญ The maximum number of relations the oracle will register in a basic block ๐Ÿงท
  • transitive-relations-work-bound โ€” ๐Ÿ’… A work bound for when transitive relations get discovered from existing relations ๐Ÿ”
  • min-pagesize โ€” ๐Ÿ”ฅ The minimum page size used for warning and early break vectorization purposes ๐Ÿ“„
  • openacc-kernels โ€” ๐Ÿงช Specifies the mode for handling OpenACC kernels constructs. With --param=openacc-kernels=decompose, OpenACC kernels constructs get broken into parts, a sequence of compute constructs, and each part gets handled on its own. This one's still work in progress ๐Ÿšง With --param=openacc-kernels=parloops, OpenACC kernels constructs are handled en bloc by the parloops pass. This is the current default โœ…
  • openacc-privatization โ€” ๐Ÿ™Š Controls whether the -fopt-info-omp-note and applicable -fdump-tree-*-details options emit OpenACC privatization diagnostics. With --param=openacc-privatization=quiet, nothing gets diagnosed. This is the current default. With --param=openacc-privatization=noisy, diagnostics do get emitted ๐Ÿ“ฃ
  • cycle-accurate-model โ€” ๐ŸŽฏ Says whether GCC should assume the scheduling description is mostly a cycle-accurate model of the target processor the code is meant to run on, when there are no cache misses. Nonzero means the selected scheduling model is accurate and probably describes an in-order processor, and scheduling should aggressively spill to fill any pipeline bubbles. This is the current default. Zero means the scheduling description might be unavailable or inaccurate, or maybe not applicable at all, like for modern out-of-order processors ๐ŸŒ€

The following choices of name are available on AArch64 targets:

  • aarch64-vect-compare-costs โ€” ๐Ÿงฎ When vectorizing, GCC can try several different approaches and let the cost model pick the cheapest one. That includes:
    • Trying both SVE and Advanced SIMD, when SVE is available.
    • Trying to use 64-bit Advanced SIMD vectors for the smallest data elements, rather than using 128-bit vectors for everything.
    • Trying to use "unpacked" SVE vectors for smaller elements. This includes storing smaller elements in larger containers and accessing elements with extending loads and truncating stores.
  • aarch64-float-recp-precision โ€” ๐Ÿฆ‹ The number of Newton iterations for calculating the reciprocal for the float type. The precision of division is proportional to this param when division approximation is enabled. The default value is 1 โœจ
  • aarch64-double-recp-precision โ€” ๐Ÿ’– The number of Newton iterations for calculating the reciprocal for the double type. The precision of division is proportional to this param when division approximation is enabled. The default value is 2 ๐Ÿ’œ
  • aarch64-autovec-preference โ€” ๐Ÿ‘‘ Forces an ISA selection strategy for auto-vectorization. The choices are:
    • default โ€” uses the default heuristics.
    • asimd-only โ€” uses only Advanced SIMD for auto-vectorization.
    • sve-only โ€” uses only SVE for auto-vectorization.
    • prefer-asimd โ€” uses both Advanced SIMD and SVE, but prefers Advanced SIMD when the costs are deemed equal.
    • prefer-sve โ€” uses both Advanced SIMD and SVE, but prefers SVE when the costs are deemed equal.
  • aarch64-ldp-policy โ€” ๐ŸŒˆ A fine-grained policy for load pairs. With --param=aarch64-ldp-policy=default, the policy of the tuning structure gets used. This is the current default. With --param=aarch64-ldp-policy=always, ldp gets emitted regardless of alignment. With --param=aarch64-ldp-policy=never, ldp is not emitted. With --param=aarch64-ldp-policy=aligned, ldp only gets emitted if the source pointer is aligned to at least double the alignment of the type ๐Ÿซถ

aarch64-stp-policy is the fine-grained knob for store pairs ๐Ÿ’…. With --param=aarch64-stp-policy=default, you get whatever the tuning structure says, which is also the current default โœจ. With --param=aarch64-stp-policy=always, GCC emits stp no matter what the alignment looks like ๐Ÿ”ฅ. With --param=aarch64-stp-policy=never, GCC does not emit stp at all ๐Ÿ™ˆ. With --param=aarch64-stp-policy=aligned, GCC only emits stp if the source pointer is aligned to at least double the alignment of the type ๐ŸŽ€.

  • aarch64-ldp-alias-check-limit โ€” this caps how many alias checks the AArch64 load/store pair fusion pass does when it tries to form an ldp/stp ๐Ÿฆ‹. Higher numbers make the pass more aggressive about re-ordering loads over stores, but compile time goes up to pay for it ๐Ÿฅฒ.
  • aarch64-ldp-writeback โ€” this controls which writeback opportunities the AArch64 load/store pair fusion pass bothers with ๐ŸŒธ. A value of 0 turns writeback handling off completely ๐Ÿšซ. A value of 1 means it tries to form pairs that involve one or more existing individual writeback accesses wherever it can ๐Ÿซถ. A value of 2 means it also goes out of its way to opportunistically create writeback opportunities by folding in trailing destructive updates of the base register used by a pair ๐Ÿ’œ.
  • aarch64-loop-vect-issue-rate-niters โ€” some AArch64 CPUs tuning tries to weigh both latencies and issue rates when deciding whether a loop should be vectorized with SVE, vectorized with Advanced SIMD, or not vectorized at all ๐Ÿง . If you set this to n, GCC skips that heuristic for loops that are known to run in fewer than n Advanced SIMD iterations โญ.
  • aarch64-vect-unroll-limit โ€” the vectorizer checks the available tuning info to decide whether unrolling the main vectorized loop would help, and by how much ๐ŸŒˆ. This param sets the upper bound on how much it's allowed to unroll that main loop ๐Ÿ“. The default value is four ๐Ÿ“.

The following choices of name are available on GCN targets ๐Ÿงš:

  • gcn-preferred-vectorization-factor โ€” the preferred vectorization factor ๐Ÿ’–. Your options are default, 32, or 64 โœจ.

The following choices of name are available on i386 and x86_64 targets ๐Ÿ’…:

  • x86-stlf-window-ninsns โ€” the number of instructions above which the STFL stall penalty can be compensated ๐Ÿซง.
  • x86-stv-max-visits โ€” the maximum number of use and def visits allowed while discovering a STV chain before the discovery gets aborted ๐Ÿ›‘.

๐Ÿฆ‹ Program Instrumentation Options ๐Ÿฆ‹

okay so GCC has a whole bunch of command-line options that bolt run-time instrumentation onto the code it normally generates ๐Ÿ’–. Instrumentation can be for collecting profiling stats so you can find your program's hot spots, doing code coverage analysis, or running profile-guided optimizations ๐Ÿ“Š. Another class is adding run-time checks that catch programming errors like invalid pointer dereferences or out-of-bounds array accesses, plus sneaky deliberate attacks like stack smashing or C++ vtable hijacking ๐Ÿ›ก๏ธ. And there's also a general hook you can use to implement other kinds of tracing or function-level instrumentation for debugging or program analysis ๐Ÿ”.

  • -p, -pg โ€” these generate extra code that writes profile information for the analysis program prof (with -p) or gprof (with -pg) ๐Ÿ“ˆ. You have to use this option when compiling the source files you want data about, and you have to use it when linking too, no skipping ๐Ÿ™…โ€โ™€๏ธ.

You can use the function attribute "no_instrument_function" to stop profiling of individual functions when you're compiling with these options ๐Ÿ™ˆ.

  • -fprofile-arcs โ€” this adds code so program flow arcs get instrumented ๐ŸŒŠ. While the program runs, it records how many times each branch and call is executed, and how many times each one is taken or returns ๐Ÿ”. On targets that support constructors with priority support, profiling handles constructors, destructors, and C++ constructors (and destructors) of classes used as the type of a global variable properly ๐Ÿ’œ.

When the compiled program exits, it saves this data to a file called auxname.gcda for each source file ๐Ÿ’พ. That data can be used for profile-directed optimizations (-fbranch-probabilities) or for test coverage analysis (-ftest-coverage) ๐ŸŽ€. Each object file's auxname comes from the name of the output file if you explicitly specified one and it isn't the final executable; otherwise it's the basename of the source file ๐Ÿง. Either way, any suffix gets removed. So foo.gcda is made for input file dir/foo.c, or dir/foo.gcda for an output file specified as -o dir/foo.o ๐Ÿฆ‹.

Heads up bestie: if a command line directly links source files, the matching .gcda files get prefixed with the unsuffixed name of the output file ๐Ÿซถ. For example, gcc a.c b.c -o binary would generate binary-a.gcda and binary-b.gcda files ๐Ÿ“.
  • -fcondition-coverage โ€” this adds code so program conditions get instrumented ๐Ÿ”. While the program runs, it records which terms in a conditional contribute to a decision ๐Ÿงฉ. That lets you check that all terms in a Boolean function are tested and have an independent effect on the outcome of a decision โœ…. You can read the result with "gcov --conditions" ๐Ÿ“–.
  • -fpath-coverage โ€” this adds code so the paths taken get tracked ๐Ÿ›ค๏ธ. While the program runs, it records the prime paths taken ๐ŸŒŸ. The number of paths grows super fast with complexity, so to avoid exploding compile times, GCC gives up on instrumentation if the approximate number of paths goes over the limit set by -fpath-coverage-limit ๐Ÿ˜ญ. You can read the result with "gcov --prime-paths --prime-paths-lines --prime-paths-source", ๐Ÿ’….
  • -fpath-coverage-limit=limit โ€” this is the threshold where -fpath-coverage throws in the towel on instrumenting a function ๐Ÿณ๏ธ. The limit is approximate and conservative, since GCC uses a pessimistic heuristic that slightly overcounts the running number of paths, and it gives up if the threshold is hit before all the paths are found ๐Ÿค. This option is not for fine-grained control over which functions get instrumented; it's there to limit the effect of path explosion and keep compile times reasonable โฑ๏ธ. The default is 250000 ๐Ÿ“.
  • --coverage โ€” this is the one-stop shop for compiling and linking code instrumented for coverage analysis ๐ŸŒˆ. It's a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov (when linking) โœจ. Check the docs for those options if you want the deets ๐Ÿ“š.

Here's the full routine, step by step:

  • Compile the source files with -fprofile-arcs plus your optimization and code generation options. For test coverage analysis, add the extra -ftest-coverage option too. You don't need to profile every single source file in a program ๐Ÿ™Œ.
  • Compile the source files additionally with -fprofile-abs-path so it creates absolute path names in the .gcno files. That lets gcov find the correct sources in projects where compilations happen from different working directories ๐Ÿ—บ๏ธ.
  • Link your object files with -lgcov or -fprofile-arcs (the latter implies the former) ๐Ÿ”—.
  • Run the program on a representative workload to generate the arc profile information ๐Ÿƒโ€โ™€๏ธ. You can repeat this any number of times ๐Ÿ”. You can run concurrent instances of your program, and as long as the file system supports locking, the data files will be correctly updated ๐Ÿ”’. Unless a strict ISO C dialect option is in effect, fork calls are detected and handled correctly without double counting ๐Ÿด.

Also, an object file can be recompiled multiple times, and the corresponding .gcda file merges as long as the source file and the compiler options stay unchanged ๐Ÿงฉ.

  • For profile-directed optimizations, compile the source files again with the same optimization and code generation options plus -fbranch-probabilities ๐Ÿš€.
  • For test coverage analysis, use gcov to produce human-readable info from the .gcno and .gcda files ๐Ÿ“. Check the gcov documentation for more deets ๐Ÿ’–.

With -fprofile-arcs, for each function of your program GCC creates a program flow graph and then finds a spanning tree for that graph ๐ŸŒณ. Only the arcs that are not on the spanning tree have to be instrumented: the compiler adds code to count how many times those arcs get executed ๐Ÿงฎ. When an arc is the only exit or the only entrance to a block, the instrumentation code can go into the block; otherwise, a new basic block has to be created to hold it ๐Ÿงฑ.

With -fcondition-coverage, for each conditional in your program GCC creates a bitset and records the exercised boolean values that have an independent effect on the outcome of that expression ๐ŸŽฏ.

With -fpath-coverage, GCC finds, enumerates, and records the taken prime paths of each function, unless the number of paths would exceed the limit set by -fpath-coverage-limit ๐Ÿ›ค๏ธ. If the limit is exceeded, the function is not instrumented, just as if -fpath-coverage wasn't used ๐Ÿ™ƒ. A prime path is the longest sequence of unique blocks, except possibly the first and last, that is not a subpath of any other path ๐ŸŒŸ.

  • -ftest-coverage โ€” this produces a notes file that the gcov code-coverage utility can use to show program coverage ๐Ÿ“’. Each source file's note file is called auxname.gcno ๐Ÿ—‚๏ธ. Check the -fprofile-arcs description above for what auxname means and how to generate test coverage data ๐Ÿ”. Coverage data matches the source files more closely if you don't optimize ๐Ÿ™.
  • -fprofile-abs-path โ€” this automatically converts relative source file names to absolute path names in the .gcno files ๐Ÿ—บ๏ธ. That lets gcov find the correct sources in projects where compilations happen from different working directories ๐Ÿซถ.
  • -fprofile-dir=path โ€” okay so this one tells GCC where to hunt for profile data files ๐Ÿ—‚๏ธโœจ It only affects profile data made by -fprofile-generate, -ftest-coverage, and -fprofile-arcs, and is used by -fprofile-use, -fbranch-probabilities, and their related options. Absolute and relative paths both work ๐Ÿ’…

By default GCC uses the current directory as path, so the profile data file lands in the same directory as the object file. To stop file names from clashing, if the object file name isn't an absolute path, GCC mangles the absolute path of the sourcename.gcda file and uses that as the name of the .gcda file. Check -fprofile-arcs for the full details on file naming, and see the similar -fprofile-note option ๐Ÿฆ‹

When your executable runs in a massive parallel environment, it's recommended to save profiles to different folders ๐ŸŒธ You can do that with variables inside path that get exported at run-time:

  • %p โ€” the process ID ๐Ÿ†”
  • %q{VAR} โ€” the value of the environment variable VAR ๐ŸŒˆ
  • -fprofile-generate, -fprofile-generate=path โ€” this one turns on the stuff you usually need for instrumenting an app so it spits out a profile that's useful for recompiling later with profile feedback based optimization ๐Ÿ”ฅ You have to use -fprofile-generate both when compiling AND when linking, no skipping either one ๐Ÿซถ

Turning it on also enables -fprofile-arcs, -fprofile-values, -finline-functions, and -fipa-bit-cp โœจ If you give it a path, GCC looks there for the profile feedback data files. See -fprofile-dir for that ๐Ÿงš

When you're ready to actually optimize using all that collected profile info, use -fprofile-use ๐Ÿ‘‘

  • -fprofile-info-section, -fprofile-info-section=name โ€” this registers the profile information in a specific section instead of using a constructor/destructor ๐ŸŽ€ The section is called name if you give one, otherwise it defaults to ".gcov_info". A pointer to the profile information generated by -fprofile-arcs gets placed in that section for each translation unit. This option turns off the profile registration through a constructor AND turns off profile processing through a destructor ๐Ÿ™ˆ

Heads up: this is NOT meant for hosted environments like GNU/Linux ๐Ÿ’€ It targets freestanding environments (think embedded systems) with limited resources that don't support constructors/destructors or C library file I/O.

The linker can collect the input sections into one continuous memory block and define start and end symbols. Here's a GNU linker script example that defines a linker output section:

          .gcov_info      :
          {
            PROVIDE (__gcov_info_start = .);
            KEEP (*(.gcov_info))
            PROVIDE (__gcov_info_end = .);
          }

Your program can then dump the profiling information registered in that linker set, for example like this:

        #include <gcov.h>
        #include <stdio.h>
        #include <stdlib.h>

        extern const struct gcov_info *const __gcov_info_start[];
        extern const struct gcov_info *const __gcov_info_end[];

        static void
        dump (const void *d, unsigned n, void *arg)
        {
          const unsigned char *c = d;

          for (unsigned i = 0; i < n; ++i)
            printf ("%02x", c[i]);
        }

        static void
        filename (const char *f, void *arg)
        {
          __gcov_filename_to_gcfn (f, dump, arg );
        }

        static void *
        allocate (unsigned length, void *arg)
        {
          return malloc (length);
        }

        static void
        dump_gcov_info (void)
        {
          const struct gcov_info *const *info = __gcov_info_start;
          const struct gcov_info *const *end = __gcov_info_end;

          /* Obfuscate variable to prevent compiler optimizations.  */
          __asm__ ("" : "+r" (info));

          while (info != end)
          {
            void *arg = NULL;
            __gcov_info_to_gcda (*info, filename, dump, allocate, arg);
            putchar ('\n');
            ++info;
          }
        }

        int
        main (void)
        {
          dump_gcov_info ();
          return 0;
        }

The merge-stream subcommand of gcov-tool can deserialize the data stream generated by the "__gcov_filename_to_gcfn" and "__gcov_info_to_gcda" functions and merge that profile information into .gcda files on the host filesystem ๐Ÿง ๐Ÿ’œ

  • -fprofile-note=path โ€” if you give it a path, GCC saves the .gcno file into that location ๐Ÿ“ Heads up: if you combine this with multiple source files, the .gcno file gets overwritten ๐Ÿ˜ญ
  • -fprofile-prefix-path=path โ€” this one pairs with profile-generate=profile_dir and profile-use=profile_dir to tell GCC where the base directory of your built source tree is ๐ŸŒณ By default, profile_dir holds files with mangled absolute paths of all object files in the built project, which is NOT ideal if the directory you built the instrumented binary in differs from the one you used for the optimized build, because then the profile data won't be found ๐Ÿฅฒ Setting -fprofile-prefix-path=path with path pointing at the base build directory strips the irrelevant part of the path and keeps every file name relative to the main build directory ๐Ÿซถ
  • -fprofile-prefix-map=old=new โ€” when compiling files living in directory old, GCC records profiling information (with --coverage) describing them as if they lived in directory new instead ๐Ÿ—บ๏ธ See also -ffile-prefix-map and -fcanon-prefix-map ๐Ÿ’…
  • -fprofile-update=method โ€” this changes the update method for an app instrumented for profile feedback based optimization โšก The method argument should be one of single, atomic, or prefer-atomic. The first is great for single-threaded apps, while the second keeps your profile from getting corrupted by emitting thread-safe code ๐Ÿงต
Warning: if an application doesn't properly join all threads (or creates a detached thread), a profile file can still get corrupted. Yikes ๐Ÿ’€

Using prefer-atomic becomes either atomic, when the target supports it, or single otherwise ๐Ÿคทโ€โ™€๏ธ The GCC driver automatically selects prefer-atomic when -pthread is on the command line; otherwise the default method is single โœจ

If atomic is selected, the profile information gets updated using atomic operations on a best-effort basis ๐Ÿ’– Ideally those atomic operations happen in hardware. If the target platform doesn't support the required atomic operations in hardware, but libatomic is available, the profile information gets updated through calls to libatomic ๐ŸŒŸ If the target platform supports neither the hardware atomics nor libatomic, the profile information is NOT atomically updated and a warning is issued. In that case, the profiling info may be corrupt for multi-threaded apps ๐Ÿ˜ฌ

For performance reasons, if 64-bit counters are used for the profiling information and the target only supports 32-bit atomic operations in hardware, the performance-critical profiling updates are done using two 32-bit atomic operations for each counter update ๐ŸŽ๏ธ If a signal interrupts those two operations while updating a counter, the profiling information may end up in an inconsistent state ๐Ÿ™ˆ

  • -fprofile-filter-files=regex โ€” instruments only functions from files whose name matches any of the regular expressions (separated by semi-colons) ๐Ÿ”

For example, -fprofile-filter-files=main\.c;module.*\.c will instrument only main.c and all C files starting with 'module' ๐Ÿงš

  • -fprofile-exclude-files=regex โ€” instruments only functions from files whose name does NOT match any of the regular expressions (separated by semi-colons) ๐Ÿšซ

For example, -fprofile-exclude-files=/usr/.* will prevent instrumentation of all files located in the /usr/ folder ๐Ÿ™…โ€โ™€๏ธ

  • -fprofile-reproducible=[multithreaded|parallel-runs|serial] โ€” this controls the level of reproducibility of the profile gathered by "-fprofile-generate" ๐ŸŽฏ It makes it possible to rebuild a program with the same outcome, which is super useful for things like distribution packages ๐Ÿ“ฆ

With -fprofile-reproducible=serial, the profile gathered by -fprofile-generate is reproducible, provided the trained program behaves the same at each invocation of the train run, it is not multi-threaded, and profile data streaming is always done in the same order ๐Ÿซง Note that profile streaming happens at the end of the program run but also before the "fork" function is invoked ๐Ÿ“

It's pretty common for execution counts of some parts of programs to depend on things like the length of temporary file names or memory space randomization (which can affect hash-table collision rates) ๐Ÿฅด That non-reproducible part of a program can be annotated with the "no_instrument_function" function attribute ๐Ÿท๏ธ You can use gcov-dump with -l to dump the gathered data and verify that it really is reproducible ๐Ÿ”Ž

With -fprofile-reproducible=parallel-runs, the collected profile stays reproducible regardless of the order in which data gets streamed into gcda files ๐Ÿ‘ฏ This setting lets you run multiple instances of the instrumented program in parallel (such as with "make -j") โšก The catch is that it reduces the quality of the gathered data, particularly for indirect call profiling ๐Ÿ˜ญ

โœจ OPTIONS โœจ (continued) ๐Ÿ’–

  • -fsanitize=address โ€” okay bestie, this one is AddressSanitizer ๐Ÿ”, a super fast memory error detector ๐Ÿ’…. The compiler instruments memory access instructions so it can catch out-of-bounds and use-after-free bugs ๐Ÿ˜ญ. This option also turns on -fsanitize-address-use-after-scope. More details live at <https://github.com/google/sanitizers/wiki/AddressSanitizer> ๐ŸŒธ. You can tweak the run-time behavior with the ASAN_OPTIONS environment variable ๐ŸŽ€. If you set help=1 in it, the instrumented program prints its available options when it starts up โœจ. The full list of supported flags is at <https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags> ๐Ÿฆ‹. Heads up: this one cannot be combined with -fsanitize=thread or -fsanitize=hwaddress ๐Ÿ™…โ€โ™€๏ธ. Also, -fsanitize=hwaddress is currently only supported on x86-64 (only with -mlam=u48 or -mlam=u57) and AArch64, and in both cases only in ABIs with 64-bit pointers ๐Ÿ’œ.

When you compile with -fsanitize=address, you should also throw in -g ๐Ÿงš so the output is way more meaningful. Want more accurate stack traces? Use -O0, -O1, or -Og ๐Ÿ“, which (for instance) stop most function inlining. You can also add -fno-optimize-sibling-calls ๐Ÿ™ˆ, which stops sibling and tail recursive calls from being optimized; this option is implicit for -O0, -O1, or -Og. Or try -fno-ipa-icf ๐Ÿ’…, which turns off Identical Code Folding for functions. -fno-omit-frame-pointer helps your stack traces look better too ๐Ÿ”ฅ. Since multiple runs can give backtraces with different addresses thanks to ASLR (Address Space Layout Randomization) ๐ŸŒˆ, you might want to turn ASLR off. On Linux, that's done with setarch uname -m -R ./prog ๐Ÿ‘‘.

  • -fsanitize=kernel-address โ€” Enable AddressSanitizer for the Linux kernel ๐Ÿงโœจ. More details are at <https://github.com/google/kernel-sanitizers> ๐Ÿซถ.
  • -fsanitize=hwaddress โ€” This is Hardware-assisted AddressSanitizer ๐Ÿง , which uses a hardware trick where the top byte of a pointer gets ignored ๐Ÿช„. That lets it catch memory errors with very low memory overhead, which is so efficient ๐Ÿ˜ญ. Memory access instructions are instrumented to detect out-of-bounds and use-after-free bugs ๐Ÿ’œ. It enables -fsanitize-address-use-after-scope too. Check <https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html> for more details ๐ŸŒธ. The run-time behavior is controlled by the HWASAN_OPTIONS environment variable, and help=1 shows the available options at startup ๐ŸŽ€. It can't be combined with -fsanitize=thread or -fsanitize=address, and it's currently only available on AArch64 ๐Ÿฆ‹.
  • -fsanitize=kernel-hwaddress โ€” Enable Hardware-assisted AddressSanitizer for compiling the Linux kernel ๐Ÿ‘‘. It's similar to -fsanitize=kernel-address but uses an alternate instrumentation method, and similar to -fsanitize=hwaddress but with instrumentation differences needed for the kernel ๐Ÿงš. Those differences exist to avoid hwasan library initialization calls, and to account for the stack pointer having a different value in its top byte ๐Ÿ™ˆ.
Note: this option has different defaults than -fsanitize=hwaddress ๐Ÿ˜ญ. Instrumenting the stack and alloca calls is not on by default, but you can still turn it on with the command-line options --param hwasan-instrument-stack=1 and --param hwasan-instrument-allocas=1 respectively ๐Ÿ’…. Using a random frame tag is not implemented for kernel instrumentation ๐Ÿ’€.
  • -fsanitize=pointer-compare โ€” Instruments comparison operations (<, <=, >, >=) with pointer operands ๐Ÿ”. This option must be combined with either -fsanitize=kernel-address or -fsanitize=address, and it cannot be combined with -fsanitize=thread ๐Ÿ™…โ€โ™€๏ธ. Note: by default this check is disabled at run time ๐Ÿ’€. To turn it on, add detect_invalid_pointer_pairs=2 to the ASAN_OPTIONS environment variable โœจ. Using detect_invalid_pointer_pairs=1 only detects invalid operations when both pointers are non-null ๐ŸŒธ.
  • -fsanitize=pointer-subtract โ€” Instruments subtraction with pointer operands ๐Ÿฆ‹. It must be combined with either -fsanitize=kernel-address or -fsanitize=address, and it cannot be combined with -fsanitize=thread ๐Ÿ’œ. Same deal as above: the check is disabled at run time by default ๐Ÿ˜ญ. Add detect_invalid_pointer_pairs=2 to ASAN_OPTIONS to enable it ๐ŸŽ€. With detect_invalid_pointer_pairs=1, invalid operations only get caught when both pointers are non-null ๐Ÿ“.
  • -fsanitize=shadow-call-stack โ€” Enable ShadowCallStack ๐Ÿ›ก๏ธ, a security enhancement that protects programs against return address overwrites (like stack buffer overflows ๐Ÿ˜ฑ). It works by saving a function's return address to a separately allocated shadow call stack in the function prologue, then restoring the return address from that shadow stack in the function epilogue ๐Ÿงš. Instrumentation only happens in functions that need to save the return address to the stack โœจ.

Currently this only supports the aarch64 platform ๐ŸŒˆ. It's specifically designed for Linux kernels that enable the CONFIG_SHADOW_CALL_STACK option ๐Ÿง. For user space programs, runtime support isn't currently provided in libc and libgcc, so if you want this in user space you'll need to bring your own runtime support ๐Ÿซ . It should be noted that this may cause the ABI rules to be broken โš ๏ธ.

On aarch64, the instrumentation uses the platform register x18 ๐Ÿ‘€. That generally means any code that might run on the same thread as code compiled with ShadowCallStack must be compiled with -ffixed-x18. Otherwise, functions compiled without -ffixed-x18 might clobber x18 and corrupt the shadow stack pointer ๐Ÿ’€.

Also, because there's no userspace runtime support, code compiled with ShadowCallStack can't use exception handling ๐Ÿ™…โ€โ™€๏ธ. Use -fno-exceptions to turn exceptions off. More details at <https://clang.llvm.org/docs/ShadowCallStack.html> ๐ŸŒธ.

  • -fsanitize=thread โ€” Enable ThreadSanitizer, a fast data race detector ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ. Memory access instructions are instrumented to detect data race bugs ๐Ÿ”ฅ. See <https://github.com/google/sanitizers/wiki#threadsanitizer> for more details ๐Ÿฆ‹. The run-time behavior can be influenced with the TSAN_OPTIONS environment variable, and you can find the list of supported options at <https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags> ๐ŸŽ€. This option cannot be combined with -fsanitize=address or -fsanitize=leak ๐Ÿ™…โ€โ™€๏ธ.

When you compile with -fsanitize=thread, you should also use -g for more meaningful output ๐Ÿงš.

Note that sanitized atomic builtins cannot throw exceptions when operating on invalid memory addresses with non-call exceptions (-fnon-call-exceptions) โš ๏ธ.

  • -fsanitize=leak โ€” Enable LeakSanitizer, a memory leak detector ๐Ÿ’ง. This option only matters when linking executables ๐Ÿ”—. The executable gets linked against a library that overrides malloc and other allocator functions ๐Ÿช„. See <https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer> for more details ๐ŸŒธ. The run-time behavior can be influenced with the LSAN_OPTIONS environment variable ๐ŸŽ€. This option cannot be combined with -fsanitize=thread ๐Ÿ™…โ€โ™€๏ธ.
  • -fsanitize=undefined โ€” Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector ๐Ÿ•ต๏ธโ€โ™€๏ธ. Various computations get instrumented to catch undefined behavior at runtime ๐Ÿ’…. See <https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html> for more details ๐Ÿฆ‹. The run-time behavior can be influenced with the UBSAN_OPTIONS environment variable ๐ŸŽ€. Here are the current suboptions ๐Ÿ‘‡:
    • -fsanitize=shift โ€” Checks that the result of a shift operation isn't undefined ๐Ÿซจ. What counts as undefined differs slightly between C and C++, and also between ISO C90 and C99, etc. This option has two suboptions, -fsanitize=shift-base and -fsanitize=shift-exponent ๐Ÿงฉ.
    • -fsanitize=shift-exponent โ€” Checks that the second argument of a shift operation isn't negative and is smaller than the precision of the promoted first argument ๐Ÿ”ข.
    • -fsanitize=shift-base โ€” If the second argument of a shift operation is within range, this checks that the result of the shift isn't undefined ๐Ÿง. Again, what counts as undefined differs slightly between C and C++, as well as between ISO C90 and C99, etc.
    • -fsanitize=integer-divide-by-zero โ€” Detects integer division by zero ๐Ÿ™…โ€โ™€๏ธโž—.
    • -fsanitize=unreachable โ€” The compiler turns the __builtin_unreachable call into a diagnostics message call instead ๐Ÿ“ฃ. When the __builtin_unreachable call is reached, the behavior is undefined ๐Ÿ’€.
    • -fsanitize=vla-bound โ€” Tells the compiler to check that the size of a variable length array is positive ๐Ÿ“.
    • -fsanitize=null โ€” Enables pointer checking ๐Ÿ”. Apps built with this on will throw an error message when they try to dereference a NULL pointer, when a reference (possibly an rvalue reference) gets bound to a NULL pointer, or when a method is invoked on an object pointed to by a NULL pointer ๐Ÿ™ˆ.
    • -fsanitize=return โ€” Enables return statement checking ๐Ÿ”™. Programs built with this on will throw an error message when the end of a non-void function is reached without actually returning a value ๐Ÿ˜ฑ. This option works in C++ only ๐Ÿ’œ.
    • -fsanitize=signed-integer-overflow โ€” Enables signed integer overflow checking โž•โœ–๏ธ. It checks that the result of +, *, and both unary and binary - doesn't overflow in signed arithmetic ๐Ÿงฎ. This also detects INT_MIN / -1 signed division ๐Ÿ’ฅ. Heads up: integer promotion rules must be taken into account ๐Ÿง . For example, the following is not an overflow ๐Ÿ‘‡:
        signed char a = SCHAR_MAX;
        a++;

Okay so the sanitizer options are where things get spicy ๐ŸŒถ๏ธ bestie, pay attention ๐Ÿ’…

  • -fsanitize=bounds โ€” ๐Ÿ“ Turns on array bounds instrumentation so a bunch of sneaky out-of-bounds accesses get caught. Flexible array members, flexible-array-member-like arrays, and initializers of variables with static storage are not instrumented, with one exception: flexible-array-member-like arrays, if -fstrict-flex-arrays or -fstrict-flex-arrays= or the strict_flex_array attribute says they shouldn't be treated that way. ๐Ÿ™ˆ
  • -fsanitize=bounds-strict โ€” ๐Ÿ”’ The strict version. Most out-of-bounds accesses get detected, including flexible-array-member-like arrays. Initializers of variables with static storage still aren't instrumented, so don't come for them ๐Ÿ˜ญ
  • -fsanitize=alignment โ€” ๐Ÿงญ Checks that pointers are aligned when they get dereferenced, when a reference gets bound to an insufficiently aligned target, or when a method or constructor gets invoked on an insufficiently aligned object. Misaligned vibes get flagged โœจ
  • -fsanitize=object-size โ€” ๐ŸŽฏ Instruments memory references using the __builtin_dynamic_object_size function. Various out-of-bounds pointer accesses get detected, iconic behavior ๐Ÿ‘‘
  • -fsanitize=float-divide-by-zero โ€” โž— Detects floating-point division by zero. Unlike other similar options, this one is not enabled by -fsanitize=undefined, because dividing floats by zero can be a totally legit way to get infinities and NaNs. Not everything is a bug, babe ๐Ÿฆ‹
  • -fsanitize=float-cast-overflow โ€” ๐ŸŒŠ Enables checking of floating-point to integer conversions. It checks that the result of the conversion doesn't overflow. Also unlike other similar options, this one is not enabled by -fsanitize=undefined. Heads up: it doesn't play nicely with FE_INVALID exceptions enabled ๐Ÿ’€
  • -fsanitize=nonnull-attribute โ€” ๐Ÿšซ Instruments calls to check that null values aren't passed to arguments marked as requiring a non-null value by the nonnull function attribute. No nulls at the party ๐ŸŽ€
  • -fsanitize=returns-nonnull-attribute โ€” ๐Ÿ” Instruments return statements in functions marked with the returns_nonnull function attribute, so returning a null value from one of those functions gets detected ๐ŸŒธ
  • -fsanitize=bool โ€” ๐Ÿ”˜ Instruments loads from bool. If a value other than 0/1 gets loaded, a run-time error is issued. Bools are supposed to be 0 or 1, not vibes ๐Ÿ™ƒ
  • -fsanitize=enum โ€” ๐Ÿ”ข Instruments loads from an enum type. If a value outside the range of values for that enum type gets loaded, a run-time error is issued ๐Ÿงš
  • -fsanitize=vptr โ€” ๐Ÿงฌ Instruments C++ member function calls, member accesses, and some conversions between pointers to base and derived classes. It verifies that the referenced object has the correct dynamic type, so nobody's lying about their identity ๐Ÿ’œ
  • -fsanitize=pointer-overflow โ€” โž• Instruments pointer arithmetic. If the pointer arithmetic overflows, a run-time error is issued ๐Ÿ“
  • -fsanitize=builtin โ€” ๐Ÿ› ๏ธ Instruments arguments to selected builtin functions. If an invalid value gets passed to one of those arguments, a run-time error is issued. For example, passing 0 as the argument to __builtin_ctz or __builtin_clz invokes undefined behavior, and this option diagnoses it ๐Ÿ”ฅ

Note that sanitizers tend to bump up the rate of false positive warnings, most notably the ones around -Wmaybe-uninitialized. We recommend against combining -Werror and [the use of] sanitizers. Not the move ๐Ÿ˜ญ

While -ftrapv causes traps for signed overflows to be emitted, -fsanitize=undefined gives a diagnostic message instead. This currently works only for the C family of languages ๐ŸŒˆ

  • -fno-sanitize=all โ€” ๐Ÿงน Disables all previously enabled sanitizers. -fsanitize=all is not allowed, though, because some sanitizers can't be used together, so don't even try it ๐Ÿ™…โ€โ™€๏ธ
  • -fasan-shadow-offset=number โ€” ๐Ÿงช Forces GCC to use a custom shadow offset in AddressSanitizer checks. It's useful for experimenting with different shadow memory layouts in Kernel AddressSanitizer, very mad scientist of you ๐Ÿง‘โ€๐Ÿ”ฌ
  • -fsanitize-sections=s1,s2,... โ€” ๐Ÿ—‚๏ธ Sanitizes global variables in selected user-defined sections. The si part may contain wildcards ๐Ÿฆ„
  • -fsanitize-recover[=opts] โ€” ๐Ÿ”„ -fsanitize-recover= controls the error recovery mode for the sanitizers listed in the comma-separated list of opts. When it's enabled for a sanitizer, that sanitizer tries to keep running the program as if no error happened. So multiple runtime errors can get reported in one run, and the exit code might say success even after errors were reported ๐Ÿ˜ณ The -fno-sanitize-recover= option changes this: only the first detected error gets reported, and then the program exits with a non-zero exit code ๐Ÿ›‘

Right now this only works for -fsanitize=undefined (and its suboptions, except for -fsanitize=unreachable and -fsanitize=return), -fsanitize=float-cast-overflow, -fsanitize=float-divide-by-zero, -fsanitize=bounds-strict, -fsanitize=kernel-address, and -fsanitize=address. For these sanitizers, error recovery is turned on by default, except for -fsanitize=address, where it's still experimental ๐Ÿงช -fsanitize-recover=all and -fno-sanitize-recover=all are also accepted. The former enables recovery for every sanitizer that supports it, and the latter disables recovery for every sanitizer that supports it ๐Ÿ’–

Even if recovery mode is turned on on the compiler side, it also needs to be enabled on the runtime library side, otherwise the failures are still fatal ๐Ÿ’€ The runtime library defaults to halt_on_error=0 for ThreadSanitizer and UndefinedBehaviorSanitizer, while the default value for AddressSanitizer is halt_on_error=1. You can override this by setting the halt_on_error flag in the corresponding environment variable ๐ŸŒธ

Syntax without an explicit opts parameter is deprecated. It's equivalent to specifying an opts list of:

        undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
  • -fsanitize-address-use-after-scope โ€” ๐Ÿ•ฐ๏ธ Enables sanitization of local variables to catch use-after-scope bugs. This option sets -fstack-reuse to none โœจ
  • -fsanitize-trap[=opts] โ€” ๐Ÿชค Tells the compiler to report undefined behavior for the sanitizers in the comma-separated list of opts using __builtin_trap instead of a libubsan library routine. If this is enabled for a certain sanitizer, it takes precedence over the -fsanitizer-recover= for that sanitizer. __builtin_trap gets emitted and is fatal regardless of whether recovery is enabled or disabled through -fsanitize-recover= ๐Ÿ’ฅ

The advantage? The libubsan library isn't needed and doesn't get linked in, so this works even in freestanding environments ๐Ÿงš

Right now this works with -fsanitize=undefined (and its suboptions, except for -fsanitize=vptr), -fsanitize=float-cast-overflow, -fsanitize=float-divide-by-zero, and -fsanitize=bounds-strict. -fsanitize-trap=all can also be specified, which enables it for the undefined suboptions, -fsanitize=float-cast-overflow, -fsanitize=float-divide-by-zero, and -fsanitize=bounds-strict. If -fsanitize-trap=undefined or -fsanitize-trap=all is used and -fsanitize=vptr is enabled on the command line, the instrumentation is silently ignored, since it always needs libubsan support. -fsanitize-trap=vptr is not allowed ๐Ÿ™…โ€โ™€๏ธ

  • -fsanitize-undefined-trap-on-error โ€” ๐Ÿ—‘๏ธ This one is deprecated and is an equivalent of -fsanitize-trap=all. Retire it, bestie ๐Ÿ‘‹
  • -fsanitize-coverage=trace-pc โ€” ๐Ÿ•ต๏ธ Enables coverage-guided fuzzing code instrumentation. Inserts a call to __sanitizer_cov_trace_pc into every basic block ๐Ÿ„
  • -fsanitize-coverage=trace-cmp โ€” ๐Ÿงฒ Enables dataflow-guided fuzzing code instrumentation. Inserts a call to __sanitizer_cov_trace_cmp1, __sanitizer_cov_trace_cmp2, __sanitizer_cov_trace_cmp4, or __sanitizer_cov_trace_cmp8 for integral comparisons where both operands are variables. For integral comparisons with one constant operand, it uses __sanitizer_cov_trace_const_cmp1, __sanitizer_cov_trace_const_cmp2, __sanitizer_cov_trace_const_cmp4, or __sanitizer_cov_trace_const_cmp8. For float or double comparisons it uses __sanitizer_cov_trace_cmpf or __sanitizer_cov_trace_cmpd, and for switch statements it uses __sanitizer_cov_trace_switch ๐ŸŽฐ
  • -fcf-protection=[full|branch|return|none|check], -fcf-protection โ€” ๐Ÿ›ก๏ธ Enables code instrumentation to boost program security by checking that the target addresses of control-flow transfer instructions (like indirect function calls, function returns, and indirect jumps) are valid. This stops the flow of control from getting diverted to an unexpected target. It's meant to protect against threats like Return-oriented Programming (ROP) and similar call/jmp-oriented programming (COP/JOP) ๐Ÿ”

The -fcf-protection= keywords work like this:

  • The value branch tells the compiler to check validity of control-flow transfers at indirect branch instructions, i.e. call/jmp instructions ๐Ÿ“ž
  • The value return checks validity at the point of returning from a function ๐Ÿ”™
  • The value full is an alias for specifying both branch and return ๐Ÿ‘‘
  • The value check is used for the final link with link-time optimization (LTO). An error is issued if LTO object files were compiled with different -fcf-protection values. The check value is ignored at compile time ๐Ÿง
  • The value none turns instrumentation off ๐Ÿ™…โ€โ™€๏ธ

-fcf-protection is an alias for -fcf-protection=full. To override a previous -fcf-protection option on the command line, add -fcf-protection=none and then -fcf-protection=kind ๐Ÿ”

The macro __CET__ is defined when -fcf-protection is used. The first bit of __CET__ is set to 1 for the value branch, and the second bit of __CET__ is set to 1 for return ๐Ÿงฌ

You can also use the nocf_check attribute to identify which functions and calls should be skipped from instrumentation ๐Ÿซถ

Okay so picking up right where we left off, bestie ๐Ÿ’…โœจ The x86 GNU/Linux target has a CET (Intel Control-flow Enforcement Technology) setup that works on i686 processors and newer ๐Ÿฆ‹

๐Ÿ’€ NOTE: In Ubuntu 19.10 and later, -fcf-protection is turned on by default for C, C++, ObjC, and ObjC++, as long as you haven't used -fno-cf-protection or any -fcf-protection=* flag. Ubuntu is being very protective of us ๐Ÿซถ
  • -fharden-compares โ€” okay this one is lowkey sneaky ๐Ÿ™ˆ For every logical test that survives gimple optimizations and isn't the condition in a conditional branch (think conditions for conditional moves, or ones stored in boolean variables), GCC emits extra code that computes and checks the reversed condition. If the two results don't match, it calls __builtin_trap. Pair it with -fharden-conditional-branches to cover every conditional โœจ
  • -fharden-conditional-branches โ€” for every non-vectorized conditional branch that survives gimple optimizations, GCC adds extra code that computes and verifies the reversed condition, and calls __builtin_trap if the result is unexpected. Use it with -fharden-compares to cover all conditionals ๐Ÿ’–
  • -fharden-control-flow-redundancy โ€” this one is giving bodyguard energy ๐Ÿ›ก๏ธ GCC emits extra code that sets booleans when entering basic blocks, then at function exits it verifies them and traps if the booleans don't form an execution path that matches the control flow graph ๐Ÿ”ฅ

Verification happens before returns, before mandatory tail calls (more on those below), and optionally before escaping exceptions with -fhardcfr-check-exceptions, before returning calls with -fhardcfr-check-returning-calls, and before noreturn calls with -fhardcfr-check-noreturn-calls. There are also tuning options, --param hardcfr-max-blocks and --param hardcfr-max-inline-blocks, if you want to fine-tune things ๐ŸŽ€

Tail call optimization happens too late to affect control flow redundancy. Calls that language front-ends mark as mandatory tail calls, and any calls marked early enough as potential tail calls, would also get verification before the call. Honestly though, these cases are merely theoretical, because the conditions can only be met when you use custom compiler plugins ๐Ÿ™ˆ

  • -fhardcfr-skip-leaf โ€” disables -fharden-control-flow-redundancy in leaf functions ๐Ÿƒ
  • -fhardcfr-check-exceptions โ€” when -fharden-control-flow-redundancy is active, the recorded execution path gets checked against the control flow graph at exception escape points. It's like wrapping the function body in a cleanup handler that does the check and then reraises ๐Ÿงš This option is enabled by default. If you want it gone, use -fno-hardcfr-check-exceptions ๐Ÿ’…
  • -fhardcfr-check-returning-calls โ€” when -fharden-control-flow-redundancy is active, the recorded path is checked before any function call that's immediately followed by a return of its result (if there is one). This way it doesn't block tail-call optimization, whether or not the call actually ends up becoming a tail call ๐Ÿฆ‹

This option is enabled by default whenever sibling call optimizations are on (see -foptimize-sibling-calls). But you can turn it on or off explicitly, using its negated form, no matter what the optimizations are doing ๐ŸŒธ

  • -fhardcfr-check-noreturn-calls=[always|no-xthrow|nothrow|never] โ€” when -fharden-control-flow-redundancy is active, the recorded path gets checked against the control flow graph before "noreturn" calls. You pick which ones:
    • always checks before all of them
    • no-xthrow checks before those not expected to return control to the caller through an exception. This is the default ๐Ÿ’–
    • nothrow checks before those that may not return control to the caller through an exception either
    • never checks before none of them

Heads up ๐Ÿ‘€ checking before a "noreturn" function that may return control through an exception can happen more than once, if the caller catches the exception (with a handler or a cleanup). If -fhardcfr-check-exceptions is also on, the compiler skips associating a "noreturn" call with the implicit cleanup handler, since that would be redundant with the check already done before the call. Other handlers or cleanups in the function will still modify the recorded path and check it again when another checkpoint gets hit. That checkpoint could even be another "noreturn" call, so checking might end up running multiple times ๐Ÿ˜ญ

Various optimizers can mark calls as "noreturn" and/or "nothrow" even without the matching attributes. That can change where checks go and which implicit cleanup handlers get added. Because of this unpredictability, and because raising and reraising exceptions often amounts to implicitly calling "noreturn" functions, no-xthrow is the default. It only excludes internal functions used to (re)raise exceptions from the "noreturn" treatment, and those aren't affected by these optimizations โœจ

  • -fhardened โ€” the big hardening bundle ๐Ÿ”ฅ It enables a set of flags for C and C++ that make the generated code more secure without changing its ABI. The exact flags can shift between major GCC releases, but right now it's:
-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS
-ftrivial-auto-var-init=zero -fPIE  -pie  -Wl,-z,relro,-z,now
-fstack-protector-strong -fstack-clash-protection
-fcf-protection=full (x86 GNU/Linux only)

You can see the whole list with --help=hardened ๐Ÿ‘‘

When the system glibc is older than 2.35, -D_FORTIFY_SOURCE=2 gets used instead ๐Ÿ™ˆ

This one is meant for production builds, not just debug builds, so don't be shy about shipping it ๐Ÿš€

โš ๏ธ Right now -fhardened is only supported on GNU/Linux targets.

Also, -fhardened only turns on a particular option if it wasn't already specified anywhere on the command line. For example, -fhardened -fstack-protector will only enable -fstack-protector, and not -fstack-protector-strong ๐Ÿ’€

  • -fstack-protector โ€” emits extra code to check for buffer overflows, like stack smashing attacks ๐Ÿ›ก๏ธ It works by adding a guard variable to functions with vulnerable objects. That includes functions that call alloca, and functions with buffers larger than or equal to 8 bytes. The guards get initialized when the function is entered and checked when it exits. If a guard check fails, an error message gets printed and the program exits. Only variables that are actually allocated on the stack count, so optimized-away variables or ones living in registers don't matter ๐Ÿงš
  • -fstack-protector-all โ€” like -fstack-protector, except every function gets protected ๐Ÿ’œ
  • -fstack-protector-strong โ€” like -fstack-protector, but it also protects extra functions: the ones with local array definitions, or ones that reference local frame addresses. Same deal as before, only variables actually allocated on the stack are considered ๐ŸŒˆ
๐Ÿ’… NOTE: In Ubuntu 14.10 and later, -fstack-protector-strong is enabled by default for C, C++, ObjC, and ObjC++, as long as none of -fno-stack-protector, -nostdlib, or -ffreestanding show up.
  • -fstack-protector-explicit โ€” like -fstack-protector, but only protects the functions that have the stack_protect attribute ๐ŸŽ€
  • -fstack-check โ€” generates code to verify you don't go beyond the boundary of the stack ๐Ÿฅน You should specify this if you're running in an environment with multiple threads. In a single-threaded environment you rarely need it, since stack overflow is automatically detected on nearly all systems when there's only one stack.

Heads up though: this switch doesn't actually do the checking. The operating system or the language runtime has to do that. The switch just generates code that makes sure they see the stack being extended ๐Ÿซถ

You can also give it a string parameter:

  • no means no checking
  • generic forces the use of old-style checking
  • specific uses the best checking method, and is equivalent to bare -fstack-check

Old-style checking is a generic mechanism. It doesn't need any specific target support in the compiler, but it comes with these drawbacks ๐Ÿ˜ญ

  1. Modified allocation strategy for large objects: they're always allocated dynamically if their size goes over a fixed threshold. This may change the semantics of some code.
  2. Fixed limit on the size of the static frame of functions: when it's topped by a particular function, stack checking isn't reliable and the compiler issues a warning.
  3. Inefficiency: because of both the modified allocation strategy and the generic implementation, code performance takes a hit.

Old-style stack checking is also the fallback for specific if the compiler has no target support added for it.

-fstack-check= is designed for Ada's needs, to detect infinite recursion and stack overflows. specific is an excellent choice when compiling Ada code. It's not generally enough to protect against stack-clash attacks, though. To protect against those, you want -fstack-clash-protection ๐Ÿ™ˆ

  • -fstack-clash-protection โ€” generates code to prevent stack clash style attacks ๐Ÿ’ฅ When this is on, the compiler only allocates one page of stack space at a time, and each page gets accessed right after it's allocated. That way allocations can't jump over any stack guard page the operating system provides ๐Ÿฆ‹

Most targets don't fully support stack clash protection. On those that do, -fstack-clash-protection protects dynamic stack allocations. It may also give limited protection for static stack allocations, if the target supports -fstack-check=specific.

๐Ÿ’€ NOTE: In Ubuntu 19.10 and later, -fstack-clash-protection is enabled by default for C, C++, ObjC, and ObjC++, unless -fno-stack-clash-protection is found.
  • -fstack-limit-register=reg, -fstack-limit-symbol=sym, -fno-stack-limit โ€” generates code to make sure the stack doesn't grow beyond a certain value. That value is either the value of a register (reg) or the address of a symbol (sym) ๐ŸŒธ If a larger stack is required, a signal gets raised at run time. On most targets the signal is raised before the stack overruns the boundary, so you can catch it without taking any special precautions ๐Ÿงš

okay so picture this ๐Ÿงš if the stack starts at absolute address 0x80000000 and grows downwards, you can use the flags -fstack-limit-symbol=__stack_limit and -Wl,--defsym,__stack_limit=0x7ffe0000 to enforce a stack limit of 128KB ๐Ÿ’… ngl this may only work with the GNU linker, so keep that in mind ๐Ÿ™ˆ

you can also locally override stack limit checking with the "no_stack_limit" function attribute โœจ

  • -fsplit-stack โ€” okay this one is so iconic ๐Ÿ˜ญ it generates code that automatically splits the stack before it overflows. the resulting program has a discontiguous stack, which can only overflow if the program can't allocate any more memory. it's most useful for threaded programs, because you no longer have to figure out a good stack size for every single thread ๐Ÿงต this is currently only implemented for x86 targets running GNU/Linux ๐Ÿง

When code compiled with -fsplit-stack calls code compiled without it, there might not be much stack space left for that latter code to run in ๐Ÿฅบ if compiling everything (library code included) with -fsplit-stack isn't an option, the linker can fix up those calls so the code compiled without it always gets a large stack ๐Ÿซถ support for this is in the gold linker in GNU binutils release 2.21 and later ๐Ÿ’œ

  • -fstrub=disable โ€” disable stack scrubbing entirely, ignoring any "strub" attributes ๐Ÿ™…โ€โ™€๏ธ See
  • -fstrub=strict โ€” functions default to "strub" mode "disabled" and the rules are strict ๐Ÿ’… only functions tied to "strub"-"callable" modes ("at-calls", "callable" and "always_inline" "internal") are allowed to be "callable" by functions with "strub"-enabled modes ("at-calls" and "internal") ๐Ÿ‘‘
  • -fstrub=relaxed โ€” bring back the default stack scrub ("strub") setting ๐ŸŒธ which means "strub" only turns on when it's required by "strub" attributes on functions and data types. "Relaxed" just means strub contexts can't call functions explicitly tied to "strub" mode "disabled" ๐Ÿฆ‹ this option is only useful for overriding other -fstrub=* options that come before it on the command line ๐Ÿ™ˆ
  • -fstrub=at-calls โ€” enable "at-calls" "strub" mode wherever it's viable โœจ the main use is testing, since it exercises the "strub" machinery in scenarios that stay strictly local to a translation unit. this mode changes function interfaces, so any function visible to other translation units, or any function whose address is taken, won't be affected by this option ๐Ÿ™…โ€โ™€๏ธ optimization options can also affect viability. check the "strub" attribute documentation for the details on viability and eligibility requirements ๐Ÿ“š
  • -fstrub=internal โ€” enable "internal" "strub" mode wherever it's viable ๐Ÿงช mainly for testing, to thoroughly exercise the parts of the "strub" machinery that implement the less efficient but interface-preserving "strub" mode ๐Ÿ’œ functions that wouldn't be affected by this option are pretty uncommon ๐ŸŒˆ
  • -fstrub=all โ€” enable some "strub" mode wherever it's viable ๐Ÿ”ฅ when both strub modes are viable, "at-calls" wins ๐Ÿ† -fdump-ipa-strubm adds function attributes that tell you which mode was picked for each function ๐Ÿ•ต๏ธโ€โ™€๏ธ the main use is testing, to thoroughly exercise the "strub" machinery ๐Ÿงš
  • -fvtable-verify=[std|preinit|none] โ€” okay this is C++ only ๐ŸŽ€ it turns on (or off, if you use -fvtable-verify=none) the security feature that checks at run time, for every virtual call, that the vtable pointer the call goes through is valid for the type of the object and hasn't been corrupted or overwritten ๐Ÿ”’ if an invalid vtable pointer shows up at run time, an error gets reported and the program halts immediately ๐Ÿ’€

This option builds run-time data structures at program startup, and those are what get used to verify the vtable pointers ๐Ÿซง the std and preinit values control when those data structures get built. in both cases they're built before execution reaches main. -fvtable-verify=std builds them after shared libraries have been loaded and initialized, while -fvtable-verify=preinit builds them before shared libraries have been loaded and initialized โฐ

if this option shows up multiple times on the command line with different values, none takes the highest priority over both std and preinit, and preinit takes priority over std ๐Ÿ‘‘

  • -fvtv-debug โ€” used together with -fvtable-verify=std or -fvtable-verify=preinit, this makes the compiler call debug versions of the runtime functions for the vtable verification feature ๐Ÿ› it also logs info about which vtable pointers it finds for each class ๐Ÿ“ that info gets written to a file named vtv_set_ptr_data.log in the directory named by the environment variable VTV_LOGS_DIR if that's defined, or the current working directory otherwise ๐Ÿ“‚
Note: this feature appends data to the log file. if you want a fresh log file, be sure to delete any existing one ๐Ÿ™ˆ
  • -fvtv-counts โ€” this is a debugging flag ๐Ÿ” used together with -fvtable-verify=std or -fvtable-verify=preinit, it makes the compiler keep track of the total number of virtual calls it runs into and the number of verifications it inserts ๐Ÿ“Š it also counts how many calls it inserts to certain run-time library functions and logs that info for each compilation unit ๐Ÿงพ the compiler writes all of this to a file named vtv_count_data.log in the directory named by VTV_LOGS_DIR if that's defined, or the current working directory otherwise ๐Ÿ“‚ it also counts the size of the vtable pointer sets for each class and writes that to vtv_class_set_sizes.log in the same directory ๐ŸŒธ
Note: this feature appends data to the log files. to get fresh log files, be sure to delete any existing ones ๐Ÿ™ˆ
  • -finstrument-functions โ€” generates instrumentation calls for function entry and exit ๐Ÿฆ‹ just after a function is entered and just before it exits, the following profiling functions get called with the address of the current function and its call site ๐Ÿ“ž (on some platforms, __builtin_return_address doesn't work beyond the current function, so the call site info might not be available to the profiling functions otherwise ๐Ÿฅฒ)
void __cyg_profile_func_enter (void *this_fn,
                               void *call_site);
void __cyg_profile_func_exit  (void *this_fn,
                               void *call_site);

the first argument is the address of the start of the current function, which can be looked up exactly in the symbol table ๐Ÿ”Ž

this instrumentation also happens for functions expanded inline inside other functions ๐Ÿงฉ the profiling calls show where, conceptually, the inline function is entered and exited. that means addressable versions of those functions must be available ๐Ÿซถ if all your uses of a function get expanded inline, this might mean extra code size ๐Ÿ“ if you use extern inline in your C code, an addressable version of those functions must be provided too (this is normally the case anyway, but if you get lucky and the optimizer always expands them inline, you might have gotten away without static copies ๐Ÿ€)

a function can be given the attribute no_instrument_function, and then this instrumentation isn't done for it ๐Ÿ™…โ€โ™€๏ธ that's useful for things like the profiling functions above, high-priority interrupt routines, and any functions the profiling functions can't safely be called from (maybe signal handlers, if the profiling routines generate output or allocate memory ๐Ÿ’ฅ)

  • -finstrument-functions-once โ€” this is similar to -finstrument-functions, except the profiling functions only get called once per instrumented function ๐ŸŽฏ the first profiling function is called after the first entry into the instrumented function, and the second one is called before the exit that matches that first entry ๐Ÿ’œ

the definition of "once" here is a little vague ๐ŸŒซ๏ธ because the implementation isn't protected against data races. so the implementation only guarantees that the profiling functions are called at least once per process and at most once per thread ๐Ÿงต the calls are always paired though, meaning if a thread calls the first function, it will call the second one too, unless it never reaches the exit of the instrumented function ๐Ÿซถ

  • -finstrument-functions-exclude-file-list=file,file,... โ€” sets the list of functions that get excluded from instrumentation (see the description of -finstrument-functions) ๐Ÿšซ if the file that contains a function definition matches one of the file entries, then that function doesn't get instrumented ๐Ÿ™ˆ the match is done on substrings, so if the file parameter is a substring of the file name, it counts as a match ๐Ÿ”

for example:

-finstrument-functions-exclude-file-list=/bits/stl,include/sys

this excludes any inline function defined in files whose pathnames contain /bits/stl or include/sys ๐ŸŒท

if, for some reason, you want to include the letter , in one of the sym entries, write ,, instead ๐Ÿง  for example, -finstrument-functions-exclude-file-list=',,tmp' (note the single quotes surrounding the option) ๐ŸŽ€

  • -finstrument-functions-exclude-function-list=sym,sym,... โ€” this is similar to -finstrument-functions-exclude-file-list, but this one sets the list of function names to exclude from instrumentation ๐ŸŒˆ the name that gets matched is the user-visible name, like "vector<int> blah(const vector<int> &)", not the internal mangled name (like _Z4blahRSt6vectorIiSaIiEE) ๐Ÿงš the match is done on substrings too: if the sym parameter is a substring of the function name, it counts as a match ๐Ÿ” for C99 and C++ extended identifiers, the function name must be given in UTF-8, not using universal character names ๐Ÿ’…

๐ŸŽ€ OPTIONS (continued) ๐ŸŽ€

  • -fpatchable-function-entry=N[,M] โ€” okay bestie, this one is so cute ๐Ÿฆ‹ It makes GCC drop N NOPs right at the start of every function, and the function's entry point sits just before the Mth NOP. If you leave M out, it defaults to 0, so the entry point lands right on the first NOP. Those NOPs are basically reserved extra space you can patch with whatever instrumentation you want at run time, but only if the code segment is writable ๐Ÿ’… You control how much space you get by changing the number of NOPs. The NOP used is whatever the internal GCC back-end hook gen_nop emits. This is target-specific and might also depend on the architecture variant and/or other compilation options, so it's not the same everywhere โœจ

For run-time identification, the starting addresses of these areas (their function entries minus M) also get collected into the "__patchable_function_entries" section of the final binary ๐Ÿงš

Also, __attribute__ ((patchable_function_entry (N,M))) beats -fpatchable-function-entry=N,M on the command line ๐Ÿ‘‘ so you can make a single function's area bigger or remove it entirely. If N=0, no pad location gets recorded ๐Ÿ™ˆ

The NOPs go in at the function entry address (and maybe before it, depending on M), even before the prologue. On PowerPC with the ELFv2 ABI, for a function with dual entry points, the local entry point is that same function entry address by default. Check out -msplit-patch-nops if you want to change that ๐ŸŒธ

Heads up: the maximum value of N and M is 65535. On PowerPC with the ELFv2 ABI, for dual-entry-point functions, the supported values for M are 0, 2, 6 and 14 when you are not using -msplit-patch-nops ๐Ÿซถ


๐ŸŒธ Options Controlling the Preprocessor ๐ŸŒธ

These options control the C preprocessor, which runs on every C source file before the actual compilation happens. It's giving prep work ๐Ÿ’–

If you use -E, nothing happens except preprocessing. A few of these options only make sense alongside -E, because they make the preprocessor output unsuitable for actual compilation ๐Ÿ™ˆ

Besides the options listed here, there are a bunch more for controlling include-file search paths, documented in Directory Options. And diagnostics options for the preprocessor are listed in Warning Options ๐Ÿฆ‹

  • -D name โ€” predefine name as a macro, with the definition 1 โœจ
  • -D name=definition โ€” the contents of definition get tokenized and processed as if they showed up during translation phase three in a #define directive. Quick note: the definition gets chopped off at any embedded newline characters ๐Ÿ’€

If you're calling the preprocessor from a shell (or something shell-like), you might need the shell's quoting syntax to protect characters like spaces that mean something in shell syntax ๐Ÿซถ

Want a function-like macro on the command line? Write its argument list in parentheses before the equals sign (if there is one). Parentheses mean stuff to most shells, so quote the whole option. With sh and csh, -D'name(args...)=definition' works ๐Ÿ”ฅ

-D and -U options are processed in the order you typed them on the command line. All -imacros file and -include file options get processed after all the -D and -U ones ๐Ÿ’…

  • -U name โ€” cancels any previous definition of name, whether it was built in or came from a -D option ๐Ÿ˜ญ
  • -include file โ€” processes file as if #include "file" showed up as the very first line of the primary source file. The big difference is that the first directory searched for file is the preprocessor's working directory, not the directory with the main source file. If it's not found there, the search continues through the rest of the #include "..." search chain as normal ๐Ÿงš

If you give multiple -include options, the files get included in the order they appear on the command line ๐ŸŒˆ

  • -imacros file โ€” exactly like -include, except any output made by scanning file gets thrown away. The macros it defines stay defined though. This is the move if you want all the macros from a header without also processing its declarations ๐Ÿ“

Every file given with -imacros gets processed before every file given with -include ๐Ÿ‘‘

  • -undef โ€” doesn't predefine any system-specific or GCC-specific macros. The standard predefined macros still stay defined ๐Ÿ’œ
  • -pthread โ€” defines the extra macros you need to use the POSIX threads library. Use this option consistently for both compilation and linking ๐Ÿ”ฅ It's supported on GNU/Linux targets, most other Unix derivatives, and also on x86 Cygwin and MinGW targets ๐ŸŒˆ
  • -M โ€” instead of outputting the preprocessed result, this spits out a rule suitable for make that describes the dependencies of the main source file โœจ The preprocessor writes one make rule with the object file name for that source file, a colon, and the names of all the included files, including the ones coming from -include or -imacros command-line options ๐Ÿฆ‹

Unless you set it explicitly (with -MT or -MQ), the object file name is the source file name with its suffix swapped for the object file suffix, and with any leading directory parts removed. If there are lots of included files, the rule gets split across several lines using \-newline. The rule has no commands ๐Ÿ’…

This option does not suppress the preprocessor's debug output, like -dM. To keep that debug output from mixing with your dependency rules, explicitly specify the dependency output file with -MF, or use an environment variable like DEPENDENCIES_OUTPUT. Debug output still goes to the regular output stream as normal ๐Ÿ™ˆ

Passing -M to the driver implies -E, and suppresses warnings with an implicit -w ๐Ÿซถ

  • -MM โ€” like -M, but it doesn't mention header files found in system header directories, or header files that are included (directly or indirectly) from such a header ๐ŸŒธ

This means the choice of angle brackets or double quotes in an #include directive doesn't, on its own, decide whether that header shows up in -MM dependency output ๐Ÿ’–

  • -MF file โ€” when used with -M or -MM, this picks a file to write the dependencies to. If you don't give -MF, the preprocessor sends the rules to the same place it would send preprocessed output โœจ

When used with the driver options -MD or -MMD, -MF overrides the default dependency output file ๐Ÿงš

If file is -, the dependencies get written to stdout ๐Ÿฆ‹

  • -MG โ€” used alongside something like -M that asks for dependency generation, -MG assumes missing header files are generated files and adds them to the dependency list without raising an error ๐Ÿ˜ญ The dependency filename is taken straight from the #include directive, with no path prepended. -MG also suppresses preprocessed output, since a missing header makes that useless ๐Ÿ’€

This feature is used for automatically updating makefiles ๐ŸŒˆ

  • -Mno-modules โ€” disables dependency generation for compiled module interfaces ๐Ÿ™ˆ
  • -MP โ€” tells CPP to add a phony target for each dependency other than the main file, so that each one depends on nothing. These dummy rules work around errors make gives when you remove header files without updating the Makefile to match ๐Ÿซถ This is typical output:
test.o: test.c test.h

test.h:
  • -MT target โ€” changes the target of the rule made by dependency generation. By default, CPP takes the name of the main input file, deletes any directory components and file suffix (like .c), and appends the platform's usual object suffix. That result is the target ๐Ÿ‘‘

An -MT option sets the target to be exactly the string you give it ๐Ÿ’… If you want multiple targets, you can put them in a single argument to -MT, or use multiple -MT options ๐ŸŒธ

For example, -MT '$(objpfx)foo.o' might give:

$(objpfx)foo.o: foo.c
  • -MQ target โ€” same as -MT, but it quotes any characters that are special to Make ๐Ÿ”ฅ -MQ '$(objpfx)foo.o' gives:
$$(objpfx)foo.o: foo.c

The default target is automatically quoted, as if it had been given with -MQ ๐Ÿ’–

  • -MD โ€” -MD is equivalent to -M -MF file, except that -E is not implied ๐Ÿงš The driver figures out file based on whether an -o option is given. If it is, the driver uses that argument with a suffix of .d. Otherwise, it takes the name of the input file, removes any directory components and suffix, and slaps on a .d suffix ๐Ÿฆ‹

If -MD is used together with -E, any -o switch is understood to name the dependency output file. But if -MD is used without -E, each -o is understood to name a target object file ๐Ÿ™ˆ

Since -E isn't implied, -MD can generate a dependency output file as a side effect of the compilation process ๐ŸŒˆ

  • -MMD โ€” like -MD, but only mentions user header files, not system header files โœจ
  • -fpreprocessed โ€” tells the preprocessor the input file has already been preprocessed ๐Ÿ’… This suppresses stuff like macro expansion, trigraph conversion, escaped newline splicing, and processing of most directives. The preprocessor still recognizes and removes comments, so you can pass a file preprocessed with -C to the compiler without any problems ๐Ÿซถ In this mode, the integrated preprocessor is basically just a tokenizer for the front ends ๐ŸŒธ

-fpreprocessed is implicit if the input file has one of the extensions .i, .ii or .mi. Those are the extensions GCC uses for preprocessed files made by -save-temps ๐Ÿ’–

  • -fdirectives-only โ€” when preprocessing, handles directives but does not expand macros ๐Ÿ“ How it behaves depends on -E and -fpreprocessed:

With -E, preprocessing is limited to handling directives like "#define", "#ifdef", and "#error". Other preprocessor operations, like macro expansion and trigraph conversion, aren't performed. The -dD option is also implicitly enabled ๐Ÿ‘‘

With -fpreprocessed, predefinition of command-line macros and most builtin macros is disabled. Macros like "__LINE__", which depend on context, are still handled normally. This lets you compile files that were previously preprocessed with "-E -fdirectives-only" โœจ

With both -E and -fpreprocessed, the rules for -fpreprocessed take precedence. This enables full preprocessing of files that were previously preprocessed with "-E -fdirectives-only" ๐ŸŒˆ

  • -fdollars-in-identifiers ๐Ÿ’ฒ โ€” okay so this one lets $ sneak into your identifier names. Accepted, no drama.
  • -fextended-identifiers ๐ŸŒˆ โ€” accepts universal character names and extended characters in identifiers. It's already on by default for C99 (and later C standards) and C++, so you're probably already vibing with it. ๐Ÿ’…
  • -fno-canonical-system-headers ๐Ÿ™…โ€โ™€๏ธ โ€” when preprocessing, this stops GCC from shortening system header paths with canonicalization. No shortcuts, bestie.
  • -fmax-include-depth=depth ๐Ÿช† โ€” sets the max depth of nested #includes. The default is 200, which is plenty, honestly.
  • -fsearch-include-path[=kind] ๐Ÿ”Ž โ€” tells GCC to look for input files on the #include path, not just in the current directory. This is especially clutch with C++20 modules, where both header units and module interface units need to be compiled directly, like this:
g++ -c -std=c++20 -fmodules -fsearch-include-path bits/stdc++.h bits/std.cc

kind defaults to user, which searches the #include "..." path. You can also explicitly ask for system to search the #include <...> path. โœจ

  • -ftabstop=width ๐Ÿ“ โ€” sets the distance between tab stops. This helps the preprocessor report correct column numbers in warnings or errors, even when tabs show up on the line. If the value is less than 1 or greater than 100, the option gets ignored. The default is 8.
  • -ftrack-macro-expansion[=level] ๐Ÿ” โ€” tracks token locations across macro expansions, so the compiler can show you the current macro expansion stack when an error pops up inside a macro. The catch? The preprocessor and compiler eat more memory with this on. ๐Ÿฝ๏ธ The level parameter lets you pick how precisely tokens get tracked, so you can save memory if you need to:
    • Level 0 turns this option off.
    • Level 1 tracks tokens in a degraded mode for minimal memory overhead. In this mode, every token from expanding a function-like macro's argument shares the same location.
    • Level 2 tracks token locations completely. This is the most memory-hungry option, so it's the main character of the RAM budget. ๐Ÿ‘‘

If you give the option with no argument, the default parameter value is 2.

Note that -ftrack-macro-expansion=2 is activated by default. ๐Ÿ’–

  • -fmacro-prefix-map=old=new ๐Ÿ—บ๏ธ โ€” when preprocessing files living in directory old, this expands the __FILE__ and __BASE_FILE__ macros as if the files lived in directory new instead. Using . for new can turn an absolute path into a relative one, which can lead to more reproducible, location-independent builds. This option also affects __builtin_FILE() during compilation. See also -ffile-prefix-map and -fcanon-prefix-map. ๐Ÿงš
  • -fexec-charset=charset ๐Ÿ”ค โ€” sets the execution character set, which is used for string and character constants. The default is UTF-8. charset can be any encoding supported by the system's iconv library routine.
  • -fwide-exec-charset=charset ๐ŸŒ โ€” sets the wide execution character set, used for wide string and character constants. The default is one of UTF-32BE, UTF-32LE, UTF-16BE, or UTF-16LE, whichever matches the width of wchar_t and the big-endian or little-endian byte order used for code generation. Just like -fexec-charset, charset can be any encoding supported by the system's iconv library routine. Heads up though: you'll run into problems with encodings that don't fit exactly in wchar_t. ๐Ÿฅฒ
  • -finput-charset=charset ๐Ÿ“ฅ โ€” sets the input character set, which is used to translate from the input file's character set into the source character set GCC uses. If the locale doesn't specify one, or GCC can't get that info from the locale, the default is UTF-8. The locale or this command-line option can override that, but if they conflict, the command-line option wins. charset can be any encoding supported by the system's iconv library routine.
  • -fpch-deps ๐Ÿ—‚๏ธ โ€” when you're using precompiled headers, this flag makes the dependency-output flags also list the files from the precompiled header's dependencies. Without it, only the precompiled header gets listed, not the files used to create it, since those aren't consulted when a precompiled header is in use.
  • -fpch-preprocess ๐Ÿงฉ โ€” this lets you use a precompiled header together with -E. It drops a special #pragma into the output, #pragma GCC pch_preprocess "filename", to mark where the precompiled header was found and what its filename is. When -fpreprocessed is in use, GCC recognizes this #pragma and loads the PCH.

This option is off by default, because the preprocessed output is only really suitable as input to GCC. It gets switched on by -save-temps. ๐Ÿซถ

You should never write this #pragma in your own code. It's safe to edit the filename, though, if the PCH file lives in a different location. The filename can be absolute or relative to GCC's current directory.

  • -fworking-directory ๐Ÿ“‚ โ€” enables generating linemarkers in the preprocessor output that let the compiler know the working directory at the time of preprocessing. With this on, after the initial linemarker, the preprocessor emits a second linemarker with the current working directory followed by two slashes. GCC uses that directory, when it's in the preprocessed input, as the current working directory in some debugging information formats. This option is implicitly enabled when debugging info is enabled, but you can block it with the negated form -fno-working-directory. If the -P flag is in the command line, this option does nothing, since no #line directives get emitted whatsoever. ๐Ÿ™ˆ
  • -A predicate=answer ๐Ÿ™‹ โ€” makes an assertion with the predicate predicate and the answer answer. This form is preferred over the older -A predicate(answer) form, which is still supported, because it doesn't use shell special characters.
  • -A -predicate=answer ๐Ÿšซ โ€” cancels an assertion with the predicate predicate and the answer answer.
  • -C ๐Ÿ’ฌ โ€” do not discard comments. All comments get passed through to the output file, except for comments in processed directives, which are deleted along with the directive.
โš ๏ธ Be prepared for side effects when using -C. It makes the preprocessor treat comments as tokens in their own right. For example, a comment at the start of what would be a directive line turns that line into an ordinary source line, since the first token on the line is no longer a #.
  • -CC ๐Ÿ’ฌโœจ โ€” do not discard comments, including during macro expansion. This is like -C, except comments inside macros also get passed through to the output file wherever the macro is expanded.

On top of the side effects of -C, -CC converts all C++-style comments inside a macro into C-style comments. That way, later use of the macro can't accidentally comment out the rest of the source line. ๐Ÿ›ก๏ธ

The -CC option is generally used to support lint comments.

  • -P ๐Ÿšซ๐Ÿ“ โ€” inhibits generating linemarkers in the preprocessor output. This might be handy when you're running the preprocessor on something that isn't C code and will be sent to a program that could get confused by the linemarkers.
  • -traditional, -traditional-cpp ๐Ÿ•ฐ๏ธ โ€” tries to imitate pre-standard C preprocessors, instead of ISO C preprocessors. See the GNU CPP manual for details.

Note that GCC doesn't otherwise try to emulate a pre-standard C compiler, and these options only work with the -E switch, or when you invoke CPP explicitly.

  • -trigraphs ๐Ÿ”บ โ€” supports ISO C trigraphs. These are three-character sequences, all starting with ??, that ISO C defines to stand for single characters. For example, ??/ stands for \, so '??/n' is a character constant for a newline.

The nine trigraphs and their replacements are:

Trigraph:       ??(  ??)  ??<  ??>  ??=  ??/  ??'  ??!  ??-
Replacement:      [    ]    {    }    #    \    ^    |    ~

By default, GCC ignores trigraphs, but in standard-conforming modes it converts them. See the -std and -ansi options. ๐Ÿง

  • -remap ๐Ÿ—ƒ๏ธ โ€” enables special code to work around file systems that only allow really short file names, such as MS-DOS.
  • -H ๐ŸŒณ โ€” prints the name of each header file used, along with the other normal activities. Each name is indented to show how deep it sits in the #include stack. Precompiled header files get printed too, even when they turn out to be invalid. An invalid precompiled header gets printed with ...x, and a valid one with ...!.
  • -dletters ๐Ÿงช โ€” makes debugging dumps during compilation, as specified by letters. The flags documented here are the ones relevant to the preprocessor. Other letters are interpreted by the compiler proper, or reserved for future versions of GCC, so they get silently ignored. If you specify letters whose behaviors conflict, the result is undefined. ๐Ÿ’€
    • -dM ๐Ÿ“œ โ€” instead of the normal output, generates a list of #define directives for all the macros defined during preprocessing, including predefined macros. This is a great way to find out what's predefined in your version of the preprocessor. Assuming you have no file foo.h, this command:
touch foo.h; cpp -dM foo.h

shows all the predefined macros.

If you use -dM without the -E option, -dM is interpreted as a synonym for -fdump-rtl-mach. ๐Ÿค“

  • -dD ๐Ÿ“š โ€” like -dM, except it outputs both the #define directives and the result of preprocessing. Both kinds of output go to the standard output file.
  • -dN โœ‚๏ธ โ€” like -dD, but only emits the macro names, not their expansions.
  • -dI ๐Ÿ“Ž โ€” outputs #include directives in addition to the result of preprocessing.
  • -dU โณ โ€” like -dD, except only macros that get expanded, or whose definedness is tested in preprocessor directives, are output. The output is delayed until the macro is used or tested. #undef directives are also output for macros that get tested but are undefined at that time. ๐Ÿฆ‹

๐ŸŽ€ Preprocessor and Assembler Passthrough ๐ŸŽ€

  • -fdebug-cpp ๐Ÿ™ˆ โ€” This one is only useful if you're debugging GCC itself. When it's used from CPP or alongside -E, it dumps debugging info about location maps, and every token in the output gets the dump of whichever map its location belongs to. ๐Ÿ”

When you use it from GCC without -E, it does absolutely nothing. ๐Ÿ’€

  • -Wp,option ๐Ÿงš โ€” This lets you skip the compiler driver and send option straight to the preprocessor. If option has commas in it, it gets split into multiple options at each comma. ๐Ÿ”ช

Heads up, though: a lot of options get modified, translated, or interpreted by the driver before they reach the preprocessor, and -Wp forcibly skips that step. The preprocessor's direct interface is undocumented and can change at any time, so whenever you can, let the driver handle your options and avoid -Wp. ๐Ÿ™…โ€โ™€๏ธ

  • -Xpreprocessor option ๐ŸŒธ โ€” Passes option along to the preprocessor. This is your go-to for system-specific preprocessor options that GCC doesn't recognize. โœจ

If the option you want takes an argument, you need to use -Xpreprocessor twice: once for the option itself and once for the argument. ๐Ÿ’…

  • -no-integrated-cpp ๐Ÿฆ‹ โ€” Runs preprocessing as its own separate pass before compilation. By default, GCC does preprocessing as part of input tokenization and parsing, all integrated and cute. ๐Ÿซถ

With this option, the right language front end (cc1 for C, cc1plus for C++, or cc1obj for Objective-C) gets invoked twice instead: once for preprocessing only, and once for actually compiling the preprocessed input. ๐ŸŒˆ This can be super handy alongside -B or -wrapper if you want an alternate preprocessor or extra processing of your source between the normal preprocessing and compilation. ๐Ÿ”ฅ


๐ŸŽ€ Passing Options to the Assembler ๐ŸŽ€

Okay so you can pass options to the assembler too. ๐Ÿง

  • -Wa,option ๐Ÿ’œ โ€” Passes option along as an option to the assembler. If option has commas, it gets split into multiple options at those commas. ๐Ÿช„
  • -Xassembler option โœจ โ€” Also passes option to the assembler. Use this for system-specific assembler options that GCC doesn't recognize. ๐Ÿฆ‹

If you want to pass an option that takes an argument, you have to use -Xassembler twice, one time for the option and one time for the argument. ๐Ÿ™ˆ


๐ŸŒท Options for Linking ๐ŸŒท

These options come into play when the compiler links object files into an executable output file. They're meaningless if the compiler isn't doing a link step, so don't waste your energy on them then. ๐Ÿ˜ญ

  • object-file-name ๐Ÿ“ โ€” Any file name that doesn't end in a special recognized suffix is treated as an object file or library. The linker tells objects and libraries apart by looking at the file contents. If linking happens, these files get fed into the linker as input. ๐Ÿงš
  • -c, -S, -E ๐Ÿ›‘ โ€” If you use any of these, the linker doesn't run at all, so you shouldn't pass object file names as arguments. ๐Ÿ™…โ€โ™€๏ธ
  • -flinker-output=type ๐Ÿง  โ€” This controls code generation for the link-time optimizer. By default, the linker plugin figures out the linker output type automatically. But if you're debugging the compiler, or you want incremental linking with a non-LTO object file, it can be useful to set the type yourself. ๐Ÿ”ง

Here's what each type does:

  • exec makes code generation produce a static binary. In this case, -fpic and -fpie are both disabled. ๐ŸงŠ
  • dyn makes code generation produce a shared library. Here -fpic or -fPIC gets preserved, but it isn't turned on automatically. This lets you build shared libraries without position-independent code on architectures where that's possible, which means x86. ๐Ÿ’ซ
  • pie makes code generation produce an -fpie executable. You get similar optimizations to exec, except -fpie doesn't get disabled if you specified it at compilation time. ๐Ÿ‘‘
  • rel tells the compiler to assume incremental linking is happening. The sections holding intermediate code for link-time optimization get merged, pre-optimized, and output to the resulting object file. If you also specify -ffat-lto-objects, binary code gets produced for future non-LTO linking. The object file from incremental linking is smaller than a static library made from the same object files, and at link time it loads faster than that static library, as long as most of the objects in the library actually get used. ๐ŸŒธ
  • nolto-rel configures the compiler for incremental linking where code generation is forced, a final binary is produced, and the intermediate code for later link-time optimization gets stripped. When multiple object files are linked together, the result is better optimized than with link-time optimizations disabled (cross-module inlining happens, yay), but most of the benefits of whole program optimizations are lost. ๐Ÿ˜ค

During the incremental link (by -r), the linker plugin defaults to rel. Right now, with the current interfaces to GNU Binutils, it's not possible to incrementally link LTO objects and non-LTO objects into a single mixed object file. If any object file in an incremental link can't be used for link-time optimization, the linker plugin warns you and uses nolto-rel. To keep whole program optimization, it's recommended to link those objects into a static library instead. Alternatively, you can use H.J. Lu's binutils, which supports mixed objects. ๐Ÿฅน

โš ๏ธ Mixing LTO and non-LTO objects in an incremental link isn't possible with the current GNU Binutils interfaces. The plugin will warn you and fall back to nolto-rel, so static library is the safer move if you want whole program optimization. ๐Ÿ’€
  • -fuse-ld=bfd ๐Ÿฃ โ€” Uses the bfd linker instead of the default one. ๐Ÿ”—
  • -fuse-ld=gold ๐ŸŒŸ โ€” Uses the gold linker instead of the default one. โœจ
  • -fuse-ld=lld ๐Ÿ’œ โ€” Uses the LLVM lld linker instead of the default one. ๐Ÿฆ‹
  • -fuse-ld=mold ๐Ÿš€ โ€” Uses the Modern Linker (mold) instead of the default one. ๐Ÿ”ฅ
  • -llibrary, -l library ๐Ÿ“š โ€” Searches the library named library when linking. The second version, with the library as a separate argument, only exists for POSIX compliance, and honestly you shouldn't use it. ๐Ÿ™„

GCC passes -l straight through to the linker, so check your linker's docs for the exact details. The general description below applies to the GNU linker. ๐Ÿง

The linker searches a standard list of directories for the library. That includes several standard system directories, plus anything you add with -L. ๐Ÿ—บ๏ธ

Static libraries are archives of object files, and they have names like liblibrary.a. Some targets also support shared libraries, which usually look like liblibrary.so. If both kinds are found, the linker prefers the shared library, unless you use the -static option. ๐Ÿ’…

Where you put this option in the command actually matters, because the linker searches and processes libraries and object files in the order they're specified. So foo.o -lz bar.o searches library z after foo.o but before bar.o. If bar.o refers to functions in z, those functions might not get loaded. ๐Ÿ˜ญ

  • -lobjc ๐Ÿ’ โ€” You need this special case of -l to link an Objective-C or Objective-C++ program. ๐ŸŒˆ
  • -nostartfiles ๐Ÿ™ˆ โ€” Don't use the standard system startup files when linking. The standard system libraries are still used normally, unless -nostdlib, -nolibc, or -nodefaultlibs is used. ๐ŸŒท
  • -nodefaultlibs ๐Ÿซง โ€” Don't use the standard system libraries when linking. Only the libraries you specify get passed to the linker, and options that set up linkage of the system libraries, like -static-libgcc or -shared-libgcc, get ignored. The standard startup files are still used normally, unless -nostartfiles is used. ๐Ÿงธ

The compiler may generate calls to "memcmp", "memset", "memcpy" and "memmove". These are usually resolved by entries in libc. When you use this option, those entry points need to be supplied some other way. โš ๏ธ

  • -nolibc ๐ŸŒ™ โ€” Don't use the C library, or the system libraries tightly coupled with it, when linking. You'll still link with the startup files, libgcc, and any toolchain-provided language support libraries such as libgnat, libgfortran, or libstdc++, unless options preventing their inclusion are used as well. This typically removes -lc from the link command line, along with system libraries that normally go with it and become meaningless when you're assuming there's no C library, like -lpthread or -lm in some configurations. This is meant for bare-board targets where there genuinely is no C library available. ๐Ÿค–
  • -nostdlib ๐Ÿช โ€” Don't use the standard system startup files or libraries when linking. No startup files get used, and only the libraries you specify get passed to the linker. Options specifying linkage of the system libraries, like -static-libgcc or -shared-libgcc, get ignored. ๐Ÿšซ

The compiler may generate calls to "memcmp", "memset", "memcpy" and "memmove". These are usually resolved by entries in libc, so when this option is specified, those entry points need to be supplied some other way. ๐Ÿฅบ

One of the standard libraries bypassed by -nostdlib and -nodefaultlibs is libgcc.a. It's a library of internal subroutines that GCC uses to get around shortcomings of particular machines, or special needs of some languages. ๐Ÿงฉ

Honestly, you usually still need libgcc.a even when you want to avoid the other standard libraries. So when you specify -nostdlib or -nodefaultlibs, you should usually add -lgcc as well. That way you won't end up with unresolved references to internal GCC library subroutines. (An example of one of those internal subroutines is "__main", which makes sure C++ constructors get called.) ๐Ÿ’–

  • -nostdlib++ ๐Ÿฆข โ€” Don't implicitly link with the standard C++ libraries. ๐Ÿซถ
  • -e entry, --entry=entry ๐ŸŽฏ โ€” Sets the program entry point to entry. The linker interprets the argument, and the GNU linker accepts either a symbol name or an address. ๐Ÿ“
  • -pie ๐Ÿ‘‘ โ€” Produces a dynamically linked position independent executable on targets that support it. For predictable results, you also have to specify the same set of options you used for compilation (-fpie, -fPIE, or model suboptions) when you use this linker option. ๐ŸŒŒ
  • -no-pie ๐Ÿ™…โ€โ™€๏ธ โ€” Doesn't produce a dynamically linked position independent executable. ๐Ÿ’ค
  • -static-pie โ€” โœจ Makes a static position independent executable on targets that support it. It's like a regular static executable, except it can be loaded at any address without a dynamic linker ๐Ÿงš. For predictable results, you also need to use the same options you used for compilation (-fpie, -fPIE, or model suboptions) when you pass this linker option ๐Ÿ’….
  • -pthread โ€” ๐Ÿงต Links with the POSIX threads library. This works on GNU/Linux targets, most other Unix derivatives, and x86 Cygwin and MinGW targets. On some targets it also sets flags for the preprocessor, so use it consistently for both compiling and linking ๐Ÿซถ.
  • -r โ€” ๐Ÿงฉ Produces a relocatable object as output. This is also called partial linking.
  • -rdynamic โ€” ๐ŸŒˆ Passes the flag -export-dynamic to the ELF linker, on targets that support it. The linker then adds all symbols to the dynamic symbol table, not just the ones that get used ๐Ÿ‘‘. This option is needed for some uses of dlopen, or if you want backtraces from inside a program ๐Ÿ”ฅ.
  • -s โ€” ๐Ÿซง Strips all symbol table and relocation information out of the executable.
  • -static โ€” ๐ŸงŠ On systems that support dynamic linking, this overrides -pie and stops linking with shared libraries. On other systems it does nothing ๐Ÿ’€.
  • -shared โ€” ๐Ÿฆ‹ Produces a shared object that can be linked with other objects to form an executable. Not every system supports this one ๐Ÿ™ˆ. For predictable results, you also need the same options used for compilation (-fpic, -fPIC, or model suboptions) when you pass this linker option[1] โœจ.
  • -shared-libgcc, -static-libgcc โ€” ๐ŸŽ€ On systems that ship libgcc as a shared library, these force the shared or static version, respectively. If no shared version of libgcc got built when the compiler was configured, these do nothing ๐ŸŒธ.

There are a few situations where an app should use the shared libgcc instead of the static one. The biggest one is when the app wants to throw and catch exceptions across different shared libraries. In that case, every one of those libraries and the app itself should use the shared libgcc ๐Ÿ˜ญ.

So the G++ driver automatically adds -shared-libgcc whenever you build a shared library or a main executable, since C++ programs usually use exceptions. That's the right move, periodt ๐Ÿ’….

If you use the GCC driver to make shared libraries instead, they might not always get linked with the shared libgcc ๐Ÿค”. If GCC figures out at configuration time that you have a non-GNU linker, or a GNU linker that doesn't support option --eh-frame-hdr, it links the shared version of libgcc into shared libraries by default. Otherwise, it takes advantage of the linker and skips linking with the shared libgcc, so it links with the static version by default. This lets exceptions propagate through those shared libraries without paying relocation costs at library load time โญ.

โš ๏ธ However, if a library or main executable is supposed to throw or catch exceptions, you have to link it using the G++ driver, or use the option -shared-libgcc, so it gets linked with the shared libgcc. Don't skip this one bestie ๐Ÿ˜ญ.
  • -static-libasan โ€” ๐Ÿ›ก๏ธ When -fsanitize=address is used to link a program, the GCC driver automatically links against libasan. If libasan is available as a shared library and -static isn't used, it links the shared version. This option tells the GCC driver to link libasan statically, without necessarily linking other libraries statically ๐Ÿ“.
  • -static-libtsan โ€” ๐Ÿงต When -fsanitize=thread is used to link a program, the GCC driver automatically links against libtsan. If libtsan is a shared library and -static isn't used, it links the shared version. This option tells the GCC driver to link libtsan statically, without necessarily linking other libraries statically ๐Ÿ’œ.
  • -static-liblsan โ€” ๐Ÿ•ต๏ธ When -fsanitize=leak is used to link a program, the GCC driver automatically links against liblsan. If liblsan is a shared library and -static isn't used, it links the shared version. This option tells the GCC driver to link liblsan statically, without necessarily linking other libraries statically ๐ŸŒท.
  • -static-libubsan โ€” ๐Ÿš When -fsanitize=undefined is used to link a program, the GCC driver automatically links against libubsan. If libubsan is a shared library and -static isn't used, it links the shared version. This option tells the GCC driver to link libubsan statically, without necessarily linking other libraries statically ๐ŸŽ€.
  • -static-libstdc++ โ€” ๐Ÿ“š When the g++ program links a C++ program, it normally links against libstdc++ automatically. If libstdc++ is a shared library and -static isn't used, it links the shared version, which is usually fine ๐Ÿ‘Œ. But sometimes you want to freeze the libstdc++ version your program uses without going all the way to a fully static link. This option tells the g++ driver to link libstdc++ statically, without necessarily linking other libraries statically ๐Ÿซถ.
  • -symbolic โ€” ๐Ÿ”— Binds references to global symbols when building a shared object. It warns about any unresolved references (unless you override that with the link editor option -Xlinker -z -Xlinker defs). Only a few systems support this one ๐Ÿฅน.
  • -T script โ€” ๐Ÿ“œ Uses script as the linker script. Most systems using the GNU linker support this. On some targets, like bare-board targets with no operating system, -T may be needed when linking to avoid references to undefined symbols ๐Ÿ’ซ.
  • -Xlinker option โ€” ๐ŸŽ Passes option along to the linker as an option. Handy for system-specific linker options that GCC doesn't recognize ๐Ÿง .

If you want to pass an option that takes a separate argument, you need to use -Xlinker twice: once for the option and once for the argument. For example, to pass -assert definitions, you write -Xlinker -assert -Xlinker definitions. Writing -Xlinker "-assert definitions" does not work, because that sends the whole string as one argument, and the linker doesn't want that ๐Ÿ™…โ€โ™€๏ธ.

With the GNU linker it's usually easier to pass arguments using the option=value syntax instead of separate arguments. For example, you can write -Xlinker -Map=output.map instead of -Xlinker -Map -Xlinker output.map. Other linkers might not support this syntax for command-line options ๐Ÿคทโ€โ™€๏ธ.

  • -Wl,option โ€” ๐ŸŒŸ Also passes option to the linker. If option has commas in it, it gets split into multiple options at each comma. You can use this syntax to pass an argument to an option. For example, -Wl,-Map,output.map passes -Map output.map to the linker. With the GNU linker you can also get the same effect with -Wl,-Map=output.map ๐Ÿญ.
๐Ÿ“ข NOTE: In Ubuntu 8.10 and later, for LDFLAGS, the option -Wl,-z,relro is used. To turn that off, use -Wl,-z,norelro ๐Ÿ™ˆ.
  • -u symbol โ€” ๐ŸŽฃ Pretends the symbol symbol is undefined, which forces the linking of library modules that define it. You can use -u multiple times with different symbols to force loading of more library modules ๐Ÿงฒ.
  • -z keyword โ€” โš™๏ธ -z gets passed straight to the linker along with the keyword. Check the documentation for your linker to see the permitted values and what they mean ๐Ÿ“–.

These options tell GCC which directories to search for header files, libraries, and parts of the compiler ๐Ÿ—‚๏ธ:

  • -I dir
  • -iquote dir
  • -isystem dir
  • -idirafter dir โ€” ๐Ÿ“ Adds the directory dir to the list of directories searched for header files during preprocessing. If dir starts with = or $SYSROOT, that = or $SYSROOT gets swapped out for the sysroot prefix. See --sysroot and -isysroot for more on that ๐ŸŒธ.

Directories given with -iquote only apply to the quote form of the directive, "#include "file"". Directories given with -I, -isystem, or -idirafter apply to lookups for both "#include "file"" and "#include <file>" ๐Ÿ’–.

You can mix and match any number of these options on the command line to search for headers in lots of places at once ๐Ÿฆ‹. The lookup order goes like this:

  1. For the quote form of the include directive, the directory of the current file gets searched first.
  2. For the quote form, the directories from -iquote options get searched left to right, in the order they show up on the command line.
  3. Directories from -I options get scanned left to right.
  4. Directories from -isystem options get scanned left to right.
  5. Standard system directories get scanned.
  6. Directories from -idirafter options get scanned left to right.

You can use -I to override a system header file and swap in your own version, since those directories get searched before the standard system header directories ๐Ÿซข. Just don't use this option to add directories full of vendor-supplied system headers. Use -isystem for that ๐Ÿšซ.

The -isystem and -idirafter options also mark the directory as a system directory, so it gets the same special treatment as the standard system directories ๐Ÿ‘‘.

If a standard system include directory, or a directory given with -isystem, is also given with -I, the -I option gets ignored. The directory still gets searched, but as a system directory in its normal spot in the system include chain. This keeps GCC's fix-up for buggy system headers and the ordering for the #include_next directive from getting messed up by accident ๐Ÿ˜ญ. If you really need to change the search order for system directories, use the -nostdinc and/or -isystem options ๐Ÿงš.

  • -I- โ€” Splitting the include path, ya know? โœ‚๏ธ This one is deprecated, so please don't reach for it. Use -iquote for any -I directories that come before the -I-, and then drop the -I- altogether. ๐Ÿ™…โ€โ™€๏ธ

Any -I directories placed before -I- only get searched for headers pulled in with #include "file". They're skipped for #include <file>. Any -I directories placed after the -I- get searched for every #include directive. ๐Ÿ—บ๏ธ

On top of that, -I- stops the directory of the current file from being used as the first place to look for #include "file". And there's no way to undo that effect of -I-, so choose wisely, bestie. ๐Ÿ’€

  • -iprefix prefix โ€” Sets prefix as the prefix that later -iwithprefix options will use. ๐ŸŽ€ If the prefix is a directory, make sure you end it with a final /. ๐Ÿ“‚
  • -iwithprefix dir, -iwithprefixbefore dir โ€” Tacks dir onto the prefix you set earlier with -iprefix, then adds the result to the include search path. โœจ

-iwithprefixbefore slots it in the same spot -I would. -iwithprefix puts it where -idirafter would. ๐Ÿงš

  • -isysroot dir โ€” Basically the same vibe as --sysroot, but it only applies to header files. ๐Ÿ“š The exception is Darwin targets, where it covers both header files and libraries. ๐ŸŽ Check out the --sysroot option for the full tea. โ˜•
  • -imultilib dir โ€” Uses dir as a subdirectory of the directory that holds the target-specific C++ headers. ๐ŸŒธ
  • -nostdinc โ€” Tells the compiler to skip the standard system directories when hunting for header files. ๐Ÿ™ˆ Only the directories you explicitly give with -I, -iquote, -isystem, and/or -idirafter get searched (plus the directory of the current file, if that makes sense for the situation). ๐Ÿ’…
  • -nostdinc++ โ€” Skips the C++-specific standard directories when searching for headers, but still checks the other standard directories. ๐Ÿซถ (This one is used when building the C++ library, so it's giving behind-the-scenes energy. ๐Ÿ”ฅ)
  • --embed-dir=dir โ€” Adds the dir directory to the list of places searched by the #embed preprocessing directive or the __has_embed macro. ๐Ÿฆ‹ There are no default directories for #embed, so you gotta add your own. ๐Ÿ˜ญ

If dir starts with = or $SYSROOT, that = or $SYSROOT gets swapped out for the sysroot prefix. Peep --sysroot and -isysroot for more on that. ๐Ÿ‘‘

  • -iplugindir=dir โ€” Sets the directory where plugins are searched for when they're passed with -fplugin=name instead of -fplugin=path/name.so. ๐Ÿ”Œ This one isn't really meant for you to use directly. It's only passed along by the driver, so let the driver handle it. ๐Ÿค–
  • -Ldir โ€” Adds the directory dir to the list of directories searched for -l libraries. ๐Ÿ“ฆ
  • -Bprefix โ€” This is where you tell the compiler where to find its own executables, libraries, include files, and data files. ๐Ÿ—‚๏ธ

The compiler driver runs one or more of these subprograms: cpp, cc1, as, and ld. ๐Ÿ› ๏ธ For each one it runs, it tries prefix as a prefix, both with and without machine/version/ for the matching target machine and compiler version. ๐ŸŒˆ

For each subprogram, the driver checks the -B prefix first (if you gave one). If that doesn't turn anything up, or if -B wasn't specified, it tries two standard prefixes: /usr/lib/gcc/ and /usr/local/lib/gcc/. ๐Ÿ” If none of those produce a file that actually exists, the plain program name gets searched for using the directories listed in your PATH environment variable. ๐Ÿซ 

The compiler checks whether the path you passed to -B is a directory. If it is, the compiler adds a directory separator character at the end if it's needed. ๐Ÿ“

-B prefixes that act like directory names also apply to libraries in the linker, because the compiler turns those options into -L options for the linker. ๐Ÿ”— They also apply to include files in the preprocessor, because the compiler turns them into -isystem options for the preprocessor. In that case the compiler appends include to the prefix. ๐Ÿ’–

The runtime support file libgcc.a can also be searched for using the -B prefix, if needed. If it's not found there, only the two standard prefixes above get tried, and that's the whole list. ๐Ÿ™ƒ If it's still missing after all that, it gets left out of the link. ๐Ÿ’€

Another way to set a prefix that works like the -B prefix is the environment variable GCC_EXEC_PREFIX. ๐ŸŒท

Okay, here's the lowkey kludge ๐Ÿงธ: if the path given to -B is [dir/]stageN/, where N is a number from 0 to 9, it gets replaced with [dir/]include. This is there to help with bootstrapping the compiler. ๐Ÿฅพ

  • -no-canonical-prefixes โ€” Don't expand any symbolic links, don't resolve references to /../ or /./, and don't make the path absolute when generating a relative prefix. ๐Ÿงญ
  • --sysroot=dir โ€” Makes dir the logical root directory for headers and libraries. ๐ŸŒณ For example, if the compiler normally looks for headers in /usr/include and libraries in /usr/lib, it'll look in dir/usr/include and dir/usr/lib instead. ๐Ÿช„

If you use this option and the -isysroot option, then --sysroot applies to libraries, while -isysroot applies to header files. ๐Ÿค

The GNU linker (starting with version 2.16) has the support needed for this option. If your linker doesn't support it, the header file part of --sysroot still works, but the library part doesn't. ๐Ÿ˜ค

  • --no-sysroot-suffix โ€” For some targets, a suffix gets added to the root directory set with --sysroot, depending on what other options you used. So headers might get found in dir/suffix/usr/include instead of dir/usr/include. ๐Ÿงฉ This option turns off adding that suffix. ๐Ÿšซ

โœจ Options for Code Generation Conventions โœจ

These machine-independent options control the interface conventions the compiler uses during code generation. ๐Ÿง 

Most of them come in positive and negative forms. The negative form of -ffoo is -fno-foo. ๐Ÿ”„ In the list below, only one form is shown, the one that is not the default. You can work out the other one by either removing no- or adding it. ๐Ÿชž

  • -fstack-reuse=reuse-level โ€” This option controls stack space reuse for user-declared local/auto variables and compiler-generated temporaries. ๐Ÿฅž reuse_level can be all, named_vars, or none. all turns on stack reuse for every local variable and temporary. named_vars only turns on reuse for user-defined local variables that have names. none turns stack reuse off completely. The default is all. ๐Ÿ’œ

You'd need this option when a program stretches the lifetime of a scoped local variable or a compiler-generated temporary past the end point the language defines. โณ When a variable's lifetime ends and it lives in memory, the optimizing compiler is free to reuse its stack space for other temporaries or scoped local variables whose live range doesn't overlap with it. ๐Ÿซง Legacy code that stretches local lifetimes is likely to break with the stack reuse optimization. ๐Ÿ’€

For example,

                  int *p;
                  {
                    int local1;

                    p = &local1;
                    local1 = 10;
                    ....
                  }
                  {
                     int local2;
                     local2 = 20;
                     ...
                  }

                  if (*p == 10)  // out of scope use of local1
                    {

                    }

Another example:

                  struct A
                  {
                      A(int k) : i(k), j(k) { }
                      int i;
                      int j;
                  };

                  A *ap;

                  void foo(const A& ar)
                  {
                     ap = &ar;
                  }

                  void bar()
                  {
                     foo(A(10)); // temp object's lifetime ends when foo returns

                     {
                       A a(20);
                       ....
                     }
                     ap->i+= 10;  // ap references out of scope temp whose space
                                  // is reused with a. What is the value of ap->i?
                  }

The C++ standard clearly defines the lifetime of a compiler-generated temporary. ๐Ÿ“œ When that lifetime ends and the temporary lives in memory, the optimizing compiler can reuse its stack space for other temporaries or scoped local variables whose live range doesn't overlap. ๐ŸŒช๏ธ But some legacy code depends on older compilers, where temporaries' stack space was never reused, so the aggressive reuse can lead to runtime errors. ๐Ÿ˜ฑ This option is what lets you control that temporary stack reuse optimization. ๐ŸŽ›๏ธ

  • -ftrapv โ€” Generates traps for signed overflow on addition, subtraction, and multiplication operations. ๐Ÿšจ The options -ftrapv and -fwrapv override each other, so -ftrapv -fwrapv on the command line means -fwrapv is the one that's effective. ๐Ÿซ  Only active options override, though, so -ftrapv -fwrapv -fno-wrapv on the command line means -ftrapv is the one that's effective. ๐Ÿคฏ
  • -fwrapv โ€” Tells the compiler to assume that signed arithmetic overflow from addition, subtraction, and multiplication wraps around using two's-complement representation. ๐Ÿ” This flag enables some optimizations and disables others. ๐ŸŽญ The options -ftrapv and -fwrapv override each other, so -ftrapv -fwrapv on the command line means -fwrapv is effective. ๐Ÿ’… Only active options override, so -ftrapv -fwrapv -fno-wrapv on the command line means -ftrapv is effective. โœจ
  • -fwrapv-pointer โ€” Tells the compiler to assume that pointer arithmetic overflow on addition and subtraction wraps around using two's-complement representation. ๐ŸŒ€ This flag disables some optimizations that assume pointer overflow is invalid. ๐Ÿ™…โ€โ™€๏ธ
  • -fstrict-overflow โ€” This option implies -fno-wrapv -fno-wrapv-pointer, and when it's negated, it implies -fwrapv -fwrapv-pointer. ๐Ÿฆ„
  • -fexceptions โ€” okay so this turns on exception handling ๐Ÿ’– It generates the extra code needed to propagate exceptions. On some targets it also makes GCC emit frame unwind info for every function, which can bloat your data size a lot (it doesn't slow anything down though ๐Ÿซถ). If you don't say anything, GCC turns it on for languages like C++ that normally need it and leaves it off for languages like C that don't. Still, you might need to flip it on when your C code has to play nicely with exception handlers written in C++ ๐ŸŽ€ And if you're compiling older C++ programs that never use exception handling, you may want to switch it off. โœจ
  • -fnon-call-exceptions โ€” this one lets trapping instructions throw exceptions ๐Ÿ”ฅ Heads up, it needs platform-specific runtime support that doesn't exist everywhere, so check before you get attached. It only lets trapping instructions (memory references or floating-point instructions) throw. It does not let exceptions come out of arbitrary signal handlers like "SIGALRM" ๐Ÿ™ˆ Also, it turns on -fexceptions automatically.
  • -fdelete-dead-exceptions โ€” this basically says instructions that might throw but don't actually contribute to the program's execution can get optimized away ๐Ÿงน It doesn't touch calls to functions, except the ones marked with the "pure" or "const" attributes. It's on by default for the Ada and C++ compilers, since the language specs allow it. Which optimization passes actually remove dead exceptions depends on the optimization level, and each pass is enabled on its own schedule. ๐Ÿ’…
  • -funwind-tables โ€” similar to -fexceptions, except it only generates the static data that's needed and leaves the actual generated code alone ๐Ÿฆ‹ You usually don't have to turn this on yourself, because any language processor that needs this handling switches it on for you. Bestie, you're covered. ๐Ÿซถ
  • -fasynchronous-unwind-tables โ€” generates an unwind table in DWARF format, as long as the target machine supports it โœจ The table is exact at every instruction boundary, which means it can be used for stack unwinding during asynchronous events, like when a debugger or a garbage collector shows up mid-run ๐Ÿ‘‘
  • -fno-gnu-unique โ€” okay this one is a little dramatic ๐Ÿ˜ญ On systems with a recent GNU assembler and C library, the C++ compiler uses the "STB_GNU_UNIQUE" binding so that template static data members and static local variables in inline functions stay unique, even with "RTLD_LOCAL" in the mix. That matters because otherwise a library used by two different "RTLD_LOCAL" plugins could end up disagreeing about what a symbol binds to. The catch is that this makes dlclose get ignored for the affected DSOs. If your program relies on reinitializing a DSO through dlclose and dlopen, reach for -fno-gnu-unique ๐ŸŒธ
  • -fpcc-struct-return โ€” returns "short" "struct" and "union" values in memory, just like the longer ones, instead of in registers ๐Ÿงบ It's less efficient, but it lets code compiled by GCC play nicely with files compiled by other compilers, especially the Portable C Compiler (pcc) ๐Ÿซถ

The exact convention for returning structures in memory depends on the target configuration macros, so it's not one-size-fits-all.

Short structures and unions are the ones whose size and alignment match some integer type. Cute and specific, iykyk. ๐ŸŽ€

Warning: code compiled with -fpcc-struct-return is not binary compatible with code compiled with -freg-struct-return. Only use it when you need to conform to a non-default application binary interface. ๐Ÿ’€
  • -freg-struct-return โ€” returns "struct" and "union" values in registers whenever it's possible ๐Ÿš€ For small structures this is more efficient than -fpcc-struct-return.

If you don't specify either -fpcc-struct-return or -freg-struct-return, GCC just goes with whatever convention is standard for the target. If there's no standard convention, GCC defaults to -fpcc-struct-return, except on targets where GCC is the principal compiler. In those cases, GCC gets to pick the standard, and it picked the more efficient register return option. Main character energy ๐Ÿ‘‘

Warning: code compiled with -freg-struct-return is not binary compatible with code compiled with -fpcc-struct-return. Only use it when you need to conform to a non-default application binary interface. ๐Ÿ’€
  • -fshort-enums โ€” gives an "enum" type only as many bytes as it needs to hold its declared range of possible values ๐Ÿ“ To be precise, the "enum" type becomes the smallest integer type with enough room. This does nothing for an enumeration type that has a fixed underlying type.
Warning: -fshort-enums makes GCC generate code that is not binary compatible with code generated without that switch. Only use it to conform to a non-default application binary interface. โš ๏ธ
  • -fshort-wchar โ€” overrides the underlying type of "wchar_t" so it's "short unsigned int" instead of the target's default ๐ŸŒˆ This one is handy for building programs that run under WINE.
Warning: -fshort-wchar makes GCC generate code that is not binary compatible with code generated without that switch. Only use it to conform to a non-default application binary interface. ๐Ÿ’€
  • -fcommon โ€” in C code, this option controls where global variables defined without an initializer get placed. Those are called tentative definitions in the C standard ๐Ÿงš They're different from declarations of a variable with the "extern" keyword, which don't allocate any storage.

The default is -fno-common, which means the compiler puts uninitialized global variables in the BSS section of the object file. This stops the linker from merging tentative definitions, so you'll get a multiple-definition error if the same variable is accidentally defined in more than one compilation unit. Honestly, that error is saving you. ๐Ÿซถ

-fcommon puts uninitialized global variables in a common block instead. That lets the linker resolve all the tentative definitions of the same variable across different compilation units to the same object, or to a non-tentative definition. The downside: this behavior is inconsistent with C++, and on many targets it costs some speed and code size on global variable references. It's mainly useful for getting legacy code to link without errors ๐Ÿฅฒ

  • -fno-ident โ€” tells GCC to ignore the #ident directive ๐Ÿ™ˆ
  • -finhibit-size-directive โ€” don't output a .size assembler directive, or anything else that would cause trouble if the function gets split in the middle with the two halves placed far apart in memory ๐Ÿ’œ This is used when compiling crtstuff.c. You shouldn't need it for anything else, bestie.
  • -fverbose-asm โ€” puts extra commentary into the generated assembly so it's easier to read ๐Ÿ“– This is really only useful for people who actually need to read the generated assembly, like when they're debugging the compiler itself ๐Ÿค“

-fno-verbose-asm, which is the default, leaves that extra info out. That's the one you want when you're comparing two assembler files. ๐Ÿ’…

The added comments include:

  • information on the compiler version and command-line options,
  • the source code lines associated with the assembly instructions, in the form FILENAME:LINENUMBER:CONTENT OF LINE,
  • hints on which high-level expressions correspond to the various assembly instruction operands.

Here's an example. Say you have this C source file:

        int test (int n)
        {
          int i;
          int total = 0;

          for (i = 0; i < n; i++)
            total += i * i;

          return total;
        }

Compiling it to (x86_64) assembly with -S, and sending the result straight to stdout with -o -:

gcc -S test.c -fverbose-asm -Os -o -

gives output similar to this:

                .file   "test.c"
        # GNU C11 (GCC) version 7.0.0 20160809 (experimental) (x86_64-pc-linux-gnu)
          [...snip...]
        # options passed:
          [...snip...]

                .text
                .globl  test
                .type   test, @function
        test:
        .LFB0:
                .cfi_startproc
        # test.c:4:   int total = 0;
                xorl    %eax, %eax      # <retval>
        # test.c:6:   for (i = 0; i < n; i++)
                xorl    %edx, %edx      # i
        .L2:
        # test.c:6:   for (i = 0; i < n; i++)
                cmpl    %edi, %edx      # n, i
                jge     .L5     #,
        # test.c:7:     total += i * i;
                movl    %edx, %ecx      # i, tmp92
                imull   %edx, %ecx      # i, tmp92
        # test.c:6:   for (i = 0; i < n; i++)
                incl    %edx    # i
        # test.c:7:     total += i * i;
                addl    %ecx, %eax      # tmp92, <retval>
                jmp     .L2     #
        .L5:
        # test.c:10: }
                ret
                .cfi_endproc
        .LFE0:
                .size   test, .-test
                .ident  "GCC: (GNU) 7.0.0 20160809 (experimental)"
                .section        .note.GNU-stack,"",@progbits

The comments are meant for humans rather than machines, so the precise format is subject to change. Don't build your life on it ๐Ÿฆ‹

  • -frecord-gcc-switches โ€” this records the command line you used to invoke the compiler into the object file being created ๐Ÿ“ผ It's only implemented on some targets, and the exact format depends on the target and binary file format. Usually it takes the form of a section containing ASCII text. This one is related to -fverbose-asm, but that switch only puts info into the assembler output as comments, so it never makes it into the object file. If you want another way to store compiler options in the object file, check out -grecord-gcc-switches โœจ
  • -fpic โ€” ๐Ÿ’– Generates position-independent code (PIC) that's ready for a shared library, as long as your target machine supports it. All constant addresses get accessed through a global offset table (GOT), and the dynamic loader fills in those GOT entries when the program starts. (The dynamic loader isn't part of GCC, it's part of the operating system, so don't blame GCC for it ๐Ÿ™„.) If the GOT for your linked executable goes over a machine-specific maximum, the linker throws an error saying -fpic doesn't work, and then you should recompile with -fPIC instead. The maximums are 8k on the SPARC, 28k on AArch64, and 32k on the m68k and RS/6000. The x86 has no such limit ๐Ÿฆ‹.

PIC needs special support, so it only works on certain machines. For the x86, GCC supports PIC for System V but not for the Sun 386i ๐Ÿ’€. Code generated for the IBM RS/6000 is always position-independent โœจ.

When this flag is set, the macros __pic__ and __PIC__ are defined to 1 ๐ŸŒธ.

  • -fPIC โ€” ๐Ÿ”ฅ If your target machine supports it, this emits position-independent code that's suitable for dynamic linking and doesn't have any limit on the size of the global offset table. It makes a real difference on AArch64, m68k, PowerPC and SPARC.

PIC needs special support, so it only works on certain machines ๐Ÿ’….

When this flag is set, the macros __pic__ and __PIC__ are defined to 2 ๐Ÿ’œ.

  • -fpie, -fPIE โ€” ๐Ÿงš These are basically the same vibe as -fpic and -fPIC, except the position-independent code they generate can only be linked into executables. Usually you'd use them to compile code that's going to get linked with the -pie GCC option ๐ŸŽ€.

-fpie and -fPIE both define the macros __pie__ and __PIE__. Those macros have the value 1 for -fpie and 2 for -fPIE โœจ.

  • -fno-plt โ€” ๐Ÿ‘‘ Don't use the PLT for external function calls in position-independent code. Instead, the callee address gets loaded at the call sites from the GOT, and then the code branches to it. This is more efficient because it gets rid of PLT stubs and exposes GOT loads to optimizations ๐Ÿ”ฅ. On architectures like 32-bit x86, where PLT stubs expect the GOT pointer in one specific register, this gives the compiler more register allocation freedom ๐Ÿ™Œ. Heads up: lazy binding needs the PLT, so with -fno-plt, all external symbols get resolved at load time โญ.

If you only want to skip the PLT for specific external functions, use the function attribute noplt instead ๐Ÿซถ.

In position-dependent code, a few targets will also turn calls to functions marked to not use the PLT into calls through the GOT instead ๐ŸŒˆ.

  • -fno-jump-tables โ€” ๐Ÿ™ˆ Don't use jump tables for switch statements, even when they'd be more efficient than other code generation strategies. This is handy alongside -fpic or -fPIC when you're building code that's part of a dynamic linker and can't reference the address of a jump table ๐Ÿงš. On some targets, jump tables don't need a GOT, so you don't need this option there ๐Ÿ’….
  • -fno-bit-tests โ€” ๐Ÿ“ Don't use bit tests for switch statements, even if they'd be more efficient than other code generation strategies โœจ.
  • -ffixed-reg โ€” ๐Ÿ”’ Treat the register named reg as a fixed register. The generated code should never touch it (unless it's maybe being used as a stack pointer, frame pointer, or some other fixed role) ๐ŸŽ€.

reg has to be an actual register name. The names you can use depend on the machine, and they're defined in the REGISTER_NAMES macro in the machine description macro file ๐Ÿค“.

This flag doesn't have a negative form, because it's a three-way choice ๐Ÿ’ซ.

  • -fcall-used-reg โ€” ๐Ÿฆ‹ Treat the register named reg as an allocable register that gets clobbered by function calls. It can be allocated for temporaries or for variables that don't live across a call. Functions compiled this way do not save and restore the register reg ๐Ÿ˜ญ.

It's an error to use this flag with the frame pointer or stack pointer ๐Ÿšซ. Using it on other registers with fixed, pervasive roles in the machine's execution model gives disastrous results ๐Ÿ’€.

This flag doesn't have a negative form either, since it's a three-way choice ๐Ÿ’….

  • -fcall-saved-reg โ€” ๐Ÿ›ก๏ธ Treat the register named reg as an allocable register that's saved by functions. It can be allocated even for temporaries or variables that live across a call. Functions compiled this way save and restore reg if they use it ๐Ÿซถ.

Same deal as before: it's an error to use this with the frame pointer or stack pointer โŒ. Using it on other registers with fixed, pervasive roles in the machine's execution model produces disastrous results ๐Ÿ˜ฑ.

A different kind of disaster happens if you use this flag on a register where function values get returned ๐Ÿ™ˆ.

Again, no negative form, because it's a three-way choice โœจ.

  • -fpack-struct[=n] โ€” ๐Ÿ“ฆ With no value, this packs all structure members together with no holes. When you give it a value, which has to be a small power of two, structure members get packed according to that value, which represents the maximum alignment. Objects whose default alignment requirements are bigger than this get output potentially unaligned at the next fitting location ๐ŸŒธ.
โš ๏ธ Warning: -fpack-struct makes GCC generate code that is not binary compatible with code generated without that switch. It also makes the code suboptimal ๐Ÿ’€. Only use it to conform to a non-default application binary interface ๐Ÿ™.
  • -fleading-underscore โ€” ๐ŸŽ€ This one and its counterpart, -fno-leading-underscore, forcibly change how C symbols are represented in the object file. One use for it is linking with legacy assembly code ๐Ÿงธ.
โš ๏ธ Warning: -fleading-underscore makes GCC generate code that is not binary compatible with code generated without that switch. Only use it to conform to a non-default application binary interface. Also, not every target fully supports this switch ๐Ÿ˜ฌ.
  • -ftls-model=model โ€” ๐Ÿงต Changes the thread-local storage model. The model argument should be one of global-dynamic, local-dynamic, initial-exec or local-exec. The choice is subject to optimization, though: the compiler might pick a more efficient model for symbols that aren't visible outside the translation unit, or if -fpic isn't given on the command line โœจ.

Without -fpic, the default is initial-exec. With -fpic, the default is global-dynamic ๐Ÿ’–.

  • -ftrampolines โ€” ๐Ÿช„ For targets that normally need trampolines for nested functions, this always generates them instead of using descriptors. For targets that don't need them, like HP-PA or IA-64, it does nothing ๐ŸŒˆ.

A trampoline is a tiny bit of code created at run time on the stack when the address of a nested function is taken. It gets used to call the nested function indirectly. Because of that, the stack has to be made executable for the program to work properly ๐Ÿ”“.

-fno-trampolines is enabled by default on a language-by-language basis, so the compiler can avoid generating trampolines when it works out that it's safe, and replace them with descriptors instead ๐Ÿง . Descriptors are made up of data only, but the generated code has to be ready to deal with them. As of this writing, -fno-trampolines is enabled by default only for Ada ๐Ÿ‘‘.

Heads up: code compiled with -ftrampolines and code compiled with -fno-trampolines are not binary compatible if nested functions are present. So this option has to be used program-wide, and you need to handle it with extreme care ๐Ÿซฃ.

For languages other than Ada, the -ftrampolines and -fno-trampolines options currently have no effect, and trampolines are always generated on platforms that need them for nested functions ๐Ÿ’….

  • -ftrampoline-impl=[stack|heap] โ€” ๐Ÿ  By default, trampolines are generated on the stack. But some platforms, like the Apple M1, don't allow an executable stack ๐Ÿ™…. Compiling with -ftrampoline-impl=heap makes GCC generate calls to __gcc_nested_func_ptr_created and __gcc_nested_func_ptr_deleted so that trampoline space gets allocated and deallocated on the executable heap. These functions are implemented in libgcc, and they'll only be provided on specific targets: x86_64 Darwin, and x86_64 and aarch64 Linux ๐ŸŒธ.
๐Ÿšจ PLEASE NOTE: Heap trampolines are not guaranteed to be correctly deallocated if you setjmp, instantiate nested functions, and then longjmp back to a state prior to having allocated those nested functions ๐Ÿ˜ญ.
  • -fvisibility=[default|internal|hidden|protected] โ€” ๐Ÿ‘€ Sets the default ELF image symbol visibility to the option you specify. Every symbol gets marked with this unless it's overridden within the code. This can really improve linking and load times for shared object libraries, produce more optimized code, give you near-perfect API export, and prevent symbol clashes. It's strongly recommended that you use it in any shared objects you distribute ๐Ÿซถ.

Despite the name, default always means public, i.e., available to be linked against from outside the shared object ๐Ÿค”. protected and internal are pretty useless in real-world usage, so the only other commonly used option is hidden. If you don't specify -fvisibility, the default is default, meaning every symbol is public ๐ŸŒˆ.

For a good explanation of why ELF symbols should have the correct visibility, check out "How To Write Shared Libraries" by Ulrich Drepper, which you can find at <https://www.akkadia.org/drepper/> ๐Ÿ“š. But here's the superior move this option makes possible: instead of marking things hidden when the default is public, make the default hidden and mark things public. This is the norm with DLLs on Windows. With -fvisibility=hidden and __attribute__ ((visibility("default"))) in place of __declspec(dllexport), you get almost identical semantics with identical syntax. This is a huge win for anyone working on cross-platform projects ๐Ÿ’œโœจ.

For those adding visibility support to existing code, #pragma GCC visibility is a total lifesaver ๐Ÿซถ. You wrap the declarations you want to style with something like #pragma GCC visibility push(hidden), and then close it with #pragma GCC visibility pop. โœจ Now, bestie, symbol visibility is part of the API interface contract, so all new code should always spell out visibility whenever it isn't the default. Declarations that are only used inside the local DSO should be marked hidden explicitly, so you skip the PLT indirection overhead ๐Ÿ’…. Being this clear also makes the code easier to read and basically documents itself ๐Ÿ“–๐Ÿ’–. One thing to know: because of ISO C++ spec requirements, operator new and operator delete must always have default visibility ๐Ÿšซ.

Be careful with headers from outside your project, like system headers and headers from any other library you use ๐Ÿ™ˆ. They may not expect to be compiled with anything other than the default visibility. You might need to say #pragma GCC visibility push(default) before including them.

extern declarations aren't affected by -fvisibility, so a ton of code can be recompiled with -fvisibility=hidden with zero changes ๐Ÿฆ‹. The catch is that calls to extern functions with no explicit visibility go through the PLT. So it's way more effective to use __attribute ((visibility)) and/or #pragma GCC visibility to tell the compiler which extern declarations should be treated as hidden ๐Ÿ‘‘.

Also, -fvisibility does affect C++ vague linkage entities ๐Ÿ‘€. For example, an exception class that gets thrown between DSOs must be explicitly marked with default visibility, so the type_info nodes get unified between the DSOs ๐Ÿ˜ญ.

If you want an overview of these techniques, what they do for you, and how to use them, check out <https://gcc.gnu.org/wiki/Visibility> ๐ŸŒธ.


โœจ -fstrict-volatile-bitfields โœจ

  • -fstrict-volatile-bitfields โ€” Use this when accesses to volatile bit-fields (or other structure fields, though the compiler usually respects those types anyway) should be a single access at the width of the field's type, aligned to natural alignment if possible ๐ŸŽ€. For example, targets with memory-mapped peripheral registers might need every access to be 16 bits wide. With this flag, you can declare all the peripheral bit-fields as unsigned short (assuming short is 16 bits on those targets) to force GCC to use 16-bit accesses instead of a potentially more efficient 32-bit access ๐Ÿ’–.

If this option is disabled, the compiler goes for whatever is most efficient ๐ŸŽ๏ธ. In the example above, that could be a 32-bit load, even though it touches bytes that don't contain any part of the bit-field, or memory-mapped registers unrelated to the one being updated ๐Ÿ˜ฌ.

In some cases, like when the packed attribute is applied to a structure field, it might not be possible to access the field with one correctly aligned read or write for the target machine ๐Ÿฅบ. When that happens, GCC falls back to generating multiple accesses, instead of code that would fault or truncate the result at run time. Honestly, that's the responsible move ๐Ÿซถ.

Note: Due to restrictions of the C/C++11 memory model, write accesses are not allowed to touch non bit-field members. So it's recommended to define all bits of the field's type as bit-field members ๐Ÿ“.

The default value of this option is determined by the application binary interface for the target processor ๐Ÿงš.

โœจ -fsync-libcalls โœจ

  • -fsync-libcalls โ€” This controls whether any out-of-line instance of the __sync family of functions may be used to implement the C++11 __atomic family of functions ๐Ÿ”.

The default is enabled, so the only useful form of this option is -fno-sync-libcalls ๐Ÿ’…. This option is used in the implementation of the libatomic runtime library ๐Ÿ“š.

โœจ -fzero-init-padding-bits=value โœจ

  • -fzero-init-padding-bits=value โ€” This guarantees zero initialization of padding bits in automatic variable initializers ๐Ÿงธ. Some languages promise zero-initialized padding bits in certain cases. For example, C23 does this when you use empty initializers ({}), and C++ does it with zero-initialization. Also, C guarantees that fields not specified in an initializer have their padding bits zero initialized. This option lets you change when padding bits in initializers are guaranteed to be zero initialized ๐ŸŒˆ.

The default is -fzero-init-padding-bits=standard, which makes no further guarantees than the matching standard does. Here's an example, bestie ๐Ÿ‘‡:

struct A { char a; unsigned long long b; char c; };
union B { char a; unsigned long long b; };
struct A a = {}; // C23 guarantees padding bits are zero.
struct A b = { 1, 2, 3 }; // No guarantees.
union B c = {}; // C23 guarantees padding bits are zero.
union B d = { 1 }; // No guarantees.

-fzero-init-padding-bits=unions guarantees zero initialization of padding bits in unions, on top of what the standards already guarantee ๐Ÿ“. This happens if the initializer of a union is empty (then all bits of the union are zero initialized), or if the initialized member of the union is smaller than the size of the union (then padding bits outside the initialized member are guaranteed to be zero initialized). This was the GCC behavior before GCC 15 ๐Ÿ•ฐ๏ธ. In the example above, it guarantees zero initialization of the last sizeof (unsigned long long) - 1 bytes in the union ๐Ÿ’œ.

-fzero-init-padding-bits=all also guarantees zero initialization of padding bits in other aggregates ๐ŸŒธ. That covers the padding in between b.a and b.b (if there is any), and the tail padding in the structure (if there is any) โœจ.

NOTE: In Ubuntu 25.10 and later versions, -fzero-init-padding-bits=all is enabled by default for C, C++, ObjC, and ObjC++, unless -fzero-init-padding-bits=standard is found ๐Ÿšจ.

โœจ GCC Developer Options โœจ

This section covers command-line options that are mainly for GCC developers ๐Ÿง‘โ€๐Ÿ’ป. That includes options for compiler testing, digging into compiler bugs, and looking at compile-time performance problems ๐Ÿ”. It also covers options that produce debug dumps at various points in the compilation, print stats like memory use and execution time, and print info about GCC's configuration, like where it searches for libraries ๐Ÿ“Š. Honestly, you'll rarely need any of these for ordinary compiling and linking ๐Ÿ™ˆ.

A lot of developer options that make GCC dump output to a file take an optional =filename suffix. You can say stdout or - to dump to standard output, and stderr for standard error ๐Ÿ’ป.

If =filename is left out, GCC builds a default dump file name by concatenating the base dump file name, a pass number, a phase letter, and the pass name ๐Ÿงฉ. The base dump file name is the name of the output file the compiler produced, if you explicitly specified one and it's not an executable. Otherwise, it's the source file name. The pass number comes from the order passes are registered with the compiler's pass manager ๐Ÿ”ข. That's usually the same as the execution order, but passes registered by plugins, target-specific passes, or passes that get registered late are numbered higher than the pass named final, even if they run earlier ๐Ÿคฏ. The phase letter is one of i (inter-procedural analysis), l (language-specific), r (RTL), or t (tree) ๐Ÿ”ค. The files get created in the directory of the output file ๐Ÿ“.

  • -fcallgraph-info, -fcallgraph-info=MARKERS โ€” This makes the compiler output callgraph information for your program, one per object file ๐Ÿ•ธ๏ธ. The info is generated in the common VCG format. You can decorate it with extra per-node and/or per-edge information by passing a comma-separated list of markers. If the su marker is included, the callgraph gets stack usage info, which is the same as -fstack-usage. If the da marker is included, the callgraph gets info about dynamically allocated objects ๐Ÿง .

When you compile with -flto, no callgraph info gets output along with the object file ๐Ÿ™…. At LTO link time, -fcallgraph-info may generate multiple callgraph info files next to the intermediate LTO output files ๐Ÿ“‚.

  • -dletters, -fdump-rtl-pass, -fdump-rtl-pass=filename โ€” This tells GCC to make debugging dumps during compilation at the times specified by the letters ๐Ÿ”Ž. It's used for debugging the RTL-based passes of the compiler ๐Ÿ› ๏ธ.

Some -dletters switches mean something different when -E is used for preprocessing ๐Ÿง.

Debug dumps can be enabled with a -fdump-rtl switch or with some -d option letters ๐Ÿ’ก. Here are the possible letters you can use in pass and letters, and what they mean ๐Ÿ‘‡:

  • -fdump-rtl-alignments โ€” Dumps after branch alignments have been computed ๐Ÿ“.
  • -fdump-rtl-asmcons โ€” Dumps after fixing RTL statements that have unsatisfied in/out constraints ๐Ÿงท.
  • -fdump-rtl-auto_inc_dec โ€” Dumps after auto-inc-dec discovery. This pass only runs on architectures that have auto inc or auto dec instructions ๐Ÿค–.
  • -fdump-rtl-barriers โ€” Dumps after cleaning up the barrier instructions ๐Ÿงน.
  • -fdump-rtl-bbpart โ€” Dumps after partitioning hot and cold basic blocks ๐Ÿ”ฅ๐ŸงŠ.
  • -fdump-rtl-bbro โ€” Dumps after block reordering ๐Ÿ”€.
  • -fdump-rtl-btl1, -fdump-rtl-btl2 โ€” These enable dumping after the two branch target load optimization passes ๐ŸŽฏ.
  • -fdump-rtl-bypass โ€” Dumps after jump bypassing and control flow optimizations ๐Ÿ›ฃ๏ธ.
  • -fdump-rtl-combine โ€” Dumps after the RTL instruction combination pass ๐Ÿงฉ.
  • -fdump-rtl-compgotos โ€” Dumps after duplicating the computed gotos ๐Ÿฆ˜.
  • -fdump-rtl-ce1, -fdump-rtl-ce2, -fdump-rtl-ce3 โ€” These enable dumping after the three if conversion passes ๐Ÿ”€โœจ.
  • -fdump-rtl-cprop_hardreg โ€” Dumps after hard register copy propagation ๐Ÿ“‹.
  • -fdump-rtl-csa โ€” Dumps after combining stack adjustments ๐Ÿฅž.
  • -fdump-rtl-cse1, -fdump-rtl-cse2 โ€” These enable dumping after the two common subexpression elimination passes ๐Ÿชž.
  • -fdump-rtl-dce โ€” Dumps after the standalone dead code elimination passes ๐Ÿ’€.
  • -fdump-rtl-dbr โ€” Dumps after delayed branch scheduling โณ.

Okay so bestie, we're picking up right where the RTL dump flags leave off ๐Ÿ’…โœจ

  • -fdump-rtl-dce1, -fdump-rtl-dce2 โ€” ๐Ÿงน these two turn on dumping after the two dead store elimination passes. Dead stores get yeeted, and you get to see the receipts.
  • -fdump-rtl-eh โ€” ๐Ÿ›ก๏ธ dumps after the finalization of the EH handling code.
  • -fdump-rtl-eh_ranges โ€” ๐Ÿ—บ๏ธ dumps after the conversion of the EH handling range regions.
  • -fdump-rtl-expand โ€” ๐ŸŒฑ dumps after RTL generation.
  • -fdump-rtl-fwprop1, -fdump-rtl-fwprop2 โ€” โžก๏ธ turn on dumping after the two forward propagation passes.
  • -fdump-rtl-gcse1, -fdump-rtl-gcse2 โ€” ๐Ÿ” turn on dumping after global common subexpression elimination. No more doing the same math twice, periodt.
  • -fdump-rtl-init-regs โ€” ๐ŸŽฌ dumps after the registers get initialized.
  • -fdump-rtl-initvals โ€” ๐Ÿงฎ dumps after the initial value sets are computed.
  • -fdump-rtl-into_cfglayout โ€” ๐Ÿงฑ dumps right after converting to cfglayout mode.
  • -fdump-rtl-ira โ€” ๐ŸŽฏ dumps after iterated register allocation.
  • -fdump-rtl-jump โ€” ๐Ÿฆ˜ dumps after the second jump optimization.
  • -fdump-rtl-loop2 โ€” ๐Ÿ”„ turns on dumping after the RTL loop optimization passes.
  • -fdump-rtl-mach โ€” ๐Ÿ”ง dumps after the machine dependent reorganization pass, if that pass actually exists on your target.
  • -fdump-rtl-mode_sw โ€” ๐Ÿ”€ dumps after redundant mode switches are removed.
  • -fdump-rtl-rnreg โ€” ๐Ÿท๏ธ dumps after register renumbering.
  • -fdump-rtl-outof_cfglayout โ€” ๐Ÿ“ค dumps after converting from cfglayout mode.
  • -fdump-rtl-peephole2 โ€” ๐Ÿ” dumps after the peephole pass.
  • -fdump-rtl-postreload โ€” ๐Ÿ” dumps after the post-reload optimizations.
  • -fdump-rtl-pro_and_epilogue โ€” ๐ŸŽ dumps after the function prologues and epilogues get generated.
  • -fdump-rtl-sched1, -fdump-rtl-sched2 โ€” ๐Ÿ“… turn on dumping after the basic block scheduling passes.
  • -fdump-rtl-ree โ€” โœ‚๏ธ dumps after sign/zero extension elimination.
  • -fdump-rtl-seqabstr โ€” ๐Ÿงฉ dumps after common sequence discovery.
  • -fdump-rtl-shorten โ€” ๐Ÿ“ dumps after branches get shortened.
  • -fdump-rtl-split1, -fdump-rtl-split2, -fdump-rtl-split3, -fdump-rtl-split4, -fdump-rtl-split5 โ€” โœ‚๏ธ these enable dumping after five rounds of instruction splitting. Five whole rounds, bestie ๐Ÿ˜ญ
  • -fdump-rtl-sms โ€” ๐ŸŒ€ dumps after modulo scheduling. Heads up: this pass only runs on some architectures, so it might not show up for you.
  • -fdump-rtl-stack โ€” ๐Ÿฅž dumps after GCC's "flat register file" registers get converted into the x87's stack-like registers. This pass only runs on x86 variants, so it's x86-only energy.
  • -fdump-rtl-subreg1, -fdump-rtl-subreg2 โ€” ๐Ÿงฌ turn on dumping after the two subreg expansion passes.
  • -fdump-rtl-vartrack โ€” ๐Ÿ‘€ dumps after variable tracking.
  • -fdump-rtl-vregs โ€” ๐Ÿ” dumps after virtual registers get converted to hard registers.
  • -fdump-rtl-web โ€” ๐Ÿ•ธ๏ธ dumps after live range splitting.
  • -fdump-rtl-regclass, -fdump-rtl-subregs_of_mode_init, -fdump-rtl-subregs_of_mode_finish, -fdump-rtl-dfinit, -fdump-rtl-dfinish โ€” ๐Ÿซฅ these dumps are defined but they always produce empty files. Not a bug, just vibes with nothing in them.
  • -da, -fdump-rtl-all โ€” ๐ŸŒŸ produce all the dumps listed above. Everything, all at once, no cap.
  • -dA โ€” ๐Ÿท๏ธ annotates the assembler output with miscellaneous debugging information.
  • -dD โ€” ๐Ÿ“œ dumps all macro definitions at the end of preprocessing, on top of the normal output.
  • -dH โ€” ๐Ÿ’€ produces a core dump whenever an error occurs. Dramatic, but it works.
  • -dp โ€” ๐Ÿ’ฌ annotates the assembler output with a comment showing which pattern and alternative got used. The length and cost of each instruction get printed too.
  • -dP โ€” ๐ŸŽ€ dumps the RTL in the assembler output as a comment before each instruction. It also turns on -dp annotation, so it's a whole package deal.
  • -dx โ€” ๐Ÿงช just generates RTL for a function instead of compiling it. Usually paired with -fdump-rtl-expand.

  • -fdump-debug โ€” ๐Ÿž dumps the debugging information generated during the debug generation phase.
  • -fdump-earlydebug โ€” ๐ŸŒ… dumps the debugging information generated during the early debug generation phase. Early bird gets the debug info.
  • -fdump-noaddr โ€” ๐Ÿ™ˆ when doing debugging dumps, this suppresses address output. That makes it way easier to diff dumps from compiler invocations using different compiler binaries and/or different text / bss / data / heap / stack / dso start locations.
  • -freport-bug โ€” ๐Ÿšจ collects and dumps debug information into a temporary file if an internal compiler error (ICE) occurs. Oops moment, but make it useful.
  • -fdump-unnumbered โ€” ๐Ÿ”ข when doing debugging dumps, this suppresses instruction numbers and address output. Super handy for diff-ing dumps from invocations with different options, especially with and without -g.
  • -fdump-unnumbered-links โ€” ๐Ÿ”— when doing debugging dumps (see the -d option above), this suppresses instruction numbers for the links to the previous and next instructions in a sequence.
  • -fdump-ipa-switch, -fdump-ipa-switch-options โ€” ๐Ÿงญ control the dumping at various stages of inter-procedural analysis language tree to a file. The file name is generated by appending a switch specific suffix to the source file name, and the file gets created in the same directory as the output file. Here are the dumps you can ask for:
    • all โ€” ๐ŸŒˆ enables all inter-procedural analysis dumps.
    • cgraph โ€” ๐Ÿ•ธ๏ธ dumps info about call-graph optimization, unused function removal, and inlining decisions.
    • inline โ€” ๐Ÿ“ฆ dumps after function inlining.
    • strubm โ€” ๐ŸŽš๏ธ dumps after selecting "strub" modes, and records the selections as function attributes.
    • strub โ€” ๐Ÿงผ dumps "strub" transformations: interface changes, function wrapping, and insertion of builtin calls for stack scrubbing and watermarking.

Also, you can tack on -optimized, -missed, -note, and -all. They mean the same thing as they do for -fopt-info, and the default is -optimized.

For example, -fdump-ipa-inline-optimized-missed will emit info on callsites that were inlined, along with callsites that were not inlined. Main character energy ๐Ÿ‘‘

By default, the dump includes messages about successful optimizations (same as -optimized) together with low-level details about the analysis. ๐Ÿ”

  • -fdump-ipa-clones โ€” ๐Ÿ‘ creates a dump file with info about call graph node clones being created, and call graph nodes being removed, during inter-procedural optimizations and transformations. Its main purpose is so tools that make live-patches can figure out which functions need to be live-patched to fully replace a particular function (see -flive-patching). The file name is made by appending the suffix ipa-clones to the source file name, and it lives in the same directory as the output file. Each entry sits on its own line with semicolon separated fields.

For call graph clone creation, the fields are:

  1. String "Callgraph clone".
  2. Name of the function being cloned, as the assembler sees it.
  3. A number that uniquely identifies the function being cloned in the call graph. It's only unique within a compilation unit or within whole-program analysis, and it's likely to be different between the two phases.
  4. The file name of the source file where the function is defined.
  5. The line where the function definition is located.
  6. The column where the function definition is located.
  7. Name of the new function clone, as the assembler sees it.
  8. A number that uniquely identifies the new function clone in the call graph. Same deal: only unique within a compilation unit or whole-program analysis, and likely different in the two phases.
  9. The file name of the source file that the new clone's source code location points to.
  10. The line that the new clone's source code location points to.
  11. The column that the new clone's source code location points to.
  12. A string that says the reason for cloning.

For call graph clone removal, the fields are:

  1. String "Callgraph removal".
  2. Name of the function being removed, as it would be presented to the assembler.
  3. A number that uniquely identifies the function being cloned in the call graph. Again, unique only within a compilation unit or whole-program analysis, and likely different in the two phases.
  4. The file name of the source file where the function is defined.
  5. The line where the function definition is located.
  6. The column where the function definition is located.
  • -fdump-lang โ€” ๐Ÿ—ฃ๏ธ dumps language-specific info. The file name is made by appending .lang to the source file name.
  • -fdump-lang-all, -fdump-lang-switch, -fdump-lang-switch-options, -fdump-lang-switch-options=filename โ€” ๐Ÿ—‚๏ธ control the dumping of language-specific info. The options and filename parts work the same way they do for -fdump-tree. These switch values are accepted:
    • all โ€” ๐ŸŒธ enables all language-specific dumps.
    • class โ€” ๐Ÿ›๏ธ dumps class hierarchy info. Virtual table info gets emitted unless slim is specified. This one is C++ only.
    • module โ€” ๐Ÿ“ฆ dumps module info. The options lineno (locations), graph (reachability), blocks (clusters), uid (serialization), alias (mergeable), asmname (Elrond), eh (mapper) & vops (macros) may add extra info. This one is C++ only too.
    • raw โ€” ๐Ÿงพ dumps the raw internal tree data. C++ only, bestie.
  • -fdump-passes โ€” ๐Ÿ–จ๏ธ prints on stderr the list of optimization passes that are turned on and off by your current command-line options. Perfect for checking what's actually running โœจ

โœจ OPTIONS โœจ (continued) ๐ŸŽ€

-fdump-statistics-option ๐Ÿ“Š โ€” okay so this one turns on pass statistics dumping and lets you control it, and the stats land in their own file! ๐Ÿงš The file name is your source file name with a suffix ending in .statistics stuck on the end, and it gets created in the same directory as your output file ๐Ÿ“. If you use the -option form, -stats sums the counters over the whole compilation unit, while -details dumps every single event as the passes generate them ๐Ÿ‘€. With no option at all, it just sums the counters for each function compiled. Simple and iconic ๐Ÿ’…

-fdump-tree-all, -fdump-tree-switch, -fdump-tree-switch-options, -fdump-tree-switch-options=filename ๐ŸŒณ โ€” these are your tree dump switches! They control dumping at the various stages where GCC processes the intermediate language tree, into a file. If you use the -options form, options is a list of - separated options that tune the details of the dump. Not every option works with every dump, so the ones that don't make sense just get ignored, no drama ๐Ÿ™„ Here's the lineup:

  • address ๐Ÿ“ โ€” prints the address of each node. Usually this isn't very meaningful since it changes depending on the environment and source file, but its main use is tying a dump file to a debug environment.
  • asmname ๐Ÿท๏ธ โ€” if DECL_ASSEMBLER_NAME has been set for a given decl, this uses that in the dump instead of DECL_NAME. The main perk is working backward from mangled names in the assembly file, which is so helpful ๐Ÿซถ
  • slim ๐Ÿฅ— โ€” when dumping front-end intermediate representations, this stops members of a scope or a function body from being dumped just because that scope was reached. Only items directly reachable by some other path get dumped. When dumping pretty-printed trees, it skips the bodies of control structures. When dumping RTL, it prints the RTL in slim (condensed) form instead of the default LISP-like representation.
  • raw ๐Ÿงพ โ€” prints a raw representation of the tree. By default, trees get pretty-printed into a C-like representation, so this is the un-glammed version.
  • details ๐Ÿ” โ€” enables more detailed dumps (not honored by every dump option) and also includes info from the optimization passes.
  • stats ๐Ÿ“ˆ โ€” enables dumping various statistics about the pass (again, not honored by every dump option).
  • blocks ๐Ÿงฑ โ€” shows basic block boundaries. This one is disabled in raw dumps, so no sneaking it in there!
  • graph ๐Ÿ•ธ๏ธ โ€” for each of the other indicated dump files (-fdump-rtl-pass), this dumps a representation of the control flow graph to file.passid.pass.dot, suitable for viewing with GraphViz. Each function in the file is pretty-printed as a subgraph, so GraphViz can render them all in one single plot. This only works for RTL dumps, and the RTL is always dumped in slim form.
  • vops ๐ŸŽฏ โ€” shows virtual operands for every statement.
  • lineno ๐Ÿ”ข โ€” shows line numbers for statements.
  • uid ๐Ÿ†” โ€” shows the unique ID (DECL_UID) for each variable.
  • verbose ๐Ÿ—ฃ๏ธ โ€” shows the tree dump for each statement.
  • eh ๐Ÿ›Ÿ โ€” shows the EH region number holding each statement.
  • scev ๐Ÿงฎ โ€” shows scalar evolution analysis details.
  • optimized โšก โ€” shows optimization information (only available in certain passes).
  • missed ๐Ÿ™ƒ โ€” shows missed optimization information (only available in certain passes).
  • note ๐Ÿ“ โ€” shows other detailed optimization information (only available in certain passes).
  • all ๐ŸŒˆ โ€” turns on all options except raw, slim, verbose and lineno. Not everything, bestie, be picky!
  • optall ๐Ÿ‘‘ โ€” turns on all the optimization options, which means optimized, missed, and note.

Wondering what tree dumps are available or how to find the dump for a specific pass? Follow these steps, it's giving treasure hunt ๐Ÿ—บ๏ธโœจ

  1. Invoke GCC with -fdump-passes and look in the stderr output for a code that matches the pass you care about. For example, the codes tree-evrp, tree-vrp1, and tree-vrp2 correspond to the three Value Range Propagation passes. The number at the end tells apart different invocations of the same pass.
  2. To turn on the creation of the dump file, tack the pass code onto the -fdump- option prefix and invoke GCC with it. For example, to get the dump from the Early Value Range Propagation pass, invoke GCC with the -fdump-tree-evrp option. You can optionally name the dump file too. If you don't, GCC makes one as described below.
  3. Find the pass dump in a file whose name has three parts separated by periods: the name of the source file GCC was invoked to compile, a numeric suffix for the pass number followed by the letter t for tree passes (or the letter r for RTL passes), and finally the pass code. For example, the Early VRP pass dump might be in a file named myfile.c.038t.evrp in the current working directory. Heads up: the numeric codes are not stable and may change from one version of GCC to another ๐Ÿ™ˆ

-fopt-info, -fopt-info-options, -fopt-info-options=filename ๐Ÿ’ก โ€” these control optimization dumps from the various optimization passes. If you use the -options form, options is a list of - separated option keywords that pick the dump details and optimizations.

The options split into three groups: ๐Ÿงฉ

  1. options describing what kinds of messages should be emitted,
  2. options describing how verbose the dump is, and
  3. options describing which optimizations should be included.

You can freely mix options from each group since they don't overlap. If there's ever a conflict, the later option wins over the earlier one on the command line ๐Ÿ

These options control which kinds of messages get emitted:

  • optimized โœ… โ€” prints info when an optimization is successfully applied. It's up to each pass to decide what's relevant. For example, the vectorizer passes print the source location of loops that get successfully vectorized.
  • missed โŒ โ€” prints info about missed optimizations. Individual passes decide what goes in the output.
  • note ๐Ÿ—’๏ธ โ€” prints verbose info about optimizations, like certain transformations and more detailed messages about decisions.
  • all ๐ŸŽ‰ โ€” prints detailed optimization info. This includes optimized, missed, and note.

This option controls the dump verbosity:

  • internals ๐Ÿ”ฌ โ€” by default, only "high-level" messages are emitted. This turns on extra, more detailed messages, which are mostly only useful to GCC developers, so it's giving nerd ๐Ÿค“

These keywords let you pick a whole group of optimizations at once:

  • ipa ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ โ€” enables dumps from all interprocedural optimizations.
  • loop ๐Ÿ” โ€” enables dumps from all loop optimizations.
  • inline ๐Ÿ“ฆ โ€” enables dumps from all inlining optimizations.
  • omp ๐Ÿงต โ€” enables dumps from all OMP (Offloading and Multi Processing) optimizations.
  • vec ๐Ÿš€ โ€” enables dumps from all vectorization optimizations.
  • optall ๐ŸŒŸ โ€” enables dumps from all optimizations. This is a superset of the optimization groups listed above.

If options is left out, it defaults to optimized-optall, which means dumping messages about successful optimizations from all the passes, skipping anything treated as "internals" ๐Ÿ’–

If you give a filename, the dumps from all the applicable optimizations get concatenated into that file. Otherwise the dump goes to stderr. You can use multiple -fopt-info options, but only one of them can include a filename. If other filenames show up, all but the first such option get ignored, so choose wisely ๐Ÿซ 

Heads up: the output filename gets overwritten when there are multiple translation units. If you want a combined output from multiple translation units, use stderr instead! ๐Ÿšจ

Check out these examples. The first sends the optimization info to stderr:

gcc -O3 -fopt-info

This one outputs the missed optimization report from all the passes into missed.all:

gcc -O3 -fopt-info-missed=missed.all

And this one prints info about missed optimization opportunities from the vectorization passes on stderr. Bonus tea: -fopt-info-vec-missed is equivalent to -fopt-info-missed-vec, and the order of the optimization group names and message types after -fopt-info doesn't matter ๐Ÿ“

gcc -O2 -ftree-vectorize -fopt-info-vec-missed

Next up, this one outputs info about missed optimizations and optimized locations from all the inlining passes into inline.txt:

gcc -O3 -fopt-info-inline-optimized-missed=inline.txt

Finally, consider this one ๐Ÿ‘€:

gcc -fopt-info-vec-missed=vec.miss -fopt-info-loop-optimized=loop.opt

Here, the two output filenames vec.miss and loop.opt clash, since only one output file is allowed. In this case only the first option takes effect and the rest get ignored. So only vec.miss gets produced, and it contains the dumps from the vectorizer about missed opportunities ๐Ÿ˜ญ

-fsave-optimization-record ๐Ÿ’พ โ€” writes a SRCFILE.opt-record.json.gz file detailing which optimizations were performed, for the optimizations that support -fopt-info.

Warning: this option is experimental, and the format of the data inside the compressed JSON file is subject to change. Don't build your whole life on it yet! ๐Ÿงช

It's roughly equivalent to a machine-readable version of -fopt-info-all, as a collection of messages with source file, line number and column number. Each message also comes with the following extra data: โœจ

  • the execution count of the code being optimized, along with metadata about whether it came from actual profile data or just an estimate, so consumers can prioritize messages by code hotness ๐Ÿ”ฅ,
  • the function name of the code being optimized, where applicable ๐Ÿท๏ธ,
  • the "inlining chain" for the code being optimized, so that when a function is inlined into several different places (which might themselves be inlined), the reader can tell the copies apart ๐Ÿงฉ,

bestie, the message details are lowkey the best part ๐Ÿ’… each one hands you:

  • ๐Ÿ” the parts of the message that point at expressions, statements or symbol-table nodes, which kind of thing each one is, and (when GCC knows it โœจ) the source code location
  • ๐Ÿ› ๏ธ the GCC pass that spat out the message, and
  • ๐Ÿ“ the spot in GCC's own code where the message came from

Also, some messages are logically nested inside other messages, because they mirror the little implementation details of the optimization passes ๐Ÿงš

  • -fsched-verbose=n โ€” okay so, on targets that do instruction scheduling ๐ŸŽ€ this one controls how chatty the scheduler is when it writes debugging output to the dump files.

For n greater than zero, it gives you the same info as -fdump-rtl-sched1 and -fdump-rtl-sched2 ๐Ÿ’– For n greater than one, it also adds basic block probabilities, detailed ready list info and unit/insn info. For n greater than two, it throws in RTL at the abort point, control-flow and regions info. And for n over four, -fsched-verbose also brings the dependence info ๐Ÿ˜ญ๐Ÿ”ฅ

  • -fenable-kind-pass, -fdisable-kind-pass=range-list โ€” this is a whole family of options for explicitly turning optimization passes on and off ๐Ÿฆ‹ They're meant for debugging GCC itself. Regular compiler users should stick to the normal options for enabling and disabling passes instead ๐Ÿซถ
    • -fdisable-ipa-pass โ€” disables the IPA pass called pass, where pass is the pass name ๐Ÿ’… If the same pass gets invoked statically more than once in the compiler, add a sequential number to the name, starting from 1.
    • -fdisable-rtl-pass, -fdisable-rtl-pass=range-list โ€” disables the RTL pass called pass โœจ pass is the pass name, and the same numbering rule applies if it runs more than once. range-list is a comma-separated list of function ranges or assembler names. Each range is a number pair split by a colon, and it's inclusive on both ends ๐ŸŒธ If the range is trivial, you can shorten the number pair to a single number. If a function's call graph node uid falls inside one of the ranges, the pass gets disabled for that function. You can find the uid in the function header of a dump file, and you can dump the pass names with -fdump-passes ๐Ÿ‘‘
    • -fdisable-tree-pass, -fdisable-tree-pass=range-list โ€” disables the tree pass called pass ๐Ÿ’œ Check out -fdisable-rtl for how the option arguments work.
    • -fenable-ipa-pass โ€” the opposite energy: enables the IPA pass called pass ๐ŸŒˆ pass is the pass name, and if it's invoked more than once, the same sequential numbering from 1 applies.
    • -fenable-rtl-pass, -fenable-rtl-pass=range-list โ€” enables the RTL pass called pass ๐ŸŽ€ See -fdisable-rtl for the option argument description and examples.
    • -fenable-tree-pass, -fenable-tree-pass=range-list โ€” enables the tree pass called pass โญ Same deal, see -fdisable-rtl for the description of option arguments.

Here are some examples of these options in action ๐Ÿงš

        # disable ccp1 for all functions
           -fdisable-tree-ccp1
        # disable complete unroll for function whose cgraph node uid is 1
           -fenable-tree-cunroll=1
        # disable gcse2 for functions at the following ranges [1,1],
        # [300,400], and [400,1000]
        # disable gcse2 for functions foo and foo2
           -fdisable-rtl-gcse2=foo,foo2
        # disable early inlining
           -fdisable-tree-einline
        # disable ipa inlining
           -fdisable-ipa-inline
        # enable tree full unroll
           -fenable-tree-unroll
  • -fchecking, -fchecking=n โ€” turns on internal consistency checking ๐Ÿ™ˆ The default depends on how the compiler was configured. -fchecking=2 goes further and enables extra internal consistency checking that might affect code generation, so keep that in mind ๐Ÿ’€
  • -frandom-seed=string โ€” this gives GCC a seed to use instead of random numbers when it generates certain symbol names that have to be unique in every compiled file ๐Ÿฆ‹ It's also used to stamp coverage data files and the object files that produce them. Pair it with -frandom-seed to get reproducibly identical object files, and that's a whole mood ๐Ÿซถ

The string can be a number (decimal, octal or hex) or an arbitrary string. If it's an arbitrary string, GCC converts it to a number by computing CRC32 ๐Ÿ”ข

โš ๏ธ the string should be different for every file you compile, bestie!
  • -save-temps โ€” stores the usual "temporary" intermediate files permanently ๐Ÿ—‚๏ธ They get named as auxiliary output files, as specified under -dumpbase and -dumpdir.

When you use it together with the -x command-line option, -save-temps is smart enough to avoid overwriting an input source file that has the same extension as an intermediate file ๐Ÿง  If you want the intermediate file, rename the source file before using -save-temps.

  • -save-temps=cwd โ€” equivalent to -save-temps -dumpdir ./ ๐Ÿ“‚
  • -save-temps=obj โ€” equivalent to -save-temps -dumpdir outdir/, where outdir/ is the directory of the output file given after the -o option, including any directory separators ๐ŸŒท If you don't use -o, then -save-temps=obj behaves just like -save-temps=cwd โœจ
  • -time[=file] โ€” reports the CPU time each subprocess in the compilation sequence takes โฑ๏ธ For C source files, that's the compiler proper and the assembler (plus the linker if linking happens) ๐Ÿง

Without an output file specified, the output looks like this:

        # cc1 0.12 0.01
        # as 0.00 0.01

The first number on each line is the "user time", which is the time spent running the program itself ๐Ÿ’… The second number is the "system time", the time spent in operating system routines on the program's behalf. Both numbers are in seconds ๐Ÿ•ฐ๏ธ

With an output file specified, the output gets appended to that named file and looks like this:

        0.12 0.01 cc1 <options>
        0.00 0.01 as <options>

The "user time" and "system time" get moved in front of the program name, and the options passed to the program are shown too ๐Ÿช„ That way you can later tell which file was being compiled and with which options ๐Ÿ“

  • -fdump-final-insns[=file] โ€” dumps the final internal representation (RTL) to file ๐ŸŒŸ If the optional argument is left out (or if file is "."), the dump file's name comes from appending ".gkd" to the dump base name, see -dumpbase.
  • -fcompare-debug[=opts] โ€” if no error happens during compilation, this runs the compiler a second time, adding opts and -fcompare-debug-second to the arguments for that second run ๐Ÿชž It dumps the final internal representation in both compilations and prints an error if they differ. Iconic debugging energy ๐Ÿ˜ญ

If the equal sign is omitted, the default -gtoggle is used.

The environment variable GCC_COMPARE_DEBUG, if it's defined, non-empty and nonzero, implicitly enables -fcompare-debug ๐ŸŒˆ If GCC_COMPARE_DEBUG is defined to a string starting with a dash, that string is used for opts. Otherwise the default -gtoggle is used.

-fcompare-debug=, with the equal sign but without opts, is the same as -fno-compare-debug ๐Ÿ™… That turns off dumping the final representation and the second compilation, and it even stops GCC_COMPARE_DEBUG from taking effect.

To verify full coverage during -fcompare-debug testing, set GCC_COMPARE_DEBUG to say -fcompare-debug-not-overridden. GCC rejects that as an invalid option in any actual compilation (rather than preprocessing, assembly or linking) ๐Ÿ™ˆ If you only want a warning instead, setting GCC_COMPARE_DEBUG to -w%n-fcompare-debug not overridden will do the trick โœจ

  • -fcompare-debug-second โ€” this one gets passed to the compiler implicitly for the second compilation requested by -fcompare-debug ๐Ÿซง It comes along with options that silence warnings, and it leaves out any other options that would make the compiler write output to files or to standard output as a side effect. Dump files and preserved temporary files get renamed to include the ".gk" extra extension during that second compilation, so they don't overwrite the ones from the first run ๐Ÿ—ƒ๏ธ

When you pass this option to the compiler driver, the first compilation gets skipped. That makes it pretty useful for basically nothing except debugging the compiler proper ๐Ÿค“

  • -gtoggle โ€” turns off debug info generation if leaving out this option would generate it, or turns it on at level 2 otherwise ๐Ÿ” Its position on the command line doesn't matter. It takes effect after all other options are processed, and it only does so once, no matter how many times you give it. It's mainly meant to be used with -fcompare-debug ๐Ÿ’œ
  • -fvar-tracking-assignments-toggle โ€” toggles -fvar-tracking-assignments ๐ŸŽ€ It works the same way that -gtoggle toggles -g.
  • -Q โ€” when you use it on the command line before --help=, -Q acts as a modifier for the help output ๐Ÿงพ

Otherwise, this option makes the compiler print out each function name as it gets compiled, and print some statistics about each pass when it finishes ๐Ÿ“Š

  • -ftime-report โ€” makes the compiler print some statistics to stderr about the time each pass consumed when it finishes โณ

If SARIF output of diagnostics was requested with -fdiagnostics-format=sarif-file or -fdiagnostics-format=sarif-stderr, then the -ftime-report information gets emitted as JSON inside the SARIF output instead ๐Ÿงฉ The precise format of that JSON data is subject to change, and the values may not exactly match what goes to stderr, since they're written out at a slightly different place within the compiler ๐Ÿ™ƒ

  • -ftime-report-details โ€” records the time consumed by infrastructure parts separately for each pass ๐Ÿ”ฌ
  • -fira-verbose=n โ€” controls how verbose the dump file for the integrated register allocator is ๐ŸŒธ The default value is 5. If n is greater than or equal to 10, the dump output gets sent to stderr using the same format as n minus 10 ๐Ÿ’–
  • -flto-report โ€” okay so this one's a total gossip sesh for the compiler ๐Ÿ•ต๏ธโ€โ™€๏ธ It prints a report with internal details on how the link-time optimizer is working. The contents change from version to version, so don't get too attached to the exact format. It's mainly for GCC developers poking around when processing object files in LTO mode (via -flto) ๐Ÿฆ‹

Disabled by default ๐Ÿ’…

  • -flto-report-wpa โ€” same tea as -flto-report, but it only spills during the WPA phase of link-time optimization ๐Ÿซ–
  • -fmem-report โ€” the compiler gives you a little stats recap about permanent memory allocation once it's totally finished ๐Ÿ“Šโœจ
  • -fmem-report-wpa โ€” same memory stats moment, but only for the WPA phase ๐Ÿง 
  • -fpre-ipa-mem-report, -fpost-ipa-mem-report โ€” the compiler spills permanent memory allocation stats either before or after interprocedural optimization. Pick your timeline bestie ๐Ÿ•ฐ๏ธ๐Ÿ’œ
  • -fmultiflags โ€” this one is lowkey iconic ๐Ÿ‘‘ It lets multilib-aware TFLAGS build target libraries with options that are different from the ones the compiler is configured to use by default. It works through specs set up by compiler internals, by the target, or by builders at configure time.

Like TFLAGS, it means the target libraries can be built for portable baseline environments, while the compiler's defaults stay more demanding. That's so useful, because users can easily override the compiler's defaults for their own programs if those aren't ideal for their target environment. Rebuilding the runtime libraries is usually not as easy or desirable, so this is a whole vibe ๐ŸŒธ

Unlike TFLAGS, specs let different flags get picked for different multilibs. The way to make that happen is to build with make TFLAGS=-fmultiflags, after configuring with --with-specs=%{fmultiflags:...}.

โš ๏ธ This option gets discarded by the driver once it's done processing driver self spec. So it's gone after that, periodt.

It's also handy for checking that TFLAGS are being used to build all the target libraries. You do that by configuring a non-bootstrap compiler with --with-specs='%{!fmultiflags:%emissing TFLAGS}' and then building the compiler and target libraries ๐Ÿ”

  • -fprofile-report โ€” the compiler prints stats about how consistent the (estimated) profile is and what effect each individual pass had ๐Ÿ“ˆ
  • -fstack-usage โ€” the compiler outputs stack usage info for your program, per function ๐Ÿฅž The dump file's name is made by appending .su to the auxname. The auxname comes from the name of the output file if you explicitly specified one and it isn't an executable. Otherwise, it's the basename of the source file.

Each entry has three fields:

  • The name of the function.
  • A number of bytes.
  • One or more qualifiers: "static", "dynamic", "bounded".

The "static" qualifier means the function handles the stack statically. A fixed number of bytes gets allocated for the frame on function entry and released on function exit, and no other stack adjustments happen in the function. The second field is that fixed number of bytes ๐Ÿงฑ

The "dynamic" qualifier means the function handles the stack dynamically. On top of the static allocation, stack adjustments are made in the function body, like pushing and popping arguments around function calls ๐ŸŽข If the "bounded" qualifier is also there, the amount of those adjustments is bounded at compile time, and the second field is an upper bound on the total stack used by the function. If "bounded" isn't present, the amount of adjustments isn't bounded at compile time, and the second field only represents the bounded part.

  • -fstats โ€” emits stats about front-end processing at the end of compilation. Only the C++ front end supports this, and the info is mostly only useful to the G++ dev team ๐Ÿค“
  • -fdbg-cnt-list โ€” prints the name and the counter upper bound for every debug counter ๐Ÿ”ข
  • -fdbg-cnt=counter-value-list โ€” sets the internal debug counter lower and upper bounds. counter-value-list is a comma-separated list of name:lower_bound1-upper_bound1 [:lower_bound2-upper_bound2...] tuples. Each one sets the counter's name and a list of closed intervals. The lower bound is optional and gets zero initialized if it isn't set.

For example, with -fdbg-cnt=dce:2-4:10-11,tail_call:10, dbg_cnt(dce) only returns true for the second, third, fourth, tenth and eleventh invocation. For dbg_cnt(tail_call), true is returned for the first 10 invocations ๐ŸŽ€

  • -print-file-name=library โ€” prints the full absolute name of the library file library that would be used when linking, and then stops right there. GCC doesn't compile or link anything with this one, it just spits out the file name ๐Ÿ–จ๏ธ
  • -print-multi-directory โ€” prints the directory name for the multilib selected by any other switches on the command line. That directory is supposed to exist in GCC_EXEC_PREFIX ๐Ÿ“‚
  • -print-multi-lib โ€” prints the mapping from multilib directory names to the compiler switches that enable them. The directory name is separated from the switches by ;. Each switch starts with @ instead of -, and there are no spaces between multiple switches. This is meant to make shell processing easier ๐Ÿš
  • -print-multi-os-directory โ€” prints the path to OS libraries for the selected multilib, relative to some lib subdirectory. If OS libraries are in the lib subdirectory and no multilibs are used, this is usually just .. If OS libraries are in libsuffix sibling directories, it prints something like ../lib64, ../lib or ../lib32. If they're in lib/subdir subdirectories, it prints something like amd64, sparcv9 or ev6 ๐ŸŒˆ
  • -print-multiarch โ€” prints the path to OS libraries for the selected multiarch, relative to some lib subdirectory ๐Ÿ›๏ธ
  • -print-prog-name=program โ€” same vibe as -print-file-name, but it searches for a program like cpp instead ๐Ÿ”Ž
  • -print-libgcc-file-name โ€” same as -print-file-name=libgcc.a โœจ

This is useful when you use -nostdlib or -nodefaultlibs but you do want to link with libgcc.a. You can do:

gcc -nostdlib <files>... `gcc -print-libgcc-file-name`
  • -print-search-dirs โ€” prints the name of the configured installation directory and a list of the program and library directories gcc searches. Then it stops, no other action ๐Ÿ“œ

This is a lifesaver when gcc prints the error message installation problem, cannot exec cpp0: No such file or directory. To fix it, you either put cpp0 and the other compiler components where gcc expects to find them, or you set the environment variable GCC_EXEC_PREFIX to the directory where you installed them. And don't forget the trailing /! ๐Ÿ™ˆ

  • -print-sysroot โ€” prints the target sysroot directory that's used during compilation. This is the target sysroot specified either at configure time or with the --sysroot option, possibly with an extra suffix that depends on compilation options. If no target sysroot is specified, it prints nothing ๐ŸŒท
  • -print-sysroot-headers-suffix โ€” prints the suffix added to the target sysroot when searching for headers. If the compiler isn't configured with such a suffix, it gives an error instead. Then it stops ๐Ÿšซ
  • -dumpmachine โ€” prints the compiler's target machine (for example, i686-pc-linux-gnu) and that's it ๐Ÿ–ฅ๏ธ
  • -dumpversion โ€” prints the compiler version (for example, 3.0, 6.3.0 or 7) and then stops ๐Ÿซง This is the compiler version used in filesystem paths and specs. Depending on how the compiler was configured, it can be a single number (major version), two numbers separated by a dot (major and minor version), or three numbers separated by dots (major, minor and patchlevel version).
  • -dumpfullversion โ€” prints the full compiler version and nothing else. The output is always three numbers separated by dots: major, minor and patchlevel version ๐Ÿ’ฏ
  • -dumpspecs โ€” prints the compiler's built-in specs, then stops. (This one gets used when GCC itself is being built ๐Ÿ› ๏ธ)

๐Ÿ’– Machine-Dependent Options ๐Ÿ’–

Each target machine GCC supports can have its very own options. For example, you can compile for a particular processor variant or ABI, or control optimizations that are specific to that machine. By convention, the names of machine-specific options start with -m ๐ŸŽ€

Some configurations of the compiler also support extra target-specific options. These are usually there for compatibility with other compilers on the same platform ๐Ÿซถ

๐Ÿฆ‹ AArch64 Options ๐Ÿฆ‹

These options are defined for AArch64 implementations:

  • -mabi=name โ€” generates code for the specified data model. The valid values are ilp32, which is a SysV-like data model where int, long int and pointers are 32 bits, and lp64, which is a SysV-like data model where int is 32 bits but long int and pointers are 64 bits ๐Ÿงš

The default depends on the specific target configuration. The LP64 and ILP32 ABIs are not link-compatible, so you have to compile your entire program with the same ABI and link it with a compatible set of libraries.

โš ๏ธ The ilp32 model is deprecated. Not the vibe anymore, bestie ๐Ÿ’€
  • -mbig-endian โ€” generates big-endian code. This is the default when GCC is configured for an aarch64_be-*-* target ๐ŸŒŠ
  • -mgeneral-regs-only โ€” generates code that only uses the general-purpose registers. This stops the compiler from using floating-point and Advanced SIMD registers, but it doesn't put any restrictions on the assembler ๐Ÿง˜โ€โ™€๏ธ
  • -mlittle-endian โ€” generates little-endian code. This is the default when GCC is configured for an aarch64-*-* target but not an aarch64_be-*-* target ๐Ÿ“
  • -mcmodel=tiny โ€” generates code for the tiny code model. The program and its statically defined symbols must be within 1MB of each other. Programs can be statically or dynamically linked ๐Ÿฃ
  • -mcmodel=small โ€” generates code for the small code model. The program and its statically defined symbols must be within 4GB of each other. Programs can be statically or dynamically linked. This is the default code model ๐Ÿ’…
  • -mcmodel=large โ€” generates code for the large code model. It makes no assumptions about addresses and sizes of sections. Programs can only be statically linked with this one. Also, -mcmodel=large is incompatible with -mabi=ilp32, -fpic and -fPIC, so don't mix them up ๐Ÿ™ˆ

๐ŸŽ€ OPTIONS (AArch64 cont.) ๐ŸŽ€

-mtp=name lets you pick which system register acts as the thread pointer ๐Ÿงต. The valid values are tpidr_el0, tpidrro_el0, tpidr_el1, tpidr_el2, and tpidr_el3. For backwards compatibility, the aliases el0, el1, el2, and el3 also work ๐Ÿ’…. The default is tpidr_el0. Bestie, please compile everything that needs to interoperate with the same value of this option, or you'll end up grabbing a different thread pointer from the wrong exception level ๐Ÿ˜ญ.

  • -mstrict-align, -mno-strict-align โ€” โœจ These let you avoid, or allow, memory accesses that might not be aligned on a natural object boundary, as described in the architecture specification ๐Ÿ“.
  • -momit-leaf-frame-pointer, -mno-omit-leaf-frame-pointer โ€” ๐Ÿƒ Omit or keep the frame pointer in leaf functions. Omitting it is the default behaviour, so that's the one you get unless you ask otherwise ๐Ÿ™ˆ.
  • -mstack-protector-guard=guard, -mstack-protector-guard-reg=reg, -mstack-protector-guard-offset=offset โ€” ๐Ÿ›ก๏ธ These generate stack protection code using a canary at guard. The supported locations are global for a global canary, or sysreg for a canary in an appropriate system register.

With the sysreg choice, -mstack-protector-guard-reg=reg and -mstack-protector-guard-offset=offset decide which system register is the base register for reading the canary, and what offset from that base to read from. There is no default register or offset here, because this is entirely for use within the Linux kernel ๐Ÿง. Hands off unless you know what you're doing, periodt ๐Ÿซถ.

  • -mtls-dialect=desc โ€” ๐Ÿงš Uses TLS descriptors as the thread-local storage mechanism for dynamic accesses of TLS variables. This is the default, so you're already good โœจ.
  • -mtls-dialect=traditional โ€” ๐Ÿ•ฐ๏ธ Uses traditional TLS as the thread-local storage mechanism for dynamic accesses of TLS variables, instead of descriptors.
  • -mtls-size=size โ€” ๐Ÿ“ Sets the bit size of immediate TLS offsets. The valid values are 12, 24, 32, and 48. This option needs binutils 2.26 or newer, so check your toolchain first ๐Ÿ”ง.
  • -mfix-cortex-a53-835769, -mno-fix-cortex-a53-835769 โ€” ๐Ÿฉน Enables or disables the workaround for the ARM Cortex-A53 erratum number 835769. It works by inserting a NOP instruction between memory instructions and 64-bit integer multiply-accumulate instructions. This flag gets ignored if an architecture or cpu is specified on the command line that doesn't need the workaround ๐Ÿ’œ.
  • -mfix-cortex-a53-843419, -mno-fix-cortex-a53-843419 โ€” ๐Ÿฉน Enables or disables the workaround for the ARM Cortex-A53 erratum number 843419. This one is applied at link time, so this flag only passes the corresponding flag to the linker ๐Ÿ”—. It gets ignored too if an architecture or cpu is specified on the command line that doesn't need it.
  • -mlow-precision-recip-sqrt, -mno-low-precision-recip-sqrt โ€” โšก Enables or disables the reciprocal square root approximation. This only does anything if -ffast-math or -funsafe-math-optimizations is used as well. When enabled, reciprocal square root results drop to about 16 bits of precision for single precision and 32 bits for double precision ๐Ÿ“‰.
  • -mlow-precision-sqrt, -mno-low-precision-sqrt โ€” โšก Enables or disables the square root approximation. Again, this only has an effect if -ffast-math or -funsafe-math-optimizations is used as well. Enabling it reduces square root results to about 16 bits for single precision and 32 bits for double precision. If you enable it, it also implies -mlow-precision-recip-sqrt ๐Ÿ”ฅ.
  • -mlow-precision-div, -mno-low-precision-div โ€” โšก Enables or disables the division approximation. Same deal: it only has an effect with -ffast-math or -funsafe-math-optimizations. Division results get reduced to about 16 bits for single precision and 32 bits for double precision ๐Ÿ™ƒ.
  • -mtrack-speculation, -mno-track-speculation โ€” ๐Ÿ•ต๏ธ Enables or disables generation of extra code to track speculative execution through conditional branches. That tracking state can then be used by the compiler when expanding calls to "__builtin_speculation_safe_copy", so it can produce a more efficient code sequence ๐Ÿ‘‘.
  • -moutline-atomics, -mno-outline-atomics โ€” ๐Ÿงฉ Enables or disables calls to out-of-line helpers that implement atomic operations. These helpers figure out at runtime whether the LSE instructions from ARMv8.1-A can be used. If they can't, they fall back to the load/store-exclusive instructions that are present in the base ARMv8.0 ISA ๐Ÿง .

This option only applies when you're compiling for the base ARMv8.0 instruction set. If you're using a later revision, like -march=armv8.1-a or -march=armv8-a+lse, the ARMv8.1-Atomics instructions get used directly. The same goes for -mcpu= when the selected cpu supports the lse feature. This option is on by default ๐ŸŒˆ.

  • -march=name โ€” ๐Ÿ›๏ธ Specifies the name of the target architecture and, optionally, one or more feature modifiers. The form is -march=arch{+[no]feature}*.

๐ŸŒธ The Architecture Lineup ๐ŸŒธ

Here's the table of permissible arch values and the features each one turns on by default ๐Ÿ’–. Architecture is in the middle, the defaults are on the end:

  • armv8-a (Armv8-A) โ€” includes by default: +fp, +simd
  • armv8.1-a (Armv8.1-A) โ€” includes by default: armv8-a, +crc, +lse, +rdma
  • armv8.2-a (Armv8.2-A) โ€” includes by default: armv8.1-a
  • armv8.3-a (Armv8.3-A) โ€” includes by default: armv8.2-a, +pauth, +fcma, +jscvt
  • armv8.4-a (Armv8.4-A) โ€” includes by default: armv8.3-a, +flagm, +fp16fml, +dotprod, +rcpc2
  • armv8.5-a (Armv8.5-A) โ€” includes by default: armv8.4-a, +sb, +ssbs, +predres, +frintts, +flagm2
  • armv8.6-a (Armv8.6-A) โ€” includes by default: armv8.5-a, +bf16, +i8mm
  • armv8.7-a (Armv8.7-A) โ€” includes by default: armv8.6-a, +wfxt, +xs
  • armv8.8-a (Armv8.8-a) โ€” includes by default: armv8.7-a, +mops
  • armv8.9-a (Armv8.9-a) โ€” includes by default: armv8.8-a
  • armv9-a (Armv9-A) โ€” includes by default: armv8.5-a, +sve, +sve2
  • armv9.1-a (Armv9.1-A) โ€” includes by default: armv9-a, +bf16, +i8mm
  • armv9.2-a (Armv9.2-A) โ€” includes by default: armv9.1-a, +wfxt, +xs
  • armv9.3-a (Armv9.3-A) โ€” includes by default: armv9.2-a, +mops
  • armv9.4-a (Armv9.4-A) โ€” includes by default: armv9.3-a, +sve2p1
  • armv9.5-a (Armv9.4-A) โ€” includes by default: armv9.4-a, cpa, +faminmax, +lut
  • armv8-r (Armv8-R) โ€” includes by default: armv8-r

The value native is available on native AArch64 GNU/Linux and tells the compiler to pick the architecture of the host system ๐Ÿ–ฅ๏ธ. If the compiler can't recognise the host's architecture, this option does nothing. When you give -march=native and no other -mcpu or -mtune, GCC will pick the host CPU as the CPU to tune for, and also select the architecture features from it. In other words, -march=native gets treated as -mcpu=native ๐Ÿซง.

The permissible values for feature are listed in the sub-section on aarch64-feature-modifiers,,-march and -mcpu Feature Modifiers. If you specify conflicting feature modifiers, the right-most feature is the one that gets used ๐Ÿ‘€.

GCC uses name to decide what kind of instructions it can emit when generating assembly code. If -march is specified without -mtune or -mcpu also being specified, the code is tuned to perform well across a range of target processors implementing the target architecture ๐ŸŒŸ.

  • -mtune=name โ€” ๐ŸŽฏ Specifies the name of the target processor that GCC should tune the performance of the code for. The permissible values are: generic, cortex-a35, cortex-a53, cortex-a55, cortex-a57, cortex-a72, cortex-a73, cortex-a75, cortex-a76, cortex-a76ae, cortex-a77, cortex-a65, cortex-a65ae, cortex-a34, cortex-a78, cortex-a78ae, cortex-a78c, ares, exynos-m1, emag, falkor, oryon-1, neoverse-512tvb, neoverse-e1, neoverse-n1, neoverse-n2, neoverse-v1, neoverse-v2, grace, neoverse-v3, neoverse-v3ae, neoverse-n3, olympus, cortex-a725, cortex-x925, qdf24xx, saphira, phecda, xgene1, vulcan, octeontx, octeontx81, octeontx83, octeontx2, octeontx2t98, octeontx2t96, octeontx2t93, octeontx2f95, octeontx2f95n, octeontx2f95mm, a64fx, fujitsu-monaka, thunderx, thunderxt88, thunderxt88p1, thunderxt81, tsv110, thunderxt83, thunderx2t99, thunderx3t110, zeus, cortex-a57.cortex-a53, cortex-a72.cortex-a53, cortex-a73.cortex-a35, cortex-a73.cortex-a53, cortex-a75.cortex-a55, cortex-a76.cortex-a55, cortex-r82, cortex-r82ae, cortex-x1, cortex-x1c, cortex-x2, cortex-x3, cortex-x4, cortex-a510, cortex-a520, cortex-a520ae, cortex-a710, cortex-a715, cortex-a720, cortex-a720ae, ampere1, ampere1a, ampere1b, cobalt-100, apple-m1, apple-m2, apple-m3, and native. ๐Ÿ’…

The values cortex-a57.cortex-a53, cortex-a72.cortex-a53, cortex-a73.cortex-a35, cortex-a73.cortex-a53, cortex-a75.cortex-a55, cortex-a76.cortex-a55, apple-m1, apple-m2, apple-m3, and gb10 tell GCC to tune for a big.LITTLE system ๐Ÿฃ๐Ÿ˜.

The value neoverse-512tvb tells GCC to tune for Neoverse cores that (a) implement SVE and (b) have a total vector bandwidth of 512 bits per cycle. In other words, it tells GCC to tune for Neoverse cores that can execute 4 128-bit Advanced SIMD arithmetic instructions a cycle, and that can execute an equivalent number of SVE arithmetic instructions per cycle (2 for 256-bit SVE, 4 for 128-bit SVE). This is more general than tuning for a specific core like Neoverse V1, but more specific than the default tuning described below ๐ŸŽ€.

Additionally, on native AArch64 GNU/Linux systems, the value native tunes performance to the host system. If the compiler can't recognise the processor of the host system, this option does nothing ๐Ÿ–ฅ๏ธ.

Where none of -mtune=, -mcpu=, or -march= are specified, the code is tuned to perform well across a range of target processors ๐ŸŒˆ.

This option cannot be suffixed by feature modifiers, so don't even try ๐Ÿ™…โ€โ™€๏ธ.
  • -mcpu=name โ€” ๐Ÿง  Specifies the name of the target processor, optionally suffixed by one or more feature modifiers. The form is -mcpu=cpu{+[no]feature}*, where the permissible values for cpu are the same as those available for -mtune. The permissible values for feature are documented in the sub-section on aarch64-feature-modifiers,,-march and -mcpu Feature Modifiers. Conflicting feature modifiers? The right-most one wins ๐Ÿ‘‘.

GCC uses name to determine what kind of instructions it can emit when generating assembly code (as if by -march), and to determine the target processor it should tune for performance (as if by -mtune). If this option is used alongside -march or -mtune, those options take precedence over the matching part of this option ๐Ÿ’–.

Okay so the Neoverse thing is lowkey the most iconic flag ever ๐Ÿ’… -mcpu=neoverse-512tvb isn't one specific core, it's a whole group of Neoverse cores that (a) implement SVE and (b) have a total vector bandwidth of 512 bits a cycle ๐Ÿฆ‹ Unless you override it with -march, it makes code that can run on a Neoverse V1 core, since V1 is the first Neoverse core with both of those properties ๐Ÿ”ฅ And unless you override it with -mtune, it tunes your code the same way -mtune=neoverse-512tvb would โœจ

  • -moverride=string โ€” Okay this one is for the tuning nerds ๐Ÿค“ It overrides the tuning decisions the back-end makes in response to a -mtune= switch. The syntax, semantics, and accepted values for string are not guaranteed to stay the same across releases, so don't get too attached ๐Ÿ’€
This option is only intended to be useful when developing GCC. Like, it's for the people building the compiler, bestie ๐Ÿ™ˆ
  • -mverbose-cost-dump โ€” Turns on verbose cost model dumping in the debug dump files ๐Ÿงš It's here so you can debug the compiler itself, so it's giving compiler-dev energy ๐Ÿ’œ
  • -mpc-relative-literal-loads, -mno-pc-relative-literal-loads โ€” Turn PC-relative literal loads on or off ๐ŸŒธ With this on, literal pools get accessed with a single instruction and get emitted after each function. The catch is that it caps the maximum size of functions at 1MB ๐Ÿ˜ญ It's on by default for -mcmodel=tiny.
  • -msign-return-address=scope โ€” Picks which functions get return address signing ๐Ÿ” The valid values are none (turns signing off), non-leaf (signs functions that aren't leaf functions), and all (signs every single function). The default is none.
This option has been deprecated by -mbranch-protection, so maybe use that one instead ๐Ÿ’…
  • -mbranch-protection=none|standard|pac-ret[+leaf+b-key]|bti|gcs โ€” Picks which branch protection features you want ๐Ÿ‘‘
    • none is the default and turns off all branch protection types ๐Ÿซถ
    • standard turns on all the branch protection types. If a feature has extra tuning options, standard sets it to its standard level โœจ
    • pac-ret[+leaf] turns on return address signing to its standard level, meaning it signs functions that save the return address to memory (non-leaf functions basically always do this) using the A-key. The optional leaf argument extends the signing to include leaf functions ๐ŸŒˆ
    • The optional b-key argument signs the functions with the B-key instead of the A-key ๐Ÿ”‘
    • bti turns on the branch target identification mechanism ๐ŸŽฏ
    • gcs turns on guarded control stack compatible code generation ๐Ÿ›ก๏ธ
  • -mharden-sls=opts โ€” Enables compiler hardening against straight line speculation (SLS) ๐Ÿ’€ opts is a comma-separated list of the following options:
    • retbr
    • blr

-mharden-sls=all enables all SLS hardening, while -mharden-sls=none disables all of it ๐Ÿ™Œ

  • -mearly-ra=scope โ€” Decides when to turn on an early register allocation pass ๐Ÿง  This pass runs before instruction scheduling and tries to find a spill-free allocation of floating-point and vector code. It also tries to make use of strided multi-register instructions, like SME2's strided LD1 and ST1 โœจ
    • all runs the pass on all functions ๐Ÿซง
    • strided runs the pass on functions that have access to strided multi-register instructions ๐Ÿฆ‹
    • none disables the pass ๐Ÿ™ƒ

-mearly-ra=all is the default for -O2 and above, and for -Os. -mearly-ra=none is the default otherwise.

  • -mearly-ldp-fusion โ€” Enables the copy of the AArch64 load/store pair fusion pass that runs before register allocation ๐Ÿ“ It's on by default at -O and above.
  • -mlate-ldp-fusion โ€” Enables the copy of the AArch64 load/store pair fusion pass that runs after register allocation ๐Ÿ“ It's on by default at -O and above too.
  • -msve-vector-bits=bits โ€” Sets how many bits are in an SVE vector register ๐Ÿ’– It only does anything when SVE is enabled.

GCC has two flavours of SVE code generation. There's "vector-length agnostic" output that works with any size of vector register, and "vector-length specific" output that lets GCC make assumptions about the vector length when that helps optimisation ๐Ÿงš The valid values for bits are scalable, 128, 256, 512, 1024, and 2048. scalable gives you vector-length agnostic output. Right now -msve-vector-bits=128 also makes vector-length agnostic output, but only for big-endian targets ๐Ÿ‘€ Every other value makes vector-length specific code. These values might change in future releases, so the only one you should count on for code that's portable across different hardware SVE vector lengths is scalable ๐Ÿ˜ญ

The default is -msve-vector-bits=scalable, which makes vector-length agnostic code ๐ŸŒธ

  • -Wexperimental-fmv-target โ€” Warns you about using experimental Function Multi Versioning ๐Ÿšจ The Arm C Language Extension spec for FMV is still beta and can change, so any FMV usage is caveated: future behaviour changes and incompatibility are likely ๐Ÿ’€

โœจ -march and -mcpu Feature Modifiers โœจ

Feature modifiers used with -march and -mcpu can be any of the following, and each one also has an inverse you can write as nofeature ๐Ÿ’…

  • crc โ€” Enables the CRC extension ๐Ÿ”ฅ It's on by default for -march=armv8.1-a.
  • crypto โ€” Enables the Crypto extension ๐Ÿ” It also turns on Advanced SIMD and floating-point instructions.
  • fp โ€” Enables floating-point instructions ๐Ÿ’– It's on by default for all possible values of -march and -mcpu.
  • simd โ€” Enables Advanced SIMD instructions ๐Ÿฆ‹ It also turns on floating-point instructions, and it's on by default for all possible values of -march and -mcpu.
  • sve โ€” Enables Scalable Vector Extension instructions ๐ŸŒŠ It also turns on Advanced SIMD and floating-point instructions.
  • lse โ€” Enables Large System Extension instructions ๐Ÿงš It's on by default for -march=armv8.1-a.
  • rdma โ€” Enables Round Double Multiply Accumulate instructions โœจ It's on by default for -march=armv8.1-a.
  • fp16 โ€” Enables the FP16 extension ๐Ÿ“ It also turns on floating-point instructions.
  • fp16fml โ€” Enables the FP16 fmla extension ๐Ÿ’œ It also enables the FP16 extensions and floating-point instructions. This one is enabled by default for -march=armv8.4-a. Using it with architectures before Armv8.2-A is not supported, so don't do it ๐Ÿ™…โ€โ™€๏ธ
  • rcpc โ€” Enables the RCpc extension ๐ŸŒˆ This turns on the use of the LDAPR instructions for load-acquire atomic semantics, and passes it along to the assembler so inline asm statements can use instructions from the RCpc extension ๐Ÿซถ
  • dotprod โ€” Enables the Dot Product extension ๐ŸŽ€ It also turns on Advanced SIMD instructions.
  • aes โ€” Enables the Armv8-a aes and pmull crypto extension ๐Ÿ” It also turns on Advanced SIMD instructions.
  • sha2 โ€” Enables the Armv8-a sha2 crypto extension ๐Ÿ” It also turns on Advanced SIMD instructions.
  • sha3 โ€” Enables the sha512 and sha3 crypto extension ๐Ÿ” It also turns on Advanced SIMD instructions. Using it with architectures before Armv8.2-A is not supported ๐Ÿšซ
  • sm4 โ€” Enables the sm3 and sm4 crypto extension ๐Ÿ” It also turns on Advanced SIMD instructions. Using it with architectures before Armv8.2-A is not supported ๐Ÿšซ
  • profile โ€” Enables the Statistical Profiling extension ๐Ÿ“Š This only turns the extension on at the assembler level, and it does not affect code generation.
  • rng โ€” Enables the Armv8.5-a Random Number instructions ๐ŸŽฒ This only turns the extension on at the assembler level, and it does not affect code generation.
  • memtag โ€” Enables the Armv8.5-a Memory Tagging Extensions ๐Ÿท๏ธ Using it with architectures before Armv8.5-A is not supported.
  • sb โ€” Enables the Armv8-a Speculation Barrier instruction ๐Ÿ›‘ This only turns the extension on at the assembler level and does not affect code generation. It's enabled by default for -march=armv8.5-a.
  • ssbs โ€” Enables the Armv8-a Speculative Store Bypass Safe instruction ๐Ÿ›ก๏ธ This only turns the extension on at the assembler level and does not affect code generation. It's enabled by default for -march=armv8.5-a.
  • predres โ€” Enables the Armv8-a Execution and Data Prediction Restriction instructions ๐Ÿ™ˆ This only turns the extension on at the assembler level and does not affect code generation. It's enabled by default for -march=armv8.5-a.
  • sve2 โ€” Enables the Armv8-a Scalable Vector Extension 2 ๐Ÿš€ It also turns on SVE instructions.
  • sve2-bitperm โ€” Enables SVE2 bitperm instructions ๐Ÿงฉ It also turns on SVE2 instructions.
  • sve2-sm4 โ€” Enables SVE2 sm4 instructions ๐Ÿงฉ It also turns on SVE2 instructions.
  • sve2-aes โ€” Enables SVE2 aes instructions ๐Ÿงฉ It also turns on SVE2 instructions.
  • sve2-sha3 โ€” Enables SVE2 sha3 instructions ๐Ÿงฉ It also turns on SVE2 instructions.
  • sve2p1 โ€” Enables SVE2.1 instructions ๐Ÿš€ It also turns on SVE2 instructions.
  • tme โ€” Enables the Transactional Memory Extension ๐Ÿงต
  • i8mm โ€” Enables 8-bit Integer Matrix Multiply instructions ๐Ÿ”ข It also turns on Advanced SIMD and floating-point instructions. This one is enabled by default for -march=armv8.6-a. Using it with architectures before Armv8.2-A is not supported.
  • f32mm โ€” Enables 32-bit Floating point Matrix Multiply instructions ๐Ÿงฎ It also turns on SVE instructions. Using it with architectures before Armv8.2-A is not supported.
  • f64mm โ€” Enables 64-bit Floating point Matrix Multiply instructions ๐Ÿงฎ It also turns on SVE instructions. Using it with architectures before Armv8.2-A is not supported.
  • bf16 โ€” Enables brain half-precision floating-point instructions ๐Ÿง  It also turns on Advanced SIMD and floating-point instructions. This one is enabled by default for -march=armv8.6-a. Using it with architectures before Armv8.2-A is not supported.
  • ls64 โ€” Enables the 64-byte atomic load and store instructions for accelerators ๐Ÿš€
  • mops โ€” Enables the instructions that speed up memory operations like "memcpy", "memmove", "memset" ๐Ÿ’จ This one is enabled by default for -march=armv8.8-a
  • flagm โ€” Enables the Flag Manipulation instructions Extension ๐Ÿณ๏ธ
  • flagm2 โ€” Enables the FlagM2 flag conversion instructions ๐Ÿ”
  • pauth โ€” Enables the Pointer Authentication Extension ๐Ÿ”
  • cssc โ€” turns on the Common Short Sequence Compression instructions ๐Ÿ—œ๏ธโœจ
  • sme โ€” flips on the Scalable Matrix Extension ๐Ÿงฎ๐Ÿ’–. Bestie, this one only works if sve2 is also enabled ๐Ÿ™ˆ
  • sme-i16i64 โ€” enables the FEAT_SME_I16I64 extension to SME ๐ŸŒธ. It also turns on SME instructions.
  • sme-f64f64 โ€” enables the FEAT_SME_F64F64 extension to SME ๐Ÿฆ‹. It also turns on SME instructions.
  • sme2 โ€” enables the Scalable Matrix Extension 2 ๐Ÿ‘‘. It also turns on SME instructions.
  • sme-b16b16 โ€” enables the FEAT_SME_B16B16 extension to SME ๐Ÿ’œ. It also turns on SME2 and SVE_B16B16 instructions.
  • sme-f16f16 โ€” enables the FEAT_SME_F16F16 extension to SME ๐Ÿงš. It also turns on SME2 instructions.
  • sme2p1 โ€” enables the Scalable Matrix Extension version 2.1 ๐Ÿ”ฅ. It also turns on SME2 instructions.
  • fcma โ€” enables the complex number SIMD extensions ๐ŸŒˆ
  • jscvt โ€” enables the fjcvtzs JavaScript conversion instruction ๐Ÿ“
  • frintts โ€” enables floating-point round to integral value instructions โญ
  • wfxt โ€” enables the wfet and wfit instructions ๐Ÿซถ
  • xs โ€” enables the XS memory attribute extension ๐ŸŽ€
  • lse128 โ€” enables the LSE128 128-bit atomic instructions extension ๐Ÿ’…. It also turns on LSE instructions.
  • d128 โ€” enables support for 128-bit system register read/write instructions โœจ. It also turns on the LSE128 extension.
  • gcs โ€” enables support for the Armv9.4-a Guarded Control Stack extension ๐Ÿ›ก๏ธ๐ŸŒธ
  • the โ€” enables support for the Armv8.9-a/9.4-a translation hardening extension ๐Ÿ”’
  • rcpc2 โ€” enables the RCpc2 extension ๐Ÿ’œ
  • rcpc3 โ€” enables the RCpc3 (Release Consistency) extension ๐Ÿฆ‹
  • fp8 โ€” enables the fp8 (8-bit floating point) extension ๐Ÿซง
  • fp8fma โ€” enables the fp8 (8-bit floating point) multiply accumulate extension ๐Ÿ’–
  • ssve-fp8fma โ€” enables the fp8 (8-bit floating point) multiply accumulate extension, but in streaming mode ๐ŸŒŠ
  • fp8dot4 โ€” enables the fp8 (8-bit floating point) to single-precision 4-way dot product extension ๐Ÿ”ฅ
  • ssve-fp8dot4 โ€” enables that same fp8 to single-precision 4-way dot product extension, but in streaming mode ๐ŸŒŸ
  • fp8dot2 โ€” enables the fp8 (8-bit floating point) to half-precision 2-way dot product extension ๐Ÿฌ
  • ssve-fp8dot2 โ€” enables that same fp8 to half-precision 2-way dot product extension, but in streaming mode ๐ŸŽ€
  • faminmax โ€” enables the Floating Point Absolute Maximum/Minimum extension ๐Ÿ“
  • lut โ€” enables the Lookup Table extension ๐Ÿงพ
  • cpa โ€” enables the Checked Pointer Arithmetic instructions ๐Ÿ› ๏ธ
  • sve-b16b16 โ€” enables the SVE non-widening brain floating-point ("bf16") extension ๐Ÿง . This only has an effect when sve2 or sme2 are also enabled.

Feature crypto implies aes, sha2, and simd, which implies fp. Conversely, nofp implies nosimd, which implies nocrypto, noaes and nosha2. Main character energy, but make it a chain ๐Ÿ”—๐Ÿ’…


๐Ÿ Adapteva Epiphany Options

These -m options are defined for Adapteva Epiphany ๐Ÿ๐ŸŒธ:

  • -mhalf-reg-file โ€” okay so this one stops the compiler from allocating any register in the range r32...r63 ๐Ÿ™ˆ. That lets your code run on hardware variants that don't have those registers.
  • -mprefer-short-insn-regs โ€” the compiler will prefer registers that allow short instruction generation โœจ. Heads up, this can bump the instruction count, so overall code size might go down or up ๐Ÿคทโ€โ™€๏ธ.
  • -mbranch-cost=num โ€” sets the cost of branches to roughly num "simple" instructions ๐Ÿ’…. This cost is only a heuristic, so it's not guaranteed to give consistent results across releases.
  • -mcmove โ€” enables the generation of conditional moves ๐Ÿฆ‹
  • -mnops=num โ€” emits num NOPs before every other generated instruction ๐Ÿ˜ญ
  • -mno-soft-cmpsf โ€” for single-precision floating-point comparisons, the compiler emits an fsub instruction and tests the flags ๐Ÿƒโ€โ™€๏ธ. This is faster than a software comparison, but it can get incorrect results with NaNs, or when two different small numbers are compared and their difference is calculated as zero ๐Ÿ’€. The default is -msoft-cmpsf, which uses slower but IEEE-compliant software comparisons ๐Ÿซถ.
  • -mstack-offset=num โ€” sets the offset between the top of the stack and the stack pointer ๐Ÿ“š. E.g., a value of 8 means the eight bytes in the range sp+0...sp+7 can be used by leaf functions without stack allocation. Values other than 8 or 16 are untested and unlikely to work ๐Ÿ™ƒ. Also, this option changes the ABI, so compiling a program with a different stack offset than the libraries were compiled with generally does not work.
Heads up, bestie: this option changes the ABI! If you just want to see whether a different stack offset gives better code, this is a fine place to experiment ๐Ÿงช. But to actually build working programs with a different stack offset, the recommended move is to configure the toolchain with the appropriate --with-stack-offset=num option ๐Ÿšจ.
  • -mno-round-nearest โ€” makes the scheduler assume the rounding mode has been set to truncating ๐ŸŒ€. The default is -mround-nearest.
  • -mlong-calls โ€” if an attribute doesn't say otherwise, this assumes all calls might be beyond the offset range of the b / bl instructions ๐Ÿ“ž. So it loads the function address into a register before performing a (otherwise direct) call. This is the default ๐Ÿ‘‘.
  • -mshort-calls โ€” if an attribute doesn't say otherwise, this assumes all direct calls are in range of the b / bl instructions, so those instructions get used for direct calls โšก. The default is -mlong-calls.
  • -msmall16 โ€” assumes addresses can be loaded as 16-bit unsigned values ๐Ÿ“. This does not apply to function addresses where -mlong-calls semantics are in effect.
  • -mfp-mode=mode โ€” sets the prevailing mode of the floating-point unit ๐ŸŽ›๏ธ. This decides the floating-point mode that is provided and expected at function call and return time. Matching this to the mode you mostly need at function start can make your programs smaller and faster by avoiding unnecessary mode switches ๐Ÿš€.

mode can be set to one of the following values:

  • caller โ€” any mode at function entry is valid, and it gets retained or restored when the function returns and when it calls other functions ๐Ÿซถ. This is handy for compiling libraries or other compilation units you want to drop into programs with different prevailing FPU modes. The convenience of a single object file can outweigh the extra mode-switching overhead compared with a more specific choice of prevailing FPU mode.
  • truncate โ€” the mode used for floating-point calculations with truncating (round towards zero) rounding mode ๐ŸงŠ. That includes conversion from floating point to integer.
  • round-nearest โ€” the mode used for floating-point calculations with round-to-nearest-or-even rounding mode ๐ŸŽฏ
  • int โ€” the mode used to perform integer calculations in the FPU, e.g. integer multiply, or integer multiply-and-accumulate ๐Ÿ”ข

The default is -mfp-mode=caller ๐Ÿ’…

  • -mno-split-lohi, -mno-postinc, -mno-postmodify โ€” code generation tweaks that disable, respectively, splitting of 32-bit loads, generation of post-increment addresses, and generation of post-modify addresses ๐Ÿงฉ. The defaults are msplit-lohi, -mpost-inc, and -mpost-modify.
  • -mnovect-double โ€” changes the preferred SIMD mode to SImode ๐ŸŽ€. The default is -mvect-double, which uses DImode as the preferred SIMD mode.
  • -max-vect-align=num โ€” the maximum alignment for SIMD vector mode types ๐Ÿ“. num may be 4 or 8, and the default is 8. Note that this is an ABI change, even though many library function interfaces are unaffected if they don't use SIMD vector modes in places that affect the size and/or alignment of relevant types ๐Ÿ™ˆ.
  • -msplit-vecmove-early โ€” splits vector moves into single word moves before reload ๐Ÿช„. In theory this can give better register allocation, but so far the reverse seems to be the general case ๐Ÿ˜ญ.
  • -m1reg-reg โ€” specifies a register to hold the constant -1, which makes loading small negative constants and certain bitmasks faster โšก. Allowable values for reg are r43 and r63, which specify use of that register as a fixed register, and none, which means no register is used for this purpose. The default is -m1reg-none ๐Ÿ’–.

๐ŸŽฎ AMD GCN Options

These options are defined specifically for the AMD GCN port ๐ŸŽฎ๐ŸŒˆ

  • -march=gpu, -mtune=gpu โ€” sets the architecture type or tuning for gpu ๐Ÿง . Supported values for gpu are:
    • gfx900 โ€” compiles for GCN5 Vega 10 devices (gfx900) ๐ŸŒŸ
    • gfx902 โ€” compiles for GCN5 Vega gfx902 devices. (Experimental) ๐Ÿงช
    • gfx904 โ€” compiles for GCN5 Vega gfx904 devices. (Experimental) ๐Ÿงช
    • gfx906 โ€” compiles for GCN5 Vega 20 devices (gfx906) ๐Ÿ’œ
    • gfx908 โ€” compiles for CDNA1 Instinct MI100 series devices (gfx908) ๐Ÿ”ฅ
    • gfx909 โ€” compiles for GCN5 Vega gfx909 devices. (Experimental) ๐Ÿงช
    • gfx90a โ€” compiles for CDNA2 Instinct MI200 series devices (gfx90a) ๐Ÿ‘‘
    • gfx90c โ€” compiles for GCN5 Vega 7 devices (gfx90c) ๐Ÿ“
    • gfx9-generic โ€” compiles generic code for Vega devices, executable on the following subset of GFX9 devices: gfx900, gfx902, gfx904, gfx906, gfx909 and gfx90c. (Experimental) ๐ŸŒธ
    • gfx1030 โ€” compiles for RDNA2 gfx1030 devices (GFX10 series) ๐Ÿฆ‹
    • gfx1031 โ€” compiles for RDNA2 gfx1031 devices (GFX10 series). (Experimental) ๐Ÿงช
    • gfx1032 โ€” compiles for RDNA2 gfx1032 devices (GFX10 series). (Experimental) ๐Ÿงช
    • gfx1033 โ€” compiles for RDNA2 gfx1033 devices (GFX10 series). (Experimental) ๐Ÿงช
    • gfx1034 โ€” compiles for RDNA2 gfx1034 devices (GFX10 series). (Experimental) ๐Ÿงช
    • gfx1035 โ€” compiles for RDNA2 gfx1035 devices (GFX10 series). (Experimental) ๐Ÿงช
    • gfx1036 โ€” compiles for RDNA2 gfx1036 devices (GFX10 series) ๐Ÿ’–
    • gfx10-3-generic โ€” compiles generic code for GFX10-3 devices, executable on gfx1030, gfx1031, gfx1032, gfx1033, gfx1034, gfx1035, and gfx1036. (Experimental) โœจ
    • gfx1100 โ€” compiles for RDNA3 gfx1100 devices (GFX11 series) ๐ŸŒŠ
    • gfx1101 โ€” compiles for RDNA3 gfx1101 devices (GFX11 series). (Experimental) ๐Ÿงช
    • gfx1102 โ€” compiles for RDNA3 gfx1102 devices (GFX11 series). (Experimental) ๐Ÿงช
    • gfx1103 โ€” compiles for RDNA3 gfx1103 devices (GFX11 series) ๐Ÿซง
  • gfx1150 โ€” okay so this one's for RDNA3 gfx1150 devices (GFX11 series) ๐Ÿ’œ (Experimental) ๐Ÿงช
  • gfx1151 โ€” same energy, but for RDNA3 gfx1151 devices (GFX11 series) โœจ (Experimental)
  • gfx1152 โ€” RDNA3 gfx1152 devices (GFX11 series) get the spotlight here ๐Ÿฆ‹ (Experimental)
  • gfx1153 โ€” and RDNA3 gfx1153 devices (GFX11 series) round out the squad ๐ŸŒธ (Experimental)
  • gfx11-generic โ€” this is the generic one, bestie ๐Ÿ‘‘ It compiles generic code for GFX11 devices, and that code can run on gfx1100, gfx1101, gfx1102, gfx1103, gfx1150, gfx1151, gfx1152, and gfx1153. Very inclusive ๐Ÿซถ (Experimental)
  • -msram-ecc=on, -msram-ecc=off, -msram-ecc=any โ€” these pick which SRAM-ECC mode your binaries are built for. on means the device has SRAM-ECC enabled, off means it's disabled, and any means either mode works ๐ŸŒˆ Some devices let you flip this per-process, but the compiled code has to match the device mode, no exceptions ๐Ÿ™ˆ The default is any, as long as the device actually supports it โœจ
  • -mstack-size=bytes โ€” this sets how many bytes of stack space each GPU thread (wave-front) gets ๐Ÿงš Be careful though, because there can be a lot of threads and not a ton of memory available ๐Ÿ˜ญ The stack size can also affect run-time performance. The default is 32KB when you're using OpenACC or OpenMP, and 1MB otherwise ๐Ÿ’…
  • -mxnack=on, -mxnack=off, -mxnack=any โ€” this one's about the XNACK feature ๐Ÿ“ Your binaries can be built for XNACK enabled, XNACK disabled, or either mode. Some devices always need XNACK, and some let the user decide. The compiled code has to match the device mode too. The default is -mxnack=any on devices that support Unified Shared Memory, and -mxnack=no everywhere else ๐Ÿ’–

โœจ ARC Options โœจ

Okay bestie, this next batch controls the architecture variant your code gets compiled for ๐ŸŽ€

  • -mbarrel-shifter โ€” generates instructions that the barrel shifter supports ๐Ÿ”ฅ This is the default unless -mcpu=ARC601 or -mcpu=ARCEM is in effect.
  • -mjli-always โ€” forces function calls to use the jli_s instruction ๐Ÿ’œ This option is only valid for the ARCv2 architecture.
  • -mcpu=cpu โ€” this is the big one ๐Ÿ‘‘ It sets the architecture type, register usage, and instruction scheduling parameters for cpu. There are also shortcut alias options around for backward compatibility and convenience ๐Ÿซถ These are the supported values for cpu:
    • arc600 โ€” compiles for ARC600. Aliases: -mA6, -mARC600 ๐ŸŒธ
    • arc601 โ€” compiles for ARC601. Alias: -mARC601 ๐Ÿฆ‹
    • arc700 โ€” compiles for ARC700. Aliases: -mA7, -mARC700. This is the default when you configure with --with-cpu=arc700 โœจ
    • arcem โ€” compiles for ARC EM ๐Ÿงš
    • archs โ€” compiles for ARC HS ๐Ÿ’…
    • em โ€” compiles for an ARC EM CPU with no hardware extensions ๐Ÿ“
    • em4 โ€” compiles for an ARC EM4 CPU ๐ŸŒˆ
    • em4_dmips โ€” compiles for an ARC EM4 DMIPS CPU ๐Ÿ’–
    • em4_fpus โ€” compiles for an ARC EM4 DMIPS CPU with the single-precision floating-point extension ๐Ÿ”ฅ
    • em4_fpuda โ€” compiles for an ARC EM4 DMIPS CPU with single-precision floating-point and double assist instructions ๐ŸŽ€
    • hs โ€” compiles for an ARC HS CPU with no hardware extensions except the atomic instructions ๐Ÿ™ˆ
    • hs34 โ€” compiles for an ARC HS34 CPU โญ
    • hs38 โ€” compiles for an ARC HS38 CPU ๐Ÿฆ‹
    • hs38_linux โ€” compiles for an ARC HS38 CPU with all hardware extensions turned on ๐Ÿ‘‘
    • hs4x โ€” compiles for an ARC HS4x CPU ๐ŸŒธ
    • hs4xd โ€” compiles for an ARC HS4xD CPU โœจ
    • hs4x_rel31 โ€” compiles for an ARC HS4x CPU release 3.10a ๐Ÿ’œ
    • arc600_norm โ€” compiles for an ARC 600 CPU with "norm" instructions enabled ๐Ÿซถ
    • arc600_mul32x16 โ€” compiles for an ARC 600 CPU with "norm" and 32x16-bit multiply instructions enabled ๐Ÿ”ฅ
    • arc600_mul64 โ€” compiles for an ARC 600 CPU with "norm" and "mul64"-family instructions enabled ๐ŸŒˆ
    • arc601_norm โ€” compiles for an ARC 601 CPU with "norm" instructions enabled ๐Ÿ“
    • arc601_mul32x16 โ€” compiles for an ARC 601 CPU with "norm" and 32x16-bit multiply instructions enabled ๐Ÿ’–
    • arc601_mul64 โ€” compiles for an ARC 601 CPU with "norm" and "mul64"-family instructions enabled ๐ŸŽ€
    • nps400 โ€” compiles for ARC 700 on the NPS400 chip ๐Ÿงš
    • em_mini โ€” compiles for the ARC EM minimalist configuration, which features a reduced register set ๐Ÿ™ˆ
  • -mdpfp, -mdpfp-compact โ€” generates double-precision FPX instructions, tuned for the compact implementation ๐Ÿ’…
  • -mdpfp-fast โ€” generates double-precision FPX instructions, tuned for the fast implementation โšก
  • -mno-dpfp-lrsr โ€” disables "lr" and "sr" instructions from using FPX extension aux registers ๐Ÿ™…โ€โ™€๏ธ
  • -mea โ€” generates extended arithmetic instructions ๐Ÿงฎ Right now only "divaw", "adds", "subs", and "sat16" are supported. This is only valid for -mcpu=ARC700 ๐Ÿ”ฅ
  • -mno-mpy โ€” doesn't generate "mpy"-family instructions for ARC700 ๐Ÿฅ€ This option is deprecated, so maybe don't build your whole vibe around it ๐Ÿ’€
  • -mmul32x16 โ€” generates 32x16-bit multiply and multiply-accumulate instructions โœจ
  • -mmul64 โ€” generates "mul64" and "mulu64" instructions ๐Ÿฆ‹ This is only valid for -mcpu=ARC600.
  • -mnorm โ€” generates "norm" instructions ๐ŸŒธ This is the default if -mcpu=ARC700 is in effect.
  • -mspfp, -mspfp-compact โ€” generates single-precision FPX instructions, tuned for the compact implementation ๐Ÿ’œ
  • -mspfp-fast โ€” generates single-precision FPX instructions, tuned for the fast implementation ๐Ÿ”ฅ
  • -msimd โ€” enables ARC SIMD instructions through target-specific builtins ๐Ÿงš This is only valid for -mcpu=ARC700.
  • -msoft-float โ€” this one is basically a no-op ๐Ÿ™ƒ It's only here for compatibility purposes. Software floating-point code is emitted by default, and that default can be overridden by the FPX options: -mspfp, -mspfp-compact, or -mspfp-fast for single precision, and -mdpfp, -mdpfp-compact, or -mdpfp-fast for double precision ๐Ÿซถ
  • -mswap โ€” generates "swap" instructions ๐ŸŒˆ
  • -matomic โ€” enables the locked load/store conditional extension so it can power the atomic memory built-in functions ๐Ÿ” This is not available for ARC 6xx or ARC EM cores ๐Ÿ™…โ€โ™€๏ธ
  • -mdiv-rem โ€” enables "div" and "rem" instructions for ARCv2 cores โž—
  • -mcode-density โ€” enables code density instructions for ARC EM ๐Ÿ“ This option is on by default for ARC HS.
  • -mll64 โ€” enables double load/store operations for ARC HS cores ๐Ÿ’–
  • -mtp-regno=regno โ€” specifies the thread pointer register number ๐Ÿงต
  • -mmpy-option=multo โ€” this picks a multiplier design option for ARCv2 code ๐Ÿ”ข You can specify it with either a string or a numeric value for multo. wlh1 is the default. These are the recognized values:
    • 0 or none โ€” no multiplier available ๐Ÿฅฒ
    • 1 or w โ€” a 16x16 multiplier, fully pipelined ๐ŸŒธ These instructions get enabled: "mpyw" and "mpyuw".
    • 2 or wlh1 โ€” a 32x32 multiplier, fully pipelined (1 stage) โœจ These instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 3 or wlh2 โ€” a 32x32 multiplier, fully pipelined (2 stages) ๐Ÿ’œ These instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 4 or wlh3 โ€” two 16x16 multipliers, blocking, sequential ๐Ÿฆ‹ These instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 5 or wlh4 โ€” one 16x16 multiplier, blocking, sequential ๐Ÿงš These instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 6 or wlh5 โ€” one 32x4 multiplier, blocking, sequential ๐ŸŒˆ These instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 7 or plus_dmpy โ€” ARC HS SIMD support ๐Ÿ”ฅ
    • 8 or plus_macd โ€” ARC HS SIMD support ๐Ÿ’…
    • 9 or plus_qmacw โ€” ARC HS SIMD support ๐Ÿ‘‘
Heads up: this option is only available for ARCv2 cores ๐Ÿ™ˆ
  • -mfpu=fpu โ€” turns on support for specific floating-point hardware extensions on ARCv2 cores ๐ŸŽ€ These are the supported values for fpu:
    • fpus โ€” enables single-precision floating-point hardware extensions ๐ŸŒธ
    • fpud โ€” enables double-precision floating-point hardware extensions ๐Ÿ’– The single-precision floating-point extension gets turned on too. Not available for ARC EM ๐Ÿ™…โ€โ™€๏ธ
    • fpuda โ€” enables double-precision floating-point hardware extensions using double-precision assist instructions โœจ The single-precision floating-point extension is also enabled. This is only available for ARC EM ๐Ÿงš
    • fpuda_div โ€” enables double-precision floating-point hardware extensions using double-precision assist instructions ๐Ÿงฎ The single-precision floating-point, square-root, and divide extensions are also enabled. This is only available for ARC EM ๐Ÿ”ฅ
    • fpuda_fma โ€” enables double-precision floating-point hardware extensions using double-precision assist instructions ๐Ÿ’œ The single-precision floating-point and fused multiply and add hardware extensions are also enabled. This is only available for ARC EM ๐Ÿฆ‹
    • fpuda_all โ€” enables double-precision floating-point hardware extensions using double-precision assist instructions ๐ŸŒˆ All single-precision floating-point hardware extensions are also enabled. This is only available for ARC EM ๐Ÿ‘‘
    • fpus_div โ€” enables single-precision floating-point, square-root, and divide hardware extensions โž—
    • fpud_div โ€” enables double-precision floating-point, square-root, and divide hardware extensions ๐Ÿ“ This option includes fpus_div. Not available for ARC EM ๐Ÿ™…โ€โ™€๏ธ
    • fpus_fma โ€” enables single-precision floating-point and fused multiply and add hardware extensions ๐ŸŒธ
    • fpud_fma โ€” enables double-precision floating-point and fused multiply and add hardware extensions ๐Ÿ’– This option includes fpus_fma. Not available for ARC EM ๐Ÿ™…โ€โ™€๏ธ
    • fpus_all โ€” enables all single-precision floating-point hardware extensions โœจ
    • fpud_all โ€” enables all single- and double-precision floating-point hardware extensions ๐ŸŽ€ Not available for ARC EM ๐Ÿ™…โ€โ™€๏ธ

Okay so this batch is all about interrupts and registers, and honestly the chip is doing a lot of babysitting here ๐Ÿซถโœจ

  • -mirq-ctrl-saved=register-range, blink, lp_count: this tells the processor which general-purpose registers to auto-save and restore on interrupt entry and exit ๐Ÿ’… The register-range is two registers joined by a dash. It always starts at r0, and the upper limit is the fp register. blink and lp_count are optional. Only valid for ARC EM and ARC HS cores ๐Ÿ”ฅ
  • -mrgf-banked-regs=number: sets how many registers get copied into the second register bank when a fast interrupt hits ๐Ÿฆ‹ Fast interrupts are the ones at the highest priority level, P0. They only save the PC and STATUS32 registers so they can skip memory transactions on entry and exit, which is so efficient ๐Ÿ˜ญ Use this when you're doing fast interrupts on an ARC V2 family processor. Permitted values are 4, 8, 16, and 32.
  • -mlpc-width=width: specifies the width of the "lp_count" register ๐Ÿงš Valid widths are 8, 16, 20, 24, 28, and 32 bits, and the default is fixed at 32 bits. If the width is less than 32, the compiler won't transform your loops to use the zero-delay loop mechanism unless it knows the "lp_count" register can hold the loop counter value. Depending on the width, the compiler and run-time library might still use the loop mechanism for various other needs ๐ŸŒธ This option also defines the macro "__ARC_LPC_WIDTH__" with the value of width.
  • -mrf16: tells the compiler to generate code for a 16-entry register file ๐Ÿ’œ It also defines the "__ARC_RF16__" preprocessor macro.
  • -mbranch-index: enables the use of bi or bih instructions to build jump tables ๐Ÿ“

๐Ÿงš Passed through to the assembler (and defining macros)

These options get handed down to the assembler, and they also define preprocessor macro symbols, so you can check for them in your code ๐Ÿ’–

  • -mdsp-packa: enables the DSP Pack A extensions in the assembler ๐ŸŽ€ It also sets the "__Xdsp_packa" preprocessor symbol. This option is deprecated ๐Ÿ’€
  • -mdvbf: enables the dual Viterbi butterfly extension in the assembler ๐Ÿฆ‹ It also sets "__Xdvbf". This option is deprecated.
  • -mlock: enables the locked load/store conditional extension in the assembler ๐Ÿ”’ It also sets "__Xlock".
  • -mmac-d16: passed down to the assembler, and sets "__Xxmac_d16". This option is deprecated.
  • -mmac-24: passed down to the assembler, and sets "__Xxmac_24". This option is deprecated.
  • -mrtsc: enables the 64-bit time-stamp counter extension instruction in the assembler โฑ๏ธ It also sets "__Xrtsc". This option is deprecated.
  • -mswape: enables the swap byte ordering extension instruction in the assembler ๐ŸŒˆ It also sets "__Xswape".
  • -mtelephony: enables dual- and single-operand instructions for telephony in the assembler ๐Ÿ“ž It also sets "__Xtelephony". This option is deprecated.
  • -mxy: enables the XY memory extension in the assembler โœจ It also sets "__Xxy".

๐ŸŽ€ Controlling how assembly gets annotated

The following options control how the assembly code is annotated ๐Ÿ“

  • -misize: annotates assembler instructions with estimated addresses ๐Ÿ™ˆ
  • -mannotate-align: does nothing, it's just kept around for backward compatibility ๐Ÿซ 

๐Ÿ”— Passed through to the linker

  • -marclinux: passed through to the linker to specify use of the "arclinux" emulation ๐ŸŒธ This one is turned on by default in toolchains built for the "arc-linux-uclibc" and "arceb-linux-uclibc" targets when profiling is not requested.
  • -marclinux_prof: passed through to the linker to specify use of the "arclinux_prof" emulation ๐Ÿ“ˆ This one is turned on by default in toolchains built for the "arc-linux-uclibc" and "arceb-linux-uclibc" targets when profiling is requested.

๐Ÿ‘‘ Controlling the semantics of generated code

  • -mlong-calls: generates calls as register indirect calls, which gives you access to the full 32-bit address range ๐Ÿš€
  • -mmedium-calls: doesn't use anything less than a 25-bit addressing range for calls ๐Ÿ’ซ That's the offset available for an unconditional branch-and-link instruction. Conditional execution of function calls gets suppressed so the 25-bit range can be used, instead of the 21-bit range you'd get with conditional branch-and-link. This is the default for toolchains built for the "arc-linux-uclibc" and "arceb-linux-uclibc" targets.
  • -G num: puts definitions of externally-visible data in a small data section, as long as that data is no bigger than num bytes ๐Ÿงธ The default value of num is 4 for any ARC configuration, or 8 when you have double load/store operations.
  • -mno-sdata: stops sdata references from being generated ๐Ÿ™…โ€โ™€๏ธ This is the default for toolchains built for the "arc-linux-uclibc" and "arceb-linux-uclibc" targets.
  • -mvolatile-cache: uses ordinarily cached memory accesses for volatile references ๐Ÿ’พ This is the default.
  • -mno-volatile-cache: enables cache bypass for volatile references ๐Ÿƒโ€โ™€๏ธ

๐Ÿ’… Fine-tuning code generation

The following options fine-tune code generation, so they're kind of the glam squad ๐Ÿ’–

  • -malign-call: does nothing, preserved for backward compatibility ๐Ÿซถ
  • -mauto-modify-reg: enables the use of pre/post modify with register displacement ๐Ÿช„
  • -mbbit-peephole: does nothing, preserved for backward compatibility ๐Ÿซ 
  • -mno-brcc: disables a target-specific pass in arc_reorg that generates compare-and-branch ("brcc") instructions ๐Ÿ™ƒ It has no effect on the generation of these instructions when they're driven by the combiner pass.
  • -mcase-vector-pcrel: uses PC-relative switch case tables to enable case table shortening โœ‚๏ธ This is the default for -Os.
  • -mcompact-casesi: enables the compact "casesi" pattern ๐Ÿ—‚๏ธ This is the default for -Os, and it's only available for ARCv1 cores. This option is deprecated ๐Ÿ’€
  • -mno-cond-exec: disables the ARCompact-specific pass that generates conditional execution instructions ๐Ÿ™…โ€โ™€๏ธ

Okay real talk about why this one exists ๐Ÿง  Delay slot scheduling and the interactions between operand numbers, literal sizes, instruction lengths, and conditional execution support mean the target-independent conditional execution pass is often lacking. So the ARC port keeps a special pass around that hunts for more conditional execution opportunities after register allocation, branch shortening, and delay slot scheduling are done. This pass generally, but not always, improves performance and code size, at the cost of extra compilation time, which is why there's an option to switch it off. If you're having a problem with call instructions exceeding their allowable offset range because they got conditionalized, you should consider using -mmedium-calls instead ๐Ÿ˜ญ

  • -mearly-cbranchsi: enables pre-reload use of the "cbranchsi" pattern โช
  • -mexpand-adddi: expands "adddi3" and "subdi3" at RTL generation time into "add.f", "adc" etc. ๐Ÿงฎ This option is deprecated.
  • -mindexed-loads: enables the use of indexed loads ๐Ÿ“ฆ Heads up, this can be problematic, because some optimizers then assume indexed stores exist, and they don't in this case.
  • -mlra: does nothing, preserved for backward compatibility ๐Ÿซ 
  • -mlra-priority-none: doesn't indicate any priority for target registers ๐Ÿ™ˆ
  • -mlra-priority-compact: indicates target register priority for r0..r3 / r12..r15 ๐ŸŽฏ
  • -mlra-priority-noncompact: reduces target register priority for r0..r3 / r12..r15 ๐Ÿ“‰
  • -mmillicode: when optimizing for size (using -Os), prologues and epilogues that have to save or restore a large number of registers often get shortened by calling a special function in libgcc. That's called a millicode call ๐Ÿงฉ Since these calls can cause performance issues, and/or linking issues when you link in a nonstandard way, this option lets you turn millicode call generation on or off ๐Ÿ”
  • -mcode-density-frame: lets the compiler emit "enter" and "leave" instructions ๐Ÿšช These are only valid for CPUs with the code-density feature.
  • -mmixed-code: does nothing, preserved for backward compatibility ๐Ÿซ 
  • -mq-class: this option is deprecated ๐Ÿ’€ It enables q instruction alternatives. This is the default for -Os.
  • -mRcq: does nothing, preserved for backward compatibility ๐Ÿซ 
  • -mRcw: does nothing, preserved for backward compatibility ๐Ÿซ 
  • -msize-level=level: fine-tunes size optimization with regards to instruction lengths and alignment ๐Ÿ“ The recognized values for level are:
    • 0: no size optimization. This level is deprecated and treated like 1.
    • 1: short instructions are used opportunistically โœจ
    • 2: in addition, alignment of loops and of code after barriers gets dropped ๐ŸŒ€
    • 3: in addition, optional data alignment gets dropped, and the Os option is enabled ๐Ÿ”ฅ

This defaults to 3 when -Os is in effect. Otherwise, the behavior when this isn't set is equivalent to level 1.

  • -mtune=cpu: sets instruction scheduling parameters for cpu, overriding anything implied by -mcpu= ๐ŸŽ›๏ธ

Supported values for cpu are:

  • ARC600: tunes for the ARC600 CPU ๐Ÿง
  • ARC601: tunes for the ARC601 CPU ๐Ÿง
  • ARC700: tunes for the ARC700 CPU with the standard multiplier block ๐Ÿ’ช
  • ARC700-xmac: tunes for the ARC700 CPU with the XMAC block โšก
  • ARC725D: tunes for the ARC725D CPU ๐ŸŒท
  • ARC750D: tunes for the ARC750D CPU ๐ŸŒท
  • core3: tunes for the ARCv2 core3 type CPU. This option enables usage of the "dbnz" instruction ๐Ÿง 
  • release31a: tunes for ARC4x release 3.10a ๐ŸŽ‰
  • -mmultcost=num: sets the cost to assume for a multiply instruction, with 4 being equal to a normal instruction ๐Ÿงฎ
  • -munalign-prob-threshold=probability: does nothing, preserved for backward compatibility ๐Ÿซ 

๐Ÿชฆ Deprecated, but still here for backward compatibility

The following options are kept around for backward compatibility, but they're now deprecated and will be removed in a future release. So don't build your whole life around them ๐Ÿฅน

  • -margonaut: obsolete FPX ๐Ÿ•ฐ๏ธ
  • -mbig-endian, -EB: compiles code for big-endian targets ๐ŸŒ Using these options is now deprecated. Big-endian code is supported by configuring GCC to build the "arceb-elf32" and "arceb-linux-uclibc" targets, and for those, big endian is the default ๐Ÿ’œ

Okay so bestie, we're picking up right where the ARC deprecated list leaves off ๐Ÿ’…โœจ

  • -mlittle-endian, -EL โ€” Okay so these tell GCC to build for little-endian targets ๐Ÿซถ but they're now deprecated ๐Ÿ˜ญ Little-endian is already handled by configuring GCC to build the arc-elf32 and arc-linux-uclibc targets, and little endian is the default for those ๐ŸŒธ
  • -mbarrel_shifter โ€” Replaced by -mbarrel-shifter ๐Ÿ”
  • -mdpfp_compact โ€” Replaced by -mdpfp-compact ๐Ÿ”
  • -mdpfp_fast โ€” Replaced by -mdpfp-fast ๐Ÿ”
  • -mdsp_packa โ€” Replaced by -mdsp-packa ๐Ÿ”
  • -mEA โ€” Replaced by -mea ๐Ÿ”
  • -mmac_24 โ€” Replaced by -mmac-24 ๐Ÿ”
  • -mmac_d16 โ€” Replaced by -mmac-d16 ๐Ÿ”
  • -mspfp_compact โ€” Replaced by -mspfp-compact ๐Ÿ”
  • -mspfp_fast โ€” Replaced by -mspfp-fast ๐Ÿ”
  • -mtune=cpu โ€” The values arc600, arc601, arc700 and arc700-xmac for cpu are replaced by ARC600, ARC601, ARC700 and ARC700-xmac respectively ๐Ÿ’–
  • -multcost=num โ€” Replaced by -mmultcost ๐Ÿ”

๐ŸŒˆ ARM Options ๐ŸŒˆ

These -m options are defined for the ARM port, so if you're on ARM, this is your moment ๐Ÿฆ‹

  • -mabi=name โ€” Generate code for the specified ABI ๐ŸŽ€ The permissible values are apcs-gnu, atpcs, aapcs, aapcs-linux and iwmmxt
  • -mapcs-frame โ€” Generate a stack frame that follows the ARM Procedure Call Standard for all functions ๐Ÿงš even when it isn't strictly needed for the code to run correctly. If you pair this with -fomit-frame-pointer, stack frames won't be generated for leaf functions. The default is -mno-apcs-frame, and this option is deprecated ๐Ÿ’€
  • -mapcs โ€” A synonym for -mapcs-frame, and it's also deprecated ๐Ÿ™ˆ
  • -mthumb-interwork โ€” Generate code that supports calling between the ARM and Thumb instruction sets ๐Ÿ”ฅ Without this option, on pre-v5 architectures, the two instruction sets can't be reliably used inside one program ๐Ÿ˜ญ The default is -mno-thumb-interwork, because slightly larger code gets generated when -mthumb-interwork is specified. In AAPCS configurations this option is meaningless ๐Ÿ’…
  • -mno-sched-prolog โ€” Stops the reordering of instructions in the function prologue, and also stops those instructions from being merged with the instructions in the function's body โœจ This means every function starts with a recognizable set of instructions (or one from a small set of different prologues), so you can use that info to find where functions start inside an executable chunk of code ๐Ÿ” The default is -msched-prolog
  • -mfloat-abi=name โ€” Picks which floating-point ABI to use ๐Ÿ’œ The permissible values are soft, softfp and hard
    • soft makes GCC generate output with library calls for floating-point operations ๐Ÿ“š
    • softfp lets the code use hardware floating-point instructions, but still uses the soft-float calling conventions ๐Ÿซถ
    • hard allows floating-point instructions and uses FPU-specific calling conventions โšก

The default depends on the specific target configuration. Heads up though, bestie: the hard-float and soft-float ABIs are not link-compatible ๐Ÿ˜ญ You have to compile your entire program with the same ABI and link it with a compatible set of libraries ๐Ÿ™

  • -mgeneral-regs-only โ€” Generate code that only uses the general-purpose registers ๐ŸŒธ This stops the compiler from using floating-point and Advanced SIMD registers, but it won't put any restrictions on the assembler ๐ŸŽ€
  • -mlittle-endian โ€” Generate code for a processor running in little-endian mode ๐Ÿ’– This is the default for all standard configurations ๐Ÿ‘‘
  • -mbig-endian โ€” Generate code for a processor running in big-endian mode ๐Ÿงฟ The default is to compile for a little-endian processor, so you have to ask for this one explicitly ๐Ÿ™Œ
  • -mbe8, -mbe32 โ€” When linking a big-endian image, pick between the BE8 and BE32 formats ๐Ÿ“ These have no effect on little-endian images and get ignored there ๐Ÿ™ˆ The default depends on the selected target architecture: for ARMv6 and later it's BE8, and for older architectures it's BE32. ARM has deprecated the BE32 format ๐Ÿ’€

โœจ -march=name[+extension...] โœจ

This picks the name of the target ARM architecture ๐Ÿฆ‹ GCC uses this name to figure out what kinds of instructions it's allowed to emit when generating assembly code. You can use this option together with -mcpu=, or instead of it ๐Ÿ’…

The permissible names are armv4t, armv5t, armv5te, armv6, armv6j, armv6k, armv6kz, armv6t2, armv6z, armv6zk, armv7, armv7-a, armv7ve, armv8-a, armv8.1-a, armv8.2-a, armv8.3-a, armv8.4-a, armv8.5-a, armv8.6-a, armv9-a, armv7-r, armv8-r, armv6-m, armv6s-m, armv7-m, armv7e-m, armv8-m.base, armv8-m.main, armv8.1-m.main, iwmmxt and iwmmxt2 ๐ŸŒˆ

Also, these architectures don't support the Thumb execution state, so they're recognized but support is deprecated ๐Ÿ˜ญ That's just armv4 ๐Ÿ™ˆ

Lots of the architectures support extensions, which you add by appending +extension to the architecture name ๐Ÿงš Extension options are processed in order and their capabilities stack up. An extension will also turn on any base extensions it depends on. For example, +crypto will always enable +simd ๐Ÿ”ฅ The exception to the stacking is extensions prefixed with +no...: those disable the option they name, plus any other extensions that depend on it โœ‹

For example, -march=armv7-a+simd+nofp+vfpv4 is equivalent to writing -march=armv7-a+vfpv4, since +simd gets totally disabled by the +nofp that comes after it ๐Ÿ’€

Most extension names are generic, but what they actually do depends on the architecture you apply them to ๐Ÿฆ‹ For example, +simd works on both armv7-a and armv8-a, but it turns on the original ARMv7-A Advanced SIMD (Neon) extensions for armv7-a, and the ARMv8-A variant for armv8-a ๐Ÿซถ

Here's the table of supported extensions for each architecture, in list form bestie ๐Ÿ’– Architectures not mentioned don't support any extensions.

๐ŸŒธ armv5te, armv6, armv6j, armv6k, armv6kz, armv6t2, armv6z, armv6zk ๐ŸŒธ

  • +fp โ€” The VFPv2 floating-point instructions ๐ŸŽ€ The extension +vfpv2 can be used as an alias for this one
  • +nofp โ€” Disables the floating-point instructions ๐Ÿ™…โ€โ™€๏ธ

๐Ÿงš armv7 ๐Ÿงš

The common subset of the ARMv7-A, ARMv7-R and ARMv7-M architectures โœจ

  • +fp โ€” The VFPv3 floating-point instructions, with 16 double-precision registers ๐Ÿ’œ The extension +vfpv3-d16 can be used as an alias for this one. Heads up: floating-point isn't supported by the base ARMv7-M architecture, but it is compatible with both ARMv7-A and ARMv7-R ๐Ÿ™ˆ
  • +nofp โ€” Disables the floating-point instructions ๐Ÿ™…โ€โ™€๏ธ

๐Ÿ‘‘ armv7-a ๐Ÿ‘‘

  • +mp โ€” The multiprocessing extension ๐Ÿซถ
  • +sec โ€” The security extension ๐Ÿ”
  • +fp โ€” The VFPv3 floating-point instructions, with 16 double-precision registers ๐Ÿ’– The extension +vfpv3-d16 can be used as an alias for this one
  • +simd โ€” The Advanced SIMD (Neon) v1 and the VFPv3 floating-point instructions ๐Ÿ”ฅ The extensions +neon and +neon-vfpv3 can be used as aliases for this one
  • +vfpv3 โ€” The VFPv3 floating-point instructions, with 32 double-precision registers ๐ŸŒˆ
  • +vfpv3-d16-fp16 โ€” The VFPv3 floating-point instructions, with 16 double-precision registers and the half-precision floating-point conversion operations ๐Ÿ“
  • +vfpv3-fp16 โ€” The VFPv3 floating-point instructions, with 32 double-precision registers and the half-precision floating-point conversion operations ๐Ÿ“
  • +vfpv4-d16 โ€” The VFPv4 floating-point instructions, with 16 double-precision registers ๐Ÿฆ‹
  • +vfpv4 โ€” The VFPv4 floating-point instructions, with 32 double-precision registers ๐Ÿฆ‹
  • +neon-fp16 โ€” The Advanced SIMD (Neon) v1 and the VFPv3 floating-point instructions, with the half-precision floating-point conversion operations โœจ
  • +neon-vfpv4 โ€” The Advanced SIMD (Neon) v2 and the VFPv4 floating-point instructions ๐Ÿ’…
  • +nosimd โ€” Disables the Advanced SIMD instructions (this does not disable floating point) ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” Disables the floating-point and Advanced SIMD instructions ๐Ÿ’€

๐Ÿฆ‹ armv7ve ๐Ÿฆ‹

The extended version of the ARMv7-A architecture, with support for virtualization ๐Ÿงฟ

  • +fp โ€” The VFPv4 floating-point instructions, with 16 double-precision registers ๐Ÿ’œ The extension +vfpv4-d16 can be used as an alias for this one
  • +simd โ€” The Advanced SIMD (Neon) v2 and the VFPv4 floating-point instructions ๐Ÿ”ฅ The extension +neon-vfpv4 can be used as an alias for this one
  • +vfpv3-d16 โ€” The VFPv3 floating-point instructions, with 16 double-precision registers ๐ŸŒธ
  • +vfpv3 โ€” The VFPv3 floating-point instructions, with 32 double-precision registers ๐ŸŒธ
  • +vfpv3-d16-fp16 โ€” The VFPv3 floating-point instructions, with 16 double-precision registers and the half-precision floating-point conversion operations ๐Ÿ“
  • +vfpv3-fp16 โ€” The VFPv3 floating-point instructions, with 32 double-precision registers and the half-precision floating-point conversion operations ๐Ÿ“
  • +vfpv4-d16 โ€” The VFPv4 floating-point instructions, with 16 double-precision registers ๐Ÿฆ‹
  • +vfpv4 โ€” The VFPv4 floating-point instructions, with 32 double-precision registers ๐Ÿฆ‹
  • +neon โ€” The Advanced SIMD (Neon) v1 and the VFPv3 floating-point instructions ๐ŸŽ€ The extension +neon-vfpv3 can be used as an alias for this one
  • +neon-fp16 โ€” The Advanced SIMD (Neon) v1 and the VFPv3 floating-point instructions, with the half-precision floating-point conversion operations โœจ
  • +nosimd โ€” okay so this one switches off the Advanced SIMD instructions ๐Ÿ™…โ€โ™€๏ธ but floating point stays on, so it's not a total wipeout ๐Ÿ’… (it does not disable floating point).
  • +nofp โ€” bye bye to the floating-point and Advanced SIMD instructions ๐Ÿ’€ both get disabled together ๐Ÿ˜ญ

armv8-a

  • +crc โ€” the Cyclic Redundancy Check (CRC) instructions โœจ
  • +simd โ€” the ARMv8-A Advanced SIMD and floating-point instructions ๐Ÿ”ฅ
  • +crypto โ€” the cryptographic instructions ๐Ÿ”
  • +nocrypto โ€” disables the cryptographic instructions, no cap ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” disables the floating-point, Advanced SIMD and cryptographic instructions all at once ๐Ÿ’€
  • +sb โ€” the Speculation Barrier Instruction ๐Ÿ›‘
  • +predres โ€” the Execution and Data Prediction Restriction Instructions ๐ŸŽ€

armv8.1-a

  • +simd โ€” the ARMv8.1-A Advanced SIMD and floating-point instructions ๐ŸŒˆ
  • +crypto โ€” the cryptographic instructions ๐Ÿ” this one also turns on the Advanced SIMD and floating-point instructions, so it's giving bundle deal ๐Ÿ“
  • +nocrypto โ€” disables the cryptographic instructions ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” disables the floating-point, Advanced SIMD and cryptographic instructions ๐Ÿ’€
  • +sb โ€” the Speculation Barrier Instruction ๐Ÿ›‘
  • +predres โ€” the Execution and Data Prediction Restriction Instructions ๐ŸŽ€

armv8.2-a and armv8.3-a

  • +fp16 โ€” the half-precision floating-point data processing instructions ๐ŸŒธ this also enables the Advanced SIMD and floating-point instructions too โœจ
  • +fp16fml โ€” the half-precision floating-point fmla extension ๐Ÿฆ‹ this also enables the half-precision floating-point extension, plus the Advanced SIMD and floating-point instructions ๐Ÿซถ
  • +simd โ€” the ARMv8.1-A Advanced SIMD and floating-point instructions ๐ŸŒˆ
  • +crypto โ€” the cryptographic instructions ๐Ÿ” this also enables the Advanced SIMD and floating-point instructions ๐Ÿ“
  • +dotprod โ€” enables the Dot Product extension ๐Ÿงš this also enables Advanced SIMD instructions
  • +nocrypto โ€” disables the cryptographic extension ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” disables the floating-point, Advanced SIMD and cryptographic instructions ๐Ÿ’€
  • +sb โ€” the Speculation Barrier Instruction ๐Ÿ›‘
  • +predres โ€” the Execution and Data Prediction Restriction Instructions ๐ŸŽ€
  • +i8mm โ€” the 8-bit Integer Matrix Multiply instructions ๐Ÿ’œ this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ
  • +bf16 โ€” the Brain half-precision floating-point instructions ๐Ÿง  this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ

armv8.4-a

  • +fp16 โ€” the half-precision floating-point data processing instructions ๐ŸŒธ this also enables the Advanced SIMD and floating-point instructions, as well as the Dot Product extension and the half-precision floating-point fmla extension ๐Ÿ‘‘
  • +simd โ€” the ARMv8.3-A Advanced SIMD and floating-point instructions, as well as the Dot Product extension ๐ŸŒˆ
  • +crypto โ€” the cryptographic instructions ๐Ÿ” this also enables the Advanced SIMD and floating-point instructions, as well as the Dot Product extension ๐Ÿ“
  • +nocrypto โ€” disables the cryptographic extension ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” disables the floating-point, Advanced SIMD and cryptographic instructions ๐Ÿ’€
  • +sb โ€” the Speculation Barrier Instruction ๐Ÿ›‘
  • +predres โ€” the Execution and Data Prediction Restriction Instructions ๐ŸŽ€
  • +i8mm โ€” the 8-bit Integer Matrix Multiply instructions ๐Ÿ’œ this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ
  • +bf16 โ€” the Brain half-precision floating-point instructions ๐Ÿง  this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ

armv8.5-a

  • +fp16 โ€” the half-precision floating-point data processing instructions ๐ŸŒธ this also enables the Advanced SIMD and floating-point instructions, as well as the Dot Product extension and the half-precision floating-point fmla extension ๐Ÿ‘‘
  • +simd โ€” the ARMv8.3-A Advanced SIMD and floating-point instructions, as well as the Dot Product extension ๐ŸŒˆ
  • +crypto โ€” the cryptographic instructions ๐Ÿ” this also enables the Advanced SIMD and floating-point instructions, as well as the Dot Product extension ๐Ÿ“
  • +nocrypto โ€” disables the cryptographic extension ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” disables the floating-point, Advanced SIMD and cryptographic instructions ๐Ÿ’€
  • +i8mm โ€” the 8-bit Integer Matrix Multiply instructions ๐Ÿ’œ this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ
  • +bf16 โ€” the Brain half-precision floating-point instructions ๐Ÿง  this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ

armv8.6-a

  • +fp16 โ€” the half-precision floating-point data processing instructions ๐ŸŒธ this also enables the Advanced SIMD and floating-point instructions, as well as the Dot Product extension and the half-precision floating-point fmla extension ๐Ÿ‘‘
  • +simd โ€” the ARMv8.3-A Advanced SIMD and floating-point instructions, as well as the Dot Product extension ๐ŸŒˆ
  • +crypto โ€” the cryptographic instructions ๐Ÿ” this also enables the Advanced SIMD and floating-point instructions, as well as the Dot Product extension ๐Ÿ“
  • +nocrypto โ€” disables the cryptographic extension ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” disables the floating-point, Advanced SIMD and cryptographic instructions ๐Ÿ’€
  • +i8mm โ€” the 8-bit Integer Matrix Multiply instructions ๐Ÿ’œ this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ
  • +bf16 โ€” the Brain half-precision floating-point instructions ๐Ÿง  this also enables Advanced SIMD and floating-point instructions ๐Ÿ”ฅ

armv7-r

  • +fp.sp โ€” the single-precision VFPv3 floating-point instructions ๐ŸŽˆ the extension +vfpv3xd can be used as an alias for this extension ๐Ÿฆ‹
  • +fp โ€” the VFPv3 floating-point instructions with 16 double-precision registers ๐Ÿ’Ž the extension +vfpv3-d16 can be used as an alias for this extension ๐Ÿฆ‹
  • +vfpv3xd-d16-fp16 โ€” the single-precision VFPv3 floating-point instructions with 16 double-precision registers, plus the half-precision floating-point conversion operations ๐Ÿงš
  • +vfpv3-d16-fp16 โ€” the VFPv3 floating-point instructions with 16 double-precision registers, plus the half-precision floating-point conversion operations ๐Ÿงš
  • +nofp โ€” disables the floating-point extension ๐Ÿ™…โ€โ™€๏ธ
  • +idiv โ€” the ARM-state integer division instructions โž—
  • +noidiv โ€” disables the ARM-state integer division extension ๐Ÿ™…โ€โ™€๏ธ

armv7e-m

  • +fp โ€” the single-precision VFPv4 floating-point instructions ๐ŸŽˆ
  • +fpv5 โ€” the single-precision FPv5 floating-point instructions ๐ŸŽˆ
  • +fp.dp โ€” the single- and double-precision FPv5 floating-point instructions ๐Ÿ’ฅ
  • +nofp โ€” disables the floating-point extensions ๐Ÿ™…โ€โ™€๏ธ

armv8.1-m.main

  • +dsp โ€” the DSP instructions โญ
  • +mve โ€” the M-Profile Vector Extension (MVE) integer instructions ๐ŸŽ€
  • +mve.fp โ€” the M-Profile Vector Extension (MVE) integer and single precision floating-point instructions ๐Ÿ’œ
  • +fp โ€” the single-precision floating-point instructions ๐ŸŽˆ
  • +fp.dp โ€” the single- and double-precision floating-point instructions ๐Ÿ’ฅ
  • +nofp โ€” disables the floating-point extension ๐Ÿ™…โ€โ™€๏ธ
  • +cdecp0, +cdecp1, ... , +cdecp7 โ€” enables the Custom Datapath Extension (CDE) on selected coprocessors, according to the numbers given in the options in the range 0 to 7 ๐Ÿงฉ
  • +pacbti โ€” enables the Pointer Authentication and Branch Target Identification Extension ๐Ÿ”’

armv8-m.main

  • +dsp โ€” the DSP instructions โญ
  • +nodsp โ€” disables the DSP extension ๐Ÿ™…โ€โ™€๏ธ
  • +fp โ€” the single-precision floating-point instructions ๐ŸŽˆ
  • +fp.dp โ€” the single- and double-precision floating-point instructions ๐Ÿ’ฅ
  • +nofp โ€” disables the floating-point extension ๐Ÿ™…โ€โ™€๏ธ
  • +cdecp0, +cdecp1, ... , +cdecp7 โ€” enables the Custom Datapath Extension (CDE) on selected coprocessors, according to the numbers given in the options in the range 0 to 7 ๐Ÿงฉ

armv8-r

  • +crc โ€” the Cyclic Redundancy Check (CRC) instructions โœจ
  • +fp.sp โ€” the single-precision FPv5 floating-point instructions ๐ŸŽˆ
  • +simd โ€” the ARMv8-A Advanced SIMD and floating-point instructions ๐ŸŒˆ
  • +crypto โ€” the cryptographic instructions ๐Ÿ”
  • +nocrypto โ€” disables the cryptographic instructions ๐Ÿ™…โ€โ™€๏ธ
  • +nofp โ€” disables the floating-point, Advanced SIMD and cryptographic instructions ๐Ÿ’€

Okay, two special flags you need to know about ๐Ÿซถ

-march=native tells the compiler to auto-detect the architecture of the build computer ๐Ÿ” but right now this only works on GNU/Linux, and not every architecture gets recognized ๐Ÿ˜ฌ if the auto-detect doesn't work out, the option just has no effect ๐Ÿ™ˆ

-march=unset tells the compiler to ignore any -march=... options that showed up earlier on the command line, and to behave like the option was never passed at all โœจ this is super useful for avoiding warnings about conflicting CPU and architecture options when the two produce different architecture specifications ๐Ÿ’…

  • -mtune=name โ€” okay bestie, this one tells GCC which target ARM processor to tune your code's performance for โœจ Some ARM chips run noticeably faster when you use it ๐Ÿš€ The permissible names are arm7tdmi, arm7tdmi-s, arm710t, arm720t, arm740t, strongarm, strongarm110, strongarm1100, strongarm1110, arm8, arm810, arm9, arm9e, arm920, arm920t, arm922t, arm946e-s, arm966e-s, arm968e-s, arm926ej-s, arm940t, arm9tdmi, arm10tdmi, arm1020t, arm1026ej-s, arm10e, arm1020e, arm1022e, arm1136j-s, arm1136jf-s, mpcore, mpcorenovfp, arm1156t2-s, arm1156t2f-s, arm1176jz-s, arm1176jzf-s, generic-armv7-a, cortex-a5, cortex-a7, cortex-a8, cortex-a9, cortex-a12, cortex-a15, cortex-a17, cortex-a32, cortex-a35, cortex-a53, cortex-a55, cortex-a57, cortex-a72, cortex-a73, cortex-a75, cortex-a76, cortex-a76ae, cortex-a77, cortex-a78, cortex-a78ae, cortex-a78c, cortex-a710, ares, cortex-r4, cortex-r4f, cortex-r5, cortex-r7, cortex-r8, cortex-r52, cortex-r52plus, cortex-m0, cortex-m0plus, cortex-m1, cortex-m3, cortex-m4, cortex-m7, cortex-m23, cortex-m33, cortex-m35p, cortex-m52, cortex-m55, cortex-m85, cortex-x1, cortex-x1c, cortex-m1.small-multiply, cortex-m0.small-multiply, cortex-m0plus.small-multiply, exynos-m1, marvell-pj4, neoverse-n1, neoverse-n2, neoverse-v1, xscale, iwmmxt, iwmmxt2, ep9312, fa526, fa626, fa606te, fa626te, fmp626, fa726te, star-mc1 and xgene1 ๐Ÿ’–

Also, this option can tune for a big.LITTLE system ๐ŸŒˆ The permissible big.LITTLE names are cortex-a15.cortex-a7, cortex-a17.cortex-a7, cortex-a57.cortex-a53, cortex-a72.cortex-a53, cortex-a72.cortex-a35, cortex-a73.cortex-a53, cortex-a75.cortex-a55 and cortex-a76.cortex-a55 ๐Ÿซถ

-mtune=generic-arch is giving "blend of everything" energy ๐ŸŒธ It tells GCC to tune performance for a mix of processors within the architecture arch. The goal is code that runs well on the most popular chips right now, balancing the optimizations that help some CPUs in the range while dodging the performance pitfalls of others. The effects of this may change in future GCC versions as CPU models come and go, so don't be shocked if it shifts ๐Ÿฆ‹

-mtune accepts the same extension options as -mcpu, but the extensions don't change how the generated code gets tuned ๐Ÿงš

-mtune=native makes the compiler auto-detect the CPU of the build computer ๐Ÿ‘‘ Right now this only works on GNU/Linux, and not every architecture is recognized. If the auto-detect doesn't work out, the option has no effect ๐Ÿ™ˆ

  • -mcpu=name[+extension...] โ€” this names the target ARM processor, and GCC uses it to figure out two things: the target ARM architecture (as if you'd passed -march) and the processor type to tune for (as if you'd passed -mtune) โœจ If you use this alongside -march or -mtune, those two take precedence over the matching part of this option ๐Ÿ’…

Lowkey, a lot of the supported CPUs come with optional architectural extensions ๐ŸŽ€ Those are usually enabled by default. If some implementations don't have an extension, you can use the extension syntax to switch off the ones that are missing ๐Ÿซ  Floating-point and Advanced SIMD (Neon) instructions also depend on -mfloat-abi and -mfpu. FP and SIMD instructions only get used if -mfloat-abi isn't set to soft, and any -mfpu setting other than auto will override the available floating-point and SIMD extension instructions ๐Ÿ˜ญ

For example, cortex-a9 shows up in three main configurations: integer only, with just a floating-point unit, or with floating-point and Advanced SIMD ๐Ÿ”ฅ By default everything is enabled, but you can use +nosimd to turn off just the SIMD, or +nofp to turn off both SIMD and floating-point ๐Ÿ’œ

The permissible names for this option are the same as the ones for -mtune ๐ŸŒˆ

These extension options are shared by the listed CPUs:

  • +nodsp: switches off the DSP instructions on cortex-m33, cortex-m35p, cortex-m52, cortex-m55 and cortex-m85. It also switches off the M-Profile Vector Extension (MVE) integer and single precision floating-point instructions on cortex-m52, cortex-m55 and cortex-m85 ๐Ÿ™ˆ
  • +nopacbti: switches off the Pointer Authentication and Branch Target Identification Extension on cortex-m52 and cortex-m85 ๐Ÿ”
  • +nomve: switches off the M-Profile Vector Extension (MVE) integer and single precision floating-point instructions on cortex-m52, cortex-m55 and cortex-m85 ๐Ÿ’…
  • +nomve.fp: switches off just the M-Profile Vector Extension (MVE) single precision floating-point instructions on cortex-m52, cortex-m55 and cortex-m85 โœจ
  • +cdecp0, +cdecp1, ... , +cdecp7: enables the Custom Datapath Extension (CDE) on selected coprocessors, based on the numbers given in the options in the range 0 to 7, on cortex-m52, cortex-m55 and star-mc1 ๐Ÿงš
  • +nofp: disables the floating-point instructions on arm9e, arm946e-s, arm966e-s, arm968e-s, arm10e, arm1020e, arm1022e, arm926ej-s, arm1026ej-s, cortex-r5, cortex-r7, cortex-r8, cortex-m4, cortex-m7, cortex-m33, cortex-m35p, cortex-m52, cortex-m55 and cortex-m85. On generic-armv7-a, cortex-a5, cortex-a7, cortex-a8, cortex-a9, cortex-a12, cortex-a15, cortex-a17, cortex-a15.cortex-a7, cortex-a17.cortex-a7, cortex-a32, cortex-a35, cortex-a53 and cortex-a55, it disables the floating-point and SIMD instructions ๐Ÿ’–
  • +nofp.dp: disables the double-precision component of the floating-point instructions on cortex-r5, cortex-r7, cortex-r8, cortex-r52, cortex-r52plus and cortex-m7 ๐ŸŒธ
  • +nosimd: disables the SIMD (but not floating-point) instructions on generic-armv7-a, cortex-a5, cortex-a7 and cortex-a9 ๐Ÿฆ‹
  • +crypto: enables the cryptographic instructions on cortex-a32, cortex-a35, cortex-a53, cortex-a55, cortex-a57, cortex-a72, cortex-a73, cortex-a75, exynos-m1, xgene1, cortex-a57.cortex-a53, cortex-a72.cortex-a53, cortex-a73.cortex-a35, cortex-a73.cortex-a53 and cortex-a75.cortex-a55 ๐Ÿ”ฅ

Also, the generic-armv7-a pseudo target defaults to VFPv3 with 16 double-precision registers ๐Ÿ‘‘ It supports these extension options: mp, sec, vfpv3-d16, vfpv3, vfpv3-d16-fp16, vfpv3-fp16, vfpv4-d16, vfpv4, neon, neon-vfpv3, neon-fp16 and neon-vfpv4. They mean the same thing as the extensions to -march=armv7-a ๐ŸŽ€

-mcpu=generic-arch is also allowed, and it's equivalent to -march=arch -mtune=generic-arch. Check -mtune for more info ๐Ÿซถ

-mcpu=native makes the compiler auto-detect the CPU of the build computer ๐Ÿ’… Right now this only works on GNU/Linux, and not every architecture is recognized. If the auto-detect fails, the option has no effect ๐Ÿ™ˆ

-mcpu=unset tells the compiler to ignore any -mcpu=... options that appeared earlier on the command line, and to act like that option was never passed ๐Ÿงน This is handy for avoiding warnings about conflicting CPU and architecture options when the two produce different architecture specifications โœจ

  • -mfpu=name โ€” this specifies what floating-point hardware (or hardware emulation) is available on the target ๐ŸŒˆ The permissible names are auto, vfpv2, vfpv3, vfpv3-fp16, vfpv3-d16, vfpv3-d16-fp16, vfpv3xd, vfpv3xd-fp16, neon-vfpv3, neon-fp16, vfpv4, vfpv4-d16, fpv4-sp-d16, neon-vfpv4, fpv5-d16, fpv5-sp-d16, fp-armv8, neon-fp-armv8 and crypto-neon-fp-armv8. Note that neon is an alias for neon-vfpv3, and vfp is an alias for vfpv2 ๐Ÿ“

auto is the default and it's kind of special ๐Ÿงš It makes the compiler pick the floating-point and Advanced SIMD instructions based on the settings of -mcpu and -march โœจ

If the floating-point hardware you picked includes the NEON extension (e.g. -mfpu=neon), heads up: floating-point operations aren't generated by GCC's auto-vectorization pass unless -funsafe-math-optimizations is also specified ๐Ÿ˜ญ That's because NEON hardware doesn't fully implement the IEEE 754 standard for floating-point arithmetic (denormal values are treated as zero, in particular), so using NEON instructions may lead to a loss of precision ๐Ÿ’€

You can also set the fpu name at function level using the target("fpu=") function attributes or pragmas ๐Ÿฆ‹

  • -mfp16-format=name โ€” specifies the format of the "__fp16" half-precision floating-point type ๐Ÿ’œ The permissible names are none, ieee and alternative. The default is none, and in that case the "__fp16" type isn't defined ๐Ÿ™ˆ
  • -mstructure-size-boundary=n โ€” the sizes of all structures and unions get rounded up to a multiple of the number of bits set by this option ๐ŸŽ€ Permissible values are 8, 32 and 64. The default value varies for different toolchains. For the COFF targeted toolchain, the default is 8. A value of 64 is only allowed if the underlying ABI supports it ๐Ÿ’…

Going bigger can produce faster, more efficient code, but it can also increase the size of your program โœจ Different values are potentially incompatible. Code compiled with one value can't necessarily expect to work with code or libraries compiled with another value, if they exchange information using structures or unions ๐Ÿ˜ฌ

โš ๏ธ This option is deprecated. Don't build new stuff around it, bestie ๐Ÿ’€
  • -mabort-on-noreturn โ€” generates a call to the function "abort" at the end of a "noreturn" function ๐ŸŒธ It's executed if the function tries to return ๐Ÿ˜ณ
  • -mlong-calls and -mno-long-calls โ€” tells the compiler to perform function calls by first loading the address of the function into a register, and then doing a subroutine call on that register ๐Ÿฆ‹ You need this switch if the target function lies outside the 64-megabyte addressing range of the offset-based version of the subroutine call instruction ๐ŸŒˆ

Even if this switch is enabled, not every function call gets turned into a long call ๐ŸŽ€ The vibe check goes like this: static functions, functions with the "short_call" attribute, functions inside a #pragma no_long_calls scope, and functions whose definitions were already compiled in the current compilation unit all stay short. The exceptions are always long calls, no cap: weak function definitions, functions with the "long_call" or "section" attribute, and functions inside a #pragma long_calls scope ๐Ÿ’…

This feature is not enabled by default ๐Ÿ™ˆ -mno-long-calls brings back the default behavior, and so does wrapping the function calls in a #pragma long_calls_off directive. Also, these switches do nothing for how the compiler handles function calls through function pointers, so don't expect them to โœจ

  • -msingle-pic-base โ€” okay so this one treats the register used for PIC addressing as read-only ๐Ÿ”’ Instead of loading it in the prologue for every function, the runtime system is the one responsible for setting it to an appropriate value before execution begins ๐Ÿฆ‹
  • -mpic-register=reg โ€” this lets you pick the register used for PIC addressing ๐Ÿ’œ For the standard PIC base case, the default is any suitable register the compiler decides on. For the single PIC base case, the default is R9 if the target is EABI based or stack-checking is enabled, and otherwise it's R10 ๐ŸŒธ
  • -mpic-data-is-text-relative โ€” assume the displacement between the text and data segments is fixed at static link time ๐Ÿงš This lets you use PC-relative addressing to access data you know is in the data segment. For non-VxWorks RTP targets this is enabled by default, and if you disable it on those targets, -msingle-pic-base gets enabled by default instead ๐Ÿซถ
  • -mpoke-function-name โ€” this one writes the name of each function into the text section, right before the function prologue โœจ The generated code looks kind of like this:
t0
    .ascii "arm_poke_function_name", 0
    .align
t1
    .word 0xff000000 + (t1 - t0)
arm_poke_function_name
    mov     ip, sp
    stmfd   sp!, {fp, ip, lr, pc}
    sub     fp, ip, #4

When you're doing a stack backtrace, the code can check the pc value stored at fp + 0 ๐Ÿ‘€ If the trace function then looks at location pc - 12 and the top 8 bits are set, we know there's a function name embedded right before that location, with length ((pc[-3]) & 0xff000000) ๐Ÿ˜ญ

  • -mthumb, -marm โ€” pick between generating code that runs in ARM state or Thumb state ๐Ÿ“ For most configurations the default is ARM state, but you can change that by configuring GCC with the --with-mode=state configure option ๐Ÿ’– You can also override ARM or Thumb mode for each function with the target("thumb") and target("arm") function attributes or pragmas, which is honestly so iconic ๐Ÿ‘‘
  • -mflip-thumb โ€” switches ARM and Thumb modes on alternating functions ๐ŸŒˆ This one exists purely for regression testing of mixed Thumb/ARM code generation, so please don't use it to compile your actual code ๐Ÿ’€
  • -mtpcs-frame โ€” generates a stack frame that follows the Thumb Procedure Call Standard for all non-leaf functions ๐Ÿฆ‹ (A leaf function is one that doesn't call any other functions.) The default is -mno-tpcs-frame โœจ
  • -mtpcs-leaf-frame โ€” same energy, but this time it generates a Thumb Procedure Call Standard compliant stack frame for all leaf functions ๐ŸŒธ (Again, a leaf function is one that doesn't call any other functions.) The default is -mno-apcs-leaf-frame ๐Ÿ’…
  • -mcallee-super-interworking โ€” gives every externally visible function in the file being compiled an ARM instruction set header ๐Ÿงš That header switches to Thumb mode before running the rest of the function, so these functions can be called from non-interworking code. This option is not valid in AAPCS configurations, because interworking is already enabled by default there ๐Ÿ™ˆ
  • -mcaller-super-interworking โ€” lets calls through function pointers (including virtual functions) run correctly, no matter whether the target code was compiled for interworking or not ๐Ÿซถ Heads up, there's a small overhead when executing a function pointer if you turn this on. It's also not valid in AAPCS configurations, since interworking is already enabled by default there ๐Ÿ’œ
  • -mtp=name โ€” specifies the access model for the thread local storage pointer ๐Ÿ”ฅ The soft model generates calls to __aeabi_read_tp. The other accepted models are tpidrurw, tpidruro, and tpidrprw, which fetch the thread pointer straight from the matching system register (supported from the arm6k architecture and later) โœจ Those system registers get accessed through the CP15 co-processor interface, and the argument cp15 is also accepted as a convenience alias of tpidruro ๐ŸŽ€ The argument auto uses the best available method for the selected processor, and that's also the default setting ๐Ÿ’–
  • -mtls-dialect=dialect โ€” picks the dialect used for accessing thread local storage ๐ŸŒˆ There are two: gnu and gnu2. The gnu dialect selects the original GNU scheme for supporting local and global dynamic TLS models. The gnu2 dialect selects the GNU descriptor scheme, which performs better for shared libraries ๐Ÿš€ The GNU descriptor scheme is compatible with the original scheme, but it does need new assembler, linker, and library support. Initial and local exec TLS models aren't affected by this option and always use the original scheme ๐Ÿซถ
  • -mword-relocations โ€” only generates absolute relocations on word-sized values (i.e. R_ARM_ABS32) ๐Ÿ“ This is enabled by default on targets (uClinux, SymbianOS) where the runtime loader imposes this restriction, and whenever -fpic or -fPIC is specified. This option conflicts with -mslow-flash-data ๐Ÿ™ˆ
  • -mfix-cortex-m3-ldrd โ€” some Cortex-M3 cores can cause data corruption when ldrd instructions with overlapping destination and base registers get used ๐Ÿ˜ญ This option avoids generating those instructions. It's enabled by default when -mcpu=cortex-m3 is specified ๐Ÿ’–
  • -mfix-cortex-a57-aes-1742098, -mno-fix-cortex-a57-aes-1742098, -mfix-cortex-a72-aes-1655431, -mno-fix-cortex-a72-aes-1655431 โ€” these enable (or disable) the mitigation for an erratum on Cortex-A57 and Cortex-A72 that affects the AES cryptographic instructions ๐Ÿ” This is enabled by default when either -mcpu=cortex-a57 or -mcpu=cortex-a72 is specified ๐ŸŒธ
  • -munaligned-access, -mno-unaligned-access โ€” enables (or disables) reading and writing 16- and 32-bit values from addresses that aren't 16- or 32-bit aligned ๐Ÿฆ‹ By default, unaligned access is disabled for all pre-ARMv6, all ARMv6-M, and ARMv8-M Baseline architectures, and enabled for every other architecture. If unaligned access isn't enabled, words in packed data structures get accessed one byte at a time ๐Ÿฅน

The ARM attribute "Tag_CPU_unaligned_access" gets set in the generated object file to either true or false, depending on this option's setting โœจ If unaligned access is enabled, the preprocessor symbol __ARM_FEATURE_UNALIGNED is also defined ๐Ÿงš

  • -mneon-for-64bits โ€” this one is deprecated and has no effect ๐Ÿ’€ Bye bye ๐Ÿ‘‹
  • -mslow-flash-data โ€” assumes loading data from flash is slower than fetching instructions, so literal loads get minimized for better performance ๐Ÿข This option is only supported when compiling for ARMv7 M-profile, and it's off by default. It conflicts with -mword-relocations ๐Ÿ™…โ€โ™€๏ธ
  • -masm-syntax-unified โ€” assumes inline assembler is using unified asm syntax ๐Ÿ’œ The default is currently off, which implies divided syntax. This option has no impact on Thumb2, but that may change in future releases of GCC. Divided syntax should be considered deprecated, so be careful with it ๐Ÿซถ
  • -mrestrict-it โ€” restricts generation of IT blocks so they conform to the rules of ARMv8-A ๐Ÿ”ฅ IT blocks can only contain a single 16-bit instruction from a select set of instructions. This option is on by default for ARMv8-A Thumb mode ๐Ÿ‘‘
  • -mprint-tune-info โ€” prints CPU tuning information as a comment in the assembler file ๐ŸŽ€ This is only used for regression testing of the compiler and isn't meant for ordinary code compiling. It's disabled by default ๐Ÿ™ˆ
  • -mverbose-cost-dump โ€” enables verbose cost model dumping in the debug dump files ๐Ÿ” This one's provided for debugging the compiler, so it's a niche pick ๐Ÿงš
  • -mpure-code โ€” doesn't allow constant data to be placed in code sections ๐Ÿ”’ Also, when compiling for the ELF object format, it gives all text sections the ELF processor-specific section attribute "SHF_ARM_PURECODE". This option is only available when generating non-PIC code for M-profile targets ๐Ÿ’…
  • -mcmse โ€” generates secure code as per the "ARMv8-M Security Extensions: Requirements on Development Tools Engineering Specification", which you can find at <https://developer.arm.com/documentation/ecm0359818/latest/> ๐Ÿ›ก๏ธโœจ
  • -mfix-cmse-cve-2021-35465 โ€” mitigates a potential security issue with the "VLLDM" instruction on some M-profile devices when using CMSE (CVE-2021-365465) ๐Ÿšจ This option is enabled by default when -mcpu= is used with "cortex-m33", "cortex-m35p", "cortex-m52", "cortex-m55", "cortex-m85", or "star-mc1". You can use -mno-fix-cmse-cve-2021-35465 to disable the mitigation ๐Ÿซถ
  • -mstack-protector-guard=guard, -mstack-protector-guard-offset=offset โ€” generates stack protection code using a canary at guard ๐Ÿ›ก๏ธ Supported locations are global for a global canary, or tls for a canary accessible via the TLS register. The option -mstack-protector-guard-offset= is for use with -fstack-protector-guard=tls, and it's not for use in user-land code ๐Ÿ™…โ€โ™€๏ธ๐Ÿ’–

-mfdpic -mno-fdpic

Okay so -mfdpic switches on the FDPIC ABI, which uses 64-bit function descriptors to represent pointers to functions ๐Ÿฆ‹. When the compiler is configured for arm-*-uclinuxfdpiceabi targets, this is on by default and implies -fPIE if you haven't given any PIC/PIE-related options. On other targets it only turns on the FDPIC-specific code generation features, so you'll need to pass the PIC/PIE options yourself as needed ๐Ÿ’….

Static linking is NOT supported with FDPIC ๐Ÿ’€. The program still involves the dynamic linker when it self-relocates. If you're okay with that behavior anyway, use -static and -Wl,-dynamic-linker options.

The opposite, -mno-fdpic, is useful (and required) when you're building the Linux kernel with the same arm-*-uclinuxfdpiceabi toolchain you use for your userland programs ๐ŸŒธ.

-mbranch-protection=none|standard|pac-ret[+leaf][+bti]|bti[+pac-ret[+leaf]]

Enable branch protection features, but only on armv8.1-m.main โœจ. Here's the breakdown:

  • none โ€” no branch protection or return address signing, just plain code ๐Ÿ“.
  • standard[+leaf] โ€” all branch protection features turned on at their standard level ๐Ÿ”ฅ.
  • pac-ret[+leaf] โ€” return address signing at its standard level, which means signing every function that saves the return address to memory ๐Ÿ”.
  • leaf โ€” when return address signing is on, this also signs leaf functions, even the ones that never write the return address to memory ๐Ÿƒ.
  • +bti โ€” adds landing-pad instructions at the permitted targets of indirect branch instructions ๐Ÿงš.

If the +pacbti architecture extension isn't enabled, all branch protection and return address signing operations are restricted to the instructions defined in the architectural-NOP space. The generated code stays backwards-compatible with earlier versions of the architecture, but the extra security can still kick in at run time on processors that support the PACBTI extension ๐Ÿ’œ.

Branch target enforcement with BTI can only be enabled at runtime if all the code in the application was compiled with at least -mbranch-protection=bti ๐Ÿ™ˆ.

Anything other than none is only supported on armv8-m.main or later ๐Ÿ’€.

The default is to generate code without branch protection or return address signing ๐ŸŒˆ.


โœจ AVR Options โœจ

These options are defined for AVR implementations ๐Ÿงš.

-mmcu=mcu

Specify the AVR instruction set architecture (ISA) or device type. The default is avr2 ๐Ÿ’–.

Here are the AVR devices and ISAs that are supported. Heads up bestie: complete device support needs a startup code file crtmcu.o, a device header avr/io*.h, a device library libmcu.a, and a device-specs file specs-mcu. Only the last one is provided by the compiler, and only for the mcus listed below. The rest comes from AVR-LibC (https://github.com/avrdudes/avr-libc/) or from atpack files (https://gcc.gnu.org/wiki/avr-gcc#atpack) that the hardware manufacturer hands out ๐Ÿ“ฆ. For the spec files, check out https://gcc.gnu.org/wiki/avr-gcc#spec-files โœจ.

  • avr2 โ€” "Classic" devices with up to 8 KiB of program memory ๐ŸŽ€. mcu = attiny22, attiny26, at90s2313, at90s2323, at90s2333, at90s2343, at90s4414, at90s4433, at90s4434, at90c8534, at90s8515, at90s8535.
  • avr25 โ€” "Classic" devices with up to 8 KiB of program memory and the MOVW instruction ๐Ÿฆ‹. mcu = attiny13, attiny13a, attiny24, attiny24a, attiny25, attiny261, attiny261a, attiny2313, attiny2313a, attiny43u, attiny44, attiny44a, attiny45, attiny48, attiny441, attiny461, attiny461a, attiny4313, attiny84, attiny84a, attiny85, attiny87, attiny88, attiny828, attiny841, attiny861, attiny861a, ata5272, ata6616c, at86rf401.
  • avr3 โ€” "Classic" devices with 16 KiB up to 64 KiB of program memory ๐ŸŒธ. mcu = at76c711, at43usb355.
  • avr31 โ€” "Classic" devices with 128 KiB of program memory ๐Ÿ’…. mcu = atmega103, at43usb320.
  • avr35 โ€” "Classic" devices with 16 KiB up to 64 KiB of program memory and the MOVW instruction ๐Ÿซถ. mcu = attiny167, attiny1634, atmega8u2, atmega16u2, atmega32u2, ata5505, ata6617c, ata664251, at90usb82, at90usb162.
  • avr4 โ€” "Enhanced" devices with up to 8 KiB of program memory โญ. mcu = atmega48, atmega48a, atmega48p, atmega48pa, atmega48pb, atmega8, atmega8a, atmega8hva, atmega88, atmega88a, atmega88p, atmega88pa, atmega88pb, atmega8515, atmega8535, ata5795, ata6285, ata6286, ata6289, ata6612c, at90pwm1, at90pwm2, at90pwm2b, at90pwm3, at90pwm3b, at90pwm81.
  • avr5 โ€” "Enhanced" devices with 16 KiB up to 64 KiB of program memory ๐Ÿ‘‘. This is the big one, so buckle up ๐Ÿ˜ญ. mcu = atmega16, atmega16a, atmega16hva, atmega16hva2, atmega16hvb, atmega16hvbrevb, atmega16m1, atmega16u4, atmega161, atmega162, atmega163, atmega164a, atmega164p, atmega164pa, atmega165, atmega165a, atmega165p, atmega165pa, atmega168, atmega168a, atmega168p, atmega168pa, atmega168pb, atmega169, atmega169a, atmega169p, atmega169pa, atmega32, atmega32a, atmega32c1, atmega32hvb, atmega32hvbrevb, atmega32m1, atmega32u4, atmega32u6, atmega323, atmega324a, atmega324p, atmega324pa, atmega324pb, atmega325, atmega325a, atmega325p, atmega325pa, atmega328, atmega328p, atmega328pb, atmega329, atmega329a, atmega329p, atmega329pa, atmega3250, atmega3250a, atmega3250p, atmega3250pa, atmega3290, atmega3290a, atmega3290p, atmega3290pa, atmega406, atmega64, atmega64a, atmega64c1, atmega64hve, atmega64hve2, atmega64m1, atmega64rfr2, atmega640, atmega644, atmega644a, atmega644p, atmega644pa, atmega644rfr2, atmega645, atmega645a, atmega645p, atmega649, atmega649a, atmega649p, atmega6450, atmega6450a, atmega6450p, atmega6490, atmega6490a, atmega6490p, ata5790, ata5790n, ata5791, ata6613c, ata6614q, ata5782, ata5831, ata8210, ata8510, ata5787, ata5835, ata5700m322, ata5702m322, at90pwm161, at90pwm216, at90pwm316, at90can32, at90can64, at90scr100, at90usb646, at90usb647, at94k, m3000.
  • avr51 โ€” "Enhanced" devices with 128 KiB of program memory ๐Ÿ”ฅ. mcu = atmega128, atmega128a, atmega128rfa1, atmega128rfr2, atmega1280, atmega1281, atmega1284, atmega1284p, atmega1284rfr2, at90can128, at90usb1286, at90usb1287.
  • avr6 โ€” "Enhanced" devices with a 3-byte PC, so more than 128 KiB of program memory ๐Ÿš€. mcu = atmega256rfr2, atmega2560, atmega2561, atmega2564rfr2.
  • avrxmega2 โ€” "XMEGA" devices with more than 8 KiB and up to 64 KiB of program memory ๐ŸŒˆ. mcu = atxmega8e5, atxmega16a4, atxmega16a4u, atxmega16c4, atxmega16d4, atxmega16e5, atxmega32a4, atxmega32a4u, atxmega32c3, atxmega32c4, atxmega32d3, atxmega32d4, atxmega32e5, avr64da28, avr64da28s, avr64da32, avr64da32s, avr64da48, avr64da48s, avr64da64, avr64da64s, avr64db28, avr64db32, avr64db48, avr64db64, avr64dd14, avr64dd20, avr64dd28, avr64dd32, avr64du28, avr64du32, avr64ea28, avr64ea32, avr64ea48, avr64sd28, avr64sd32, avr64sd48.
  • avrxmega3 โ€” "XMEGA" devices with up to 64 KiB of combined program memory and RAM, where the program memory is visible in the RAM address space ๐Ÿงš. mcu = attiny202, attiny204, attiny212, attiny214, attiny402, attiny404, attiny406, attiny412, attiny414, attiny416, attiny416auto, attiny417, attiny424, attiny426, attiny427, attiny804, attiny806, attiny807, attiny814, attiny816, attiny817, attiny824, attiny826, attiny827, attiny1604, attiny1606, attiny1607, attiny1614, attiny1616, attiny1617, attiny1624, attiny1626, attiny1627, attiny3214, attiny3216, attiny3217, attiny3224, attiny3226, attiny3227, atmega808, atmega809, atmega1608, atmega1609, atmega3208, atmega3209, atmega4808, atmega4809, avr16dd14, avr16dd20, avr16dd28, avr16dd32, avr16du14, avr16du20, avr16du28, avr16du32, avr16ea28, avr16ea32, avr16ea48, avr16eb14, avr16eb20, avr16eb28, avr16eb32, avr32da28, avr32da28s, avr32da32, avr32da32s, avr32da48, avr32da48s, avr32db28, avr32db32, avr32db48, avr32dd14, avr32dd20, avr32dd28, avr32dd32, avr32du14, avr32du20, avr32du28, avr32du32, avr32ea28, avr32ea32, avr32ea48, avr32sd20, avr32sd28, avr32sd32.
  • avrxmega4 โ€” "XMEGA" devices with more than 64 KiB and up to 128 KiB of program memory ๐Ÿ’–. mcu = atxmega64a3, atxmega64a3u, atxmega64a4u, atxmega64b1, atxmega64b3, atxmega64c3, atxmega64d3, atxmega64d4, avr128da28, avr128da28s, avr128da32, avr128da32s, avr128da48, avr128da48s, avr128da64, avr128da64s, avr128db28, avr128db32, avr128db48, avr128db64.
  • avrxmega5 โ€” "XMEGA" devices with more than 64 KiB and up to 128 KiB of program memory, plus more than 64 KiB of RAM ๐ŸŽ€. mcu = atxmega64a1, atxmega64a1u.
  • avrxmega6 โ€” "XMEGA" devices with more than 128 KiB of program memory ๐Ÿฆ‹. mcu = atxmega128a3, atxmega128a3u, atxmega128b1, atxmega128b3, atxmega128c3, atxmega128d3, atxmega128d4, atxmega192a3, atxmega192a3u, atxmega192c3, atxmega192d3, atxmega256a3, atxmega256a3b, atxmega256a3bu, atxmega256a3u, atxmega256c3, atxmega256d3, atxmega384c3, atxmega384d3.
  • avrxmega7 โ€” "XMEGA" devices with more than 128 KiB of program memory and more than 64 KiB of RAM ๐Ÿ‘‘. mcu = atxmega128a1, atxmega128a1u, atxmega128a4u.

okay bestie, the tiny chips get their own little squad ๐ŸŒธ

  • avrtiny โ€” the "Reduced Tiny" crew ๐Ÿงš These tiny core devices only have 16 general purpose registers and 512 B up to 4 KiB of program memory. mcu = "attiny4", "attiny5", "attiny9", "attiny10", "attiny102", "attiny104", "attiny20", "attiny40".
  • avr1 โ€” this ISA is baked into the minimal AVR core, and it's only supported for the assembler ๐Ÿ™ˆ mcu = "attiny11", "attiny12", "attiny15", "attiny28", "at90s1200".

  • -mabsdata โ€” okay so this one assumes all data in static storage can be reached with LDS / STS instructions ๐Ÿ’… It only does anything on reduced Tiny devices like ATtiny40. Also see the "absdata" AVR Variable Attributes, variable attribute โœจ
  • -mcvt โ€” compact vector table energy ๐Ÿฆ‹ Some devices support a CVT with only four entries: 0=Reset, 1=NMI, 2=Prio1 IRQ, 3=Prio0 IRQs. With this on, startup code gets linked from "crtmcu-cvt.o" instead of the usual "crtmcu.o". Besides the tiny table, the startup code also sets the bit CPUINT_CTRLA.CPUINT_CVT, which switches the CVT on for the device.

If you don't want the startup code touching CPUINT_CTRLA.CPUINT_CVT, you can satisfy the symbol "__init_cvt" so that code isn't pulled in from "libmcu.a" anymore. For example, link with -Wl,--defsym,__init_cvt=0 ๐Ÿซถ

The CVT startup code is available since AVR-LibC v2.3 ("https://github.com/avrdudes/avr-libc/issues/1010").

  • -mdouble=bits, -mlong-double=bits โ€” these set the size (in bits) of the double or long double type, respectively ๐ŸŒˆ Valid values for bits are 32 and 64. Whether a value is allowed depends on the --with-double= and --with-long-double= configure options ("https://gcc.gnu.org/install/configure.html#avr"), and the same goes for the default values of these options.
  • -mgas-isr-prologues โ€” ISRs are allowed to use the "__gcc_isr" pseudo instruction that GNU Binutils supports ๐Ÿ”ฅ If this option is on, you can still turn the feature off for individual ISRs with the AVR Function Attributes, "no_gccisr" function attribute. This feature is on by default if optimization is on (but not with -Og, @pxref{Optimize Options}), and only if GNU Binutils support PR21683 ("https://sourceware.org/PR21683") โœจ
  • -mint8 โ€” this makes int an 8-bit integer ๐Ÿ’€ All the types shrink: a char is 1 byte, an int is 1 byte, a long is 2 bytes, and a long long is 4 bytes.
Heads up: this option does not conform to the C standards! It does give you smaller code size though.
  • -mmain-is-OS_task โ€” don't save registers in main ๐Ÿ‘‘ It works the same as attaching the AVR Function Attributes, "OS_task" attribute to main. It's on by default when optimization is on.
  • -mno-call-main โ€” don't run main through the usual call-and-exit dance ๐Ÿงšโ€โ™€๏ธ
        XCALL  main
        XJMP   exit

Instead, main gets put in the section ".init9" ("https://avrdudes.github.io/avr-libc/avr-libc-user-manual/mem_sections.html#sec_dot_init"), so no call is needed at all. By turning this on you're promising that main will never return ๐Ÿ™ˆ

This one is perfect for devices with very limited resources, since it saves a few bytes of code and stack space. It works as expected since AVR-LibC v2.3 ("https://github.com/avrdudes/avr-libc/issues/1012"). With older versions there's no performance gain.

  • -mno-interrupts โ€” the generated code won't be compatible with hardware interrupts ๐Ÿ˜ญ The upside is that the code size is smaller.
  • -mrelax โ€” this tries to swap CALL (or JMP) for the shorter RCALL (or RJMP) when it's allowed โœจ Setting -mrelax just adds --mlink-relax to the assembler's command line and --relax to the linker's command line.

The linker does the jump relaxing, because jump offsets aren't known until the code is located. So the assembler code the compiler spits out is the same, but the instructions in the executable might look different from it ๐Ÿ’–

Relaxing must be turned on if linker stubs are needed. See the section on "EIND" and linker stubs below.

  • -mrodata-in-ram, -mno-rodata-in-ram โ€” these decide whether the ".rodata" sections for read-only data live in RAM or in program memory ๐ŸŽ€ For most devices there's no real choice, so this option basically acts like an assertion.

Since v14, and for the AVR64* and AVR128* devices, ".rodata" lives in flash memory by default, as long as the required GNU Binutils support (PR31124 ("https://sourceware.org/PR31124")) is around. In that case, -mrodata-in-ram can bring back the old layout with ".rodata" in RAM.

  • -mtiny-stack โ€” only change the lower 8 bits of the stack pointer ๐Ÿ“
  • -mfract-convert-truncate โ€” lets you use truncation instead of rounding towards zero for fractional fixed-point types ๐Ÿฆ‹
  • -nodevicelib โ€” don't link against AVR-LibC's device specific library "libmcu.a" ๐Ÿฅบ

Heads up: since AVR-LibC v2.3, that library holds code that's essential for your program to work properly! It contains parts of the startup code, like "__init_sp" ("https://github.com/avrdudes/avr-libc/issues/1011"), which sets up the stack pointer with the symbol "__stack". It also has "__init_cvt" ("https://github.com/avrdudes/avr-libc/issues/1010"), which sets up the hardware for a compact vector table with -mcvt, "__call_main" ("https://github.com/avrdudes/avr-libc/issues/1012"), which calls "main" and "exit", and "__do_flmap_init" ("https://github.com/avrdudes/avr-libc/issues/931"), which sets up FLMAP according to the symbol "__flmap".

  • -nodevicespecs โ€” don't add -specs=device-specs/specs-mcu to the compiler driver's command line ๐Ÿง  Now you're in charge of giving the subprocesses (compiler proper, assembler and linker) the right command line options. That means you have to supply your own device specs file with -specs=path-to-specs-file. Once you do that, there's no more need for -mmcu=mcu.

This option can also replace the older way of making custom device-specs files. That one needed -B some-path to point to a directory with a folder named "device-specs", and inside that folder a specs file named "specs-mcu", where mcu was whatever you set with -mmcu=mcu.

  • -Waddr-space-convert โ€” warns you about conversions between address spaces when the resulting address space isn't contained in the incoming one ๐Ÿ‘€
  • -Wmisspelled-isr โ€” warns if an ISR is misspelled, meaning it's missing the __vector prefix. It's enabled by default โœจ

โœจ AVR Optimization Options โœจ

These are the pure optimization options ๐Ÿ’… The options -mgas-isr-prologues, -mmain-is-OS_task, -mno-call-main and -mrelax from above are only almost optimization options, because there are rare moments where their different code generation actually matters.

  • -maccumulate-args โ€” collects outgoing function arguments and grabs/releases the stack space for them just once, in the function prologue/epilogue ๐ŸŒธ Without this option, outgoing arguments get pushed before a call and popped afterwards. See also the -fdefer-pop Optimize Options, optimization option.

Popping arguments after a call can be expensive on AVR. Accumulating the stack space can lead to smaller executables, since arguments don't need to be removed from the stack after each call.

This can shrink code for functions that make several calls to functions that take their arguments on the stack, like printf-like functions ๐Ÿ–จ๏ธ

  • -mbranch-cost=cost โ€” sets the branch costs for conditional branch instructions to cost. Reasonable values for cost are small, non-negative integers. The default branch cost is 0 ๐Ÿ’œ
  • -mcall-prologues โ€” function prologues/epilogues get expanded as calls to appropriate subroutines ๐Ÿงš Code size is smaller.
  • -mfuse-add, -mno-fuse-add, -mfuse-add=level โ€” optimizes indirect memory accesses on reduced Tiny devices ๐Ÿฆ‹ The default uses level=1 for the -Og and -O1 optimizations, and level=2 for higher optimizations. Valid values for level are 0, 1 and 2.
  • -mfuse-move, -mno-fuse-move, -mfuse-move=level โ€” runs a post reload optimization pass that tries to fuse move instructions and to split multi-byte instructions into 8-bit operations ๐Ÿ”ฅ The default uses level=3 for -O1 optimization, and level=23 for higher optimizations. Valid values for level are in the range 0 ... 23, which is a 3:2:2:2 mixed radix value. Each digit controls some aspect of the optimization.
  • -mfuse-move2 โ€” runs a post combine optimization pass that tries to fuse move instructions ๐Ÿ’–
  • -mstrict-X โ€” uses the address register X the way the hardware intends ๐ŸŒˆ That means X is only used for indirect, post-increment or pre-decrement addressing.

Without this option, the X register can be used the same way as Y or Z, which then gets emulated with extra instructions. For example, loading a value with X+const addressing (with a small non-negative const < 64) into a register Rn gets done like this:

        adiw r26, const   ; X += const
        ld   <Rn>, X        ; <Rn> = *X
        sbiw r26, const   ; X -= const
  • -msplit-bit-shift โ€” splits multi-byte shifts with a constant offset into a shift with a byte offset plus a residual shift with a non-byte offset โœ‚๏ธ This is turned on by default for -O2 and higher, including -Os but excluding -Oz. Splitting shifts with a constant offset that's a multiple of 8 is controlled by -mfuse-move.
  • -msplit-ldst โ€” splits multi-byte loads and stores into several byte loads and stores ๐Ÿ“ It's turned on by default for -O2 and higher.
  • -muse-nonzero-bits โ€” enables optimizations that are only possible when some bits in a register are always zero ๐Ÿง  It's turned on by default for -O2 and higher.

โœจ "EIND" and Devices With More Than 128 Ki Bytes of Flash โœจ

Pointers in the implementation are 16 bits wide ๐ŸŽ€ The address of a function or label is represented as a word address, so indirect jumps and calls can target any code address in the range of 64 Ki words ๐ŸŒธ

okay so, bestie, this part is about EIND ๐Ÿงš, a special function register that's basically the boss of big AVR chips. Devices with more than 128 Ki bytes of program memory need it for indirect jumps ๐Ÿ’œ

The register is called EIND, and it supplies the most significant part of the target address whenever the EICALL or EIJMP instructions get used ๐ŸŒธ

Here's the tea on indirect jumps and calls on these chips. The compiler handles them like this, and there are some limits you need to know about ๐Ÿซถ

  • The compiler never sets EIND. It doesn't touch it at all ๐Ÿ™ˆ
  • The compiler uses EIND implicitly in EICALL/EIJMP instructions, or it might read EIND directly so it can fake an indirect call or jump with a RET instruction โœจ
  • The compiler assumes EIND never changes during the startup code or the application. Like, not once. EIND is not saved or restored in function or interrupt service routine prologues or epilogues ๐Ÿ˜ญ
  • The linker makes stubs for indirect calls to functions and for computed goto. Stubs are jump pads, sometimes called trampolines ๐Ÿฆ‹ So the indirect call or jump lands on a stub, and the stub holds a direct jump to where you actually wanted to go ๐Ÿ’–
  • Linker relaxation has to be on so the linker builds the stubs correctly in every situation. Check out the compiler option -mrelax and the linker option --relax. Heads up: there are corner cases where the linker is supposed to make stubs but just aborts without relaxation, and it won't give you a helpful error message ๐Ÿ’€
  • The default linker script is set up for code with EIND = 0. If your code is supposed to work with EIND != 0, you need a custom linker script ๐Ÿ“ It has to put the sections whose names start with .trampolines into the segment that EIND points to ๐Ÿ‘‘
  • The startup code from libgcc never sets EIND. Also, startup code is a blend of libgcc and AVR-LibC, so for how AVR-LibC affects EIND, go read the AVR-LibC user manual at https://avrdudes.github.io/avr-libc/avr-libc-user-manual/ ๐Ÿ“–
  • Your own startup code is allowed to set up EIND early. For example, you can do it with initialization code in the section .init3. That code runs before the general startup code that sets up RAM and calls constructors, but after the bit of AVR-LibC startup code that points EIND at the segment holding the vector table ๐ŸŽ€

Here's an example of that .init3 trick โœจ

#include <avr/io.h>

static void
__attribute__((section(".init3"),naked,used,no_instrument_function))
init3_set_eind (void)
{
  __asm volatile ("ldi r24,pm_hh8(__trampolines_start)\n\t"
                  "out %i0,r24" :: "n" (&EIND) : "r24","memory");
}

The __trampolines_start symbol is defined in the linker script ๐Ÿง 

  • Stubs get generated automatically by the linker only when both of these conditions are met ๐Ÿ’…
    • The address of a label is taken with the gs modifier, which is short for generate stubs. It looks like this:
LDI r24, lo8(gs(<func>))
LDI r25, hi8(gs(<func>))
  • The final location of that label is in a code segment that's outside the segment where the stubs live ๐Ÿ˜ค
  • The compiler slaps gs modifiers on code labels in these situations ๐ŸŒˆ
    • Taking the address of a function or code label
    • Computed goto
    • When a prologue-save function is used, see the -mcall-prologues command-line option
    • Switch/case dispatch tables. If you don't want those dispatch tables, you can use the -fno-jump-tables command-line option
    • C and C++ constructors and destructors that get called during startup or shutdown
    • If the tools run into a gs() modifier like the one described above
  • Jumping to non-symbolic addresses like this is NOT supported ๐Ÿšซ
int main (void)
{
    /* Call function at word address 0x2 */
    return ((int(*)(void)) 0x2)();
}

Instead, you need to set up a stub, meaning you call the function through a symbol. In the example below, that symbol is func_4 ๐Ÿฆ‹

int main (void)
{
    extern int func_4 (void);

    /* Call function at byte address 0x4 */
    return func_4();
}

Then link the application with -Wl,--defsym,func_4=0x4. Or you can define func_4 in the linker script instead ๐Ÿซถ

๐Ÿ’Ž Handling of the RAMPD, RAMPX, RAMPY and RAMPZ Special Function Registers ๐Ÿ’Ž

Some AVR devices have more memory than the 64 KiB that 16-bit pointers can reach. To get at memory locations past that 64 KiB range, the content of a RAMP register becomes the high part of the address. The X, Y and Z address registers get glued to RAMPX, RAMPY and RAMPZ respectively to make a wide address. RAMPD does the same job with direct addressing ๐ŸŒธ

  • The startup code sets all the RAMP special function registers to zero ๐Ÿ“
  • If a named address space other than generic or __flash is used (that's the AVR Named Address Spaces stuff), then RAMPZ gets set as needed before the operation โœจ
  • If the device supports more than 64 KiB of RAM and the compiler has to change RAMPZ to pull off an operation, then RAMPZ gets reset to zero once the operation is done ๐Ÿ”„
  • If the device has a specific RAMP register, the ISR prologue and epilogue save and restore that SFR, and they set it to zero in case the ISR code might use it, even implicitly ๐Ÿ›ก๏ธ
  • GCC does not support RAM larger than 64 KiB for AVR targets ๐Ÿ˜ฌ If you use inline assembler to read from locations outside the 16-bit address range and you change one of the RAMP registers, you must reset it to zero after the access.

๐ŸŽ€ AVR Built-in Macros ๐ŸŽ€

GCC defines a bunch of built-in macros so your code can check whether features are there or not ๐Ÿ’– Almost all of the following built-in macros are worked out from device capabilities, so they get triggered by the -mmcu= command-line option ๐Ÿงš

For even more AVR-specific built-in macros, check out AVR Named Address Spaces and AVR Built-in Functions ๐Ÿ“–

  • __AVR_ARCH__ โ€” This one is a built-in macro that resolves to a decimal number identifying the architecture. It depends on the -mmcu=mcu option. The possible values are:
2, 25, 3, 31, 35, 4, 5, 51, 6

Those match mcu="avr2", "avr25", "avr3", "avr31", "avr35", "avr4", "avr5", "avr51" and "avr6" respectively. The values below are for mcu="avrtiny", "avrxmega2", "avrxmega3", "avrxmega4", "avrxmega5", "avrxmega6" and "avrxmega7", in order ๐Ÿ‘‘

100, 102, 103, 104, 105, 106, 107

If mcu names a device, this built-in macro gets set accordingly. For example, -mmcu=atmega8 sets the macro to 4 โœจ

  • __AVR_Device__ โ€” Setting -mmcu=device defines this built-in macro, which reflects the device's name. For example, -mmcu=atmega8 defines __AVR_ATmega8__, and -mmcu=attiny261a defines __AVR_ATtiny261A__, and so on ๐Ÿ’…

The built-in macro names follow the scheme __AVR_Device__, where Device is the device name as it appears in the AVR user manual. The difference between Device in the built-in macro and device in -mmcu=device is that the latter is always lowercase ๐ŸŒˆ

If device is not an actual device but just a core architecture like avr51, this macro is not defined ๐Ÿ™ˆ

  • __AVR_DEVICE_NAME__ โ€” Setting -mmcu=device defines this one to the device's name. For example, -mmcu=atmega8 sets it to "atmega8" ๐Ÿฆ‹

If device is just a core architecture like avr51 and not a real device, this macro is not defined ๐Ÿ’€

  • __AVR_CVT__ โ€” The code is being compiled with the -mcvt option, so it uses a compact vector table ๐Ÿซถ
  • __AVR_XMEGA__ โ€” The device or architecture belongs to the XMEGA family ๐Ÿ”ฅ
  • __AVR_HAVE_ADIW__ โ€” The device has the ADIW and SBIW instructions ๐Ÿ’œ
  • __AVR_HAVE_ELPM__ โ€” The device has the ELPM instruction ๐ŸŒธ
  • __AVR_HAVE_ELPMX__ โ€” The device has the ELPM Rn,Z and ELPM Rn,Z+ instructions ๐Ÿงš
  • __AVR_HAVE_LPMX__ โ€” The device has the LPM Rn,Z and LPM Rn,Z+ instructions โœจ
  • __AVR_HAVE_MOVW__ โ€” The device has the MOVW instruction, which does 16-bit register-to-register moves ๐Ÿ“
  • __AVR_HAVE_MUL__ โ€” The device has a hardware multiplier ๐Ÿ”ฅ
  • __AVR_HAVE_JMP_CALL__ โ€” The device has the JMP and CALL instructions. This is the case for devices with more than 8 KiB of program memory ๐Ÿ‘‘
  • __AVR_HAVE_EIJMP_EICALL__ and __AVR_3_BYTE_PC__ โ€” The device has the EIJMP and EICALL instructions. That's the case for devices with more than 128 KiB of program memory. It also means the program counter (PC) is 3 bytes wide ๐ŸŒˆ
  • __AVR_2_BYTE_PC__ โ€” The program counter (PC) is 2 bytes wide. That's the case for devices with up to 128 KiB of program memory ๐ŸŽ€
  • __AVR_HAVE_8BIT_SP__ and __AVR_HAVE_16BIT_SP__ โ€” The compiler treats the stack pointer (SP) register as an 8-bit or 16-bit register respectively. Defining these macros is affected by -mtiny-stack ๐Ÿ’…
  • __AVR_HAVE_SPH__ and __AVR_SP8__ โ€” The device has the SPH (high part of stack pointer) special function register, or it has an 8-bit stack pointer, respectively. Defining these macros is affected by -mmcu=, and for -mmcu=avr2 and -mmcu=avr25 it's also affected by -msp8 ๐Ÿฆ‹
  • __AVR_HAVE_RAMPD__, __AVR_HAVE_RAMPX__, __AVR_HAVE_RAMPY__ and __AVR_HAVE_RAMPZ__ โ€” The device has the RAMPD, RAMPX, RAMPY or RAMPZ special function register, respectively ๐Ÿง 
  • __NO_INTERRUPTS__ โ€” This macro reflects the -mno-interrupts command-line option ๐Ÿ™ˆ
  • __AVR_ERRATA_SKIP__ and __AVR_ERRATA_SKIP_JMP_CALL__ โ€” Some AVR devices (AT90S8515 and ATmega103) must not skip 32-bit instructions because of a hardware erratum ๐Ÿ˜ญ The skip instructions are SBRS, SBRC, SBIS, SBIC and CPSE. The second macro only gets defined if __AVR_HAVE_JMP_CALL__ is also set โš ๏ธ
  • __AVR_ISA_RMW__ โ€” The device has Read-Modify-Write instructions (XCH, LAC, LAS and LAT) ๐Ÿ”ฅ
  • __AVR_SFR_OFFSET__=offset โ€” Instructions that can address I/O special function registers directly, like IN, OUT, SBI, and so on, may use a different address than an instruction accessing RAM, like LD or STS. This offset depends on the device architecture, and you have to subtract it from the RAM address to get the matching I/O address ๐ŸŒธ
  • __AVR_SHORT_CALLS__ โ€” The -mshort-calls command-line option is set ๐Ÿ’–

"__AVR_PM_BASE_ADDRESS__=addr"

okay so some devices can read flash memory with the LD* instructions ๐Ÿงš the flash shows up in the data address space at an offset of __AVR_PM_BASE_ADDRESS__. if that macro isn't defined, this feature just isn't available. if it is defined, the address space is linear and you don't need to shove .rodata into RAM ๐Ÿ’– the default linker description file handles that, and it's currently available for avrtiny and avrxmega3. even better, you don't need address spaces like __flash or stuff like the progmem attribute and pgm_read_* โœจ

"__AVR_HAVE_FLMAP__"

this one is defined only when ALL of these conditions are met ๐Ÿ‘‘

  • the device has the NVMCTRL_CTRLB.FLMAP bitfield. this applies to the AVR64* and AVR128* devices ๐ŸŒˆ
  • it's not known at assembler-time which emulation will be used. ๐Ÿซถ

and it implies the compiler was configured with GNU Binutils that implement PR31124 (https://sourceware.org/PR31124) ๐Ÿ’…

"__AVR_RODATA_IN_RAM__"

this macro is undefined when the code is compiled for a core architecture ๐Ÿ™ˆ

when the code is compiled for a device, it's defined to 1 if the .rodata sections for read-only data live in RAM, and defined to 0 otherwise ๐Ÿงš

"__WITH_AVRLIBC__"

the compiler is configured to be used together with AVR-LibC ๐Ÿ’œ see the --with-avrlibc configure option.

"__HAVE_SIGNAL_N__"

the compiler supports the signal(num) and interrupt(num) AVR Function Attributes, with an argument num that specifies the number of the interrupt service routine โญ

"__HAVE_DOUBLE_MULTILIB__"

defined if -mdouble= acts as a multilib option ๐ŸŽ€

"__HAVE_DOUBLE32__" "__HAVE_DOUBLE64__"

defined if the compiler supports 32-bit double (respectively 64-bit double) ๐Ÿฆ‹ the actual layout is specified by option -mdouble=.

"__DEFAULT_DOUBLE__"

the size in bits of double if -mdouble= is not set ๐ŸŒธ if you want to test the layout of double in a program, use the built-in macro __SIZEOF_DOUBLE__ ๐Ÿ’…

"__HAVE_LONG_DOUBLE32__" "__HAVE_LONG_DOUBLE64__" "__HAVE_LONG_DOUBLE_MULTILIB__" "__DEFAULT_LONG_DOUBLE__"

same vibe as above, but for long double instead of double ๐Ÿ“

"__WITH_DOUBLE_COMPARISON__"

this one reflects the --with-double-comparison={tristate|bool|libf7} configure option (https://gcc.gnu.org/install/configure.html#avr) and is defined to 2 or 3 โœจ

"__WITH_LIBF7_LIBGCC__" "__WITH_LIBF7_MATH__" "__WITH_LIBF7_MATH_SYMBOLS__"

these reflect the --with-libf7={libgcc|math|math-symbols} configure option (https://gcc.gnu.org/install/configure.html#avr) ๐Ÿซง


โœจ AVR Internal Options โœจ

okay bestie, these are used internally by the compiler and to chat with device specs files and the compiler proper ๐Ÿซถ you don't need to set them by hand. they are not optimization options, fr ๐Ÿ’€

using these options the wrong way may lead to sub-optimal or wrong code. they're documented for completeness, and so you can better understand device specs (https://gcc.gnu.org/wiki/avr-gcc#spec-files) files ๐Ÿ˜ญ
  • -mn-flash=num โ€” assumes the flash memory has a size of num times 64 KiB ๐ŸŒˆ this decides which __flashN address spaces are available.
  • -mflmap โ€” the device has the FLMAP bit field sitting in the special function register NVMCTRL_CTRLB ๐Ÿ’œ
  • -mrmw โ€” assumes the device supports the Read-Modify-Write instructions XCH, LAC, LAS and LAT ๐Ÿ”ฅ
  • -mshort-calls โ€” assumes RJMP and RCALL can target the whole program memory ๐Ÿฆ‹ this is used for multilib generation and selection for the devices from architecture avrxmega3.
  • -mskip-bug โ€” generates code without skips (CPSE, SBRS, SBRC, SBIS, SBIC) over 32-bit instructions ๐Ÿ™ˆ
  • -msp8 โ€” treats the stack pointer register as an 8-bit register, i.e. assumes the high byte of the stack pointer is zero ๐ŸŒธ the compiler uses this to select and build multilibs for architectures avr2 and avr25, since those mix devices with and without SPH ๐Ÿ“

๐ŸŒˆ Blackfin Options ๐ŸŒˆ

  • -mcpu=cpu[-sirevision] โ€” picks the target Blackfin processor ๐Ÿ‘‘ right now cpu can be one of bf512, bf514, bf516, bf518, bf522, bf523, bf524, bf525, bf526, bf527, bf531, bf532, bf533, bf534, bf536, bf537, bf538, bf539, bf542, bf544, bf547, bf548, bf549, bf542m, bf544m, bf547m, bf548m, bf549m, bf561, bf592.

the optional sirevision is the silicon revision of the target Blackfin processor ๐Ÿ’… any workarounds for that revision get enabled. if sirevision is none, no workarounds are enabled. if it's any, all workarounds for the targeted processor are enabled. the __SILICON_REVISION__ macro is defined to two hexadecimal digits representing the major and minor numbers in the silicon revision โœจ if sirevision is none, __SILICON_REVISION__ is not defined at all. if it's any, __SILICON_REVISION__ is defined to be 0xffff. if you skip this optional sirevision, GCC assumes the latest known silicon revision of the targeted Blackfin processor ๐Ÿซถ

GCC defines a preprocessor macro for the specified cpu ๐Ÿงš for the bfin-elf toolchain, this option causes the hardware BSP provided by libgloss to be linked in if -msim is not given.

without this option, bf532 is used as the processor by default ๐ŸŽ€

note that support for bf561 is incomplete ๐Ÿ˜ญ for bf561, only the preprocessor macro is defined.

  • -msim โ€” specifies that the program will be run on the simulator ๐Ÿ”ฎ this causes the simulator BSP provided by libgloss to be linked in. it only has effect for the bfin-elf toolchain. certain other options, such as -mid-shared-library and -mfdpic, imply -msim ๐Ÿ’–
  • -momit-leaf-frame-pointer โ€” don't keep the frame pointer in a register for leaf functions ๐Ÿฆ‹ this skips the instructions to save, set up and restore frame pointers and frees up an extra register in leaf functions โœจ
  • -mspecld-anomaly โ€” when enabled, the compiler makes sure the generated code doesn't contain speculative loads after jump instructions ๐Ÿ›ก๏ธ if you use this option, __WORKAROUND_SPECULATIVE_LOADS is defined.
  • -mno-specld-anomaly โ€” don't generate extra code to stop speculative loads from happening ๐Ÿ™…โ€โ™€๏ธ
  • -mcsync-anomaly โ€” when enabled, the compiler makes sure the generated code doesn't contain CSYNC or SSYNC instructions too soon after conditional branches ๐Ÿ’œ if you use this option, __WORKAROUND_SPECULATIVE_SYNCS is defined.
  • -mno-csync-anomaly โ€” don't generate extra code to stop CSYNC or SSYNC instructions from happening too soon after a conditional branch ๐Ÿ™ˆ
  • -mlow64k โ€” when enabled, the compiler is free to take advantage of knowing the entire program fits into the low 64k of memory ๐Ÿ“
  • -mno-low64k โ€” assumes the program is arbitrarily large ๐ŸŒธ this is the default.
  • -mstack-check-l1 โ€” does stack checking using information placed into L1 scratchpad memory by the uClinux kernel ๐Ÿงš
  • -mid-shared-library โ€” generates code that supports shared libraries via the library ID method ๐ŸŽ€ this lets you execute in place and use shared libraries in an environment without virtual memory management. this option implies -fPIC, and with a bfin-elf target it also implies -msim ๐Ÿ’…
  • -mno-id-shared-library โ€” generates code that doesn't assume ID-based shared libraries are being used ๐Ÿ’– this is the default.
  • -mleaf-id-shared-library โ€” generates code that supports shared libraries via the library ID method, but assumes this library or executable won't link against any other ID shared libraries ๐Ÿฆ‹ that lets the compiler use faster code for jumps and calls โญ
  • -mno-leaf-id-shared-library โ€” don't assume the code being compiled won't link against any ID shared libraries ๐Ÿข slower code gets generated for jump and call insns.
  • -mshared-library-id=n โ€” specifies the identification number of the ID-based shared library being compiled ๐Ÿ”ฅ specifying 0 generates more compact code ๐Ÿซถ specifying other values forces the allocation of that number to the current library, but it's no more space- or time-efficient than omitting this option.
  • -msep-data โ€” generates code that lets the data segment live in a different area of memory from the text segment ๐ŸŒˆ this allows execute in place in an environment without virtual memory management, by getting rid of relocations against the text section ๐Ÿงš
  • -mno-sep-data โ€” generates code that assumes the data segment follows the text segment ๐Ÿ’œ this is the default.
  • -mlong-calls, -mno-long-calls โ€” tells the compiler to do function calls by first loading the function's address into a register, and then doing a subroutine call on that register ๐Ÿ“ž you need this switch if the target function lies outside the 24-bit addressing range of the offset-based version of the subroutine call instruction ๐Ÿ˜ญ

this feature is not enabled by default. -mno-long-calls restores the default behavior ๐Ÿ™…โ€โ™€๏ธ also note these switches have no effect on how the compiler handles function calls via function pointers ๐Ÿ’€

  • -mfast-fp โ€” links with the fast floating-point library ๐ŸŽ๏ธ this library relaxes some of the IEEE floating-point standard's rules for checking inputs against Not-a-Number (NAN), in the interest of performance ๐ŸŒธ
  • -minline-plt โ€” enables inlining of PLT entries in function calls to functions that aren't known to bind locally ๐Ÿฆ‹ it has no effect without -mfdpic.
  • -mmulticore โ€” builds a standalone application for multicore Blackfin processors ๐Ÿ‘‘ this makes sure the proper start files and link scripts supporting multicore get used, and defines the macro __BFIN_MULTICORE. it can only be used with -mcpu=bf561[-sirevision] ๐Ÿซง

this option can be combined with -mcorea or -mcoreb, which select the one-application-per-core programming model ๐Ÿ’… without -mcorea or -mcoreb, the single-application/dual-core programming model is used instead. in this model, the main function of Core B should be named coreb_main ๐Ÿงš

if you don't use this option, the single-core application programming model is used ๐ŸŽ€

  • -mcorea โ€” Builds a standalone app for Core A of BF561 when you're doing the one-application-per-core thing. It swaps in the right start files and link scripts for Core A and defines the macro "__BFIN_COREA" for you โœจ. This one only works alongside -mmulticore ๐Ÿซถ.
  • -mcoreb โ€” Same deal, but for Core B of BF561 in the one-application-per-core setup. It uses the Core B start files and link scripts and defines "__BFIN_COREB". When you turn this on, your entry point has to be "coreb_main" instead of "main" ๐Ÿ™ˆ. It also only works with -mmulticore ๐Ÿ’œ.
  • -msdram โ€” Builds a standalone app that runs out of SDRAM ๐Ÿงš. The start files and link scripts put the app in SDRAM and the macro "__BFIN_SDRAM" gets defined. Your loader has to initialize SDRAM before it loads the app, so don't skip that step bestie ๐Ÿ˜ญ.
  • -micplb โ€” Tells GCC to assume ICPLBs are enabled at run time ๐ŸŒธ. That changes how certain anomaly workarounds behave. For Linux targets it assumes ICPLBs are on by default, but for standalone apps the default is off.

๐Ÿฆ‹ C6X Options ๐Ÿฆ‹

  • -march=name โ€” Picks the target architecture, and GCC uses it to decide which instructions it's allowed to emit when it generates assembly ๐Ÿ’…. The names you can use are c62x, c64x, c64x+, c67x, c67x+, and c674x.
  • -mbig-endian โ€” Generates code for a big-endian target ๐Ÿ“.
  • -mlittle-endian โ€” Generates code for a little-endian target. This is the default, so you're already slay ๐Ÿ‘‘.
  • -msim โ€” Picks startup files and a linker script that work with the simulator ๐ŸŽ€.
  • -msdata=default โ€” Small global and static data goes in the ".neardata" section, which B14 points to โœจ. Small uninitialized global and static data goes in ".bss", right next to ".neardata". Small read-only data goes in ".rodata". For the big stuff, the matching sections are ".fardata", ".far", and ".const".
  • -msdata=all โ€” Shoves all data into the small-data sections, not just the tiny stuff, and accesses it relative to the B14 register ๐Ÿ”ฅ.
  • -msdata=none โ€” No small-data sections at all ๐Ÿ’€. Every access uses absolute addresses. All initialized global and static data goes in ".fardata", all uninitialized data goes in ".far", and all constant data goes in ".const".

๐ŸŒˆ CRIS Options ๐ŸŒˆ

These options are made specifically for the CRIS ports, so they only show up in that world ๐Ÿงš.

  • -march=architecture-type, -mcpu=architecture-type โ€” Generates code for the architecture you name โญ. The choices are v3, v8, and v10, which map to ETRAX 4, ETRAX 100, and ETRAX 100 LX. The default is v0.
  • -mtune=architecture-type โ€” Tunes everything that can be tuned in the generated code to the architecture you pick ๐Ÿฆ‹. The ABI and the set of available instructions stay the same though. Your choices are the same as for -march=architecture-type.
  • -mmax-stack-frame=n โ€” Gives you a warning if a function's stack frame goes over n bytes ๐Ÿ˜ญ.
  • -metrax4, -metrax100 โ€” These two are just nicknames: -metrax4 means -march=v3, and -metrax100 means -march=v8 ๐Ÿ’–.
  • -mmul-bug-workaround, -mno-mul-bug-workaround โ€” Works around a bug in the "muls" and "mulu" instructions on the CPU models where it actually applies ๐Ÿ™ˆ. It's off by default.
  • -mpdebug โ€” Turns on CRIS-specific verbose debug info in the assembly code โœจ. It also turns off the #NO_APP formatted-code marker that normally sits at the top of the assembly file for the assembler.
  • -mcc-init โ€” Stops GCC from reusing condition-code results from the previous instruction ๐Ÿ’…. Instead it always emits compare and test instructions before using condition codes.
  • -mno-side-effects โ€” No instructions with side effects in addressing modes, unless it's post-increment ๐Ÿซถ.
  • -mstack-align, -mno-stack-align, -mdata-align, -mno-data-align, -mconst-align, -mno-const-align โ€” The non-no- versions make the stack frame, individual data, and constants line up to the biggest single data access size your chosen CPU model supports ๐ŸŽ€. The no- versions undo that. By default everything is aligned to 32 bits. ABI stuff like structure layout doesn't change with any of these ๐Ÿ’œ.
  • -m32-bit, -m16-bit, -m8-bit โ€” Same vibe as the align options above, but these set the alignment of stack frames, writable data, and constants to 32, 16, or 8 bits ๐ŸŒธ. The default is 32-bit.
  • -mno-prologue-epilogue, -mprologue-epilogue โ€” With -mno-prologue-epilogue, GCC skips the usual function prologue and epilogue that set up the stack frame โš ๏ธ. It also won't generate any return instructions or return sequences. Only use this if you're going to eyeball the compiled code yourself, because GCC won't warn or error when call-saved registers need saving or when local variables need storage ๐Ÿ’€.
  • -melf โ€” A legacy no-op, so it does nothing ๐Ÿ™ˆ.
  • -sim โ€” Links with the input-output functions from a simulator library ๐Ÿงš. Code, initialized data, and zero-initialized data get allocated one right after the other.
  • -sim2 โ€” Like -sim, but it also passes linker options so initialized data lands at 0x40000000 and zero-initialized data lands at 0x80000000 ๐Ÿ”ฅ.

๐ŸŒธ C-SKY Options ๐ŸŒธ

GCC supports these when you're compiling for C-SKY V2 processors ๐Ÿฆ‹.

  • -march=arch โ€” Specifies the C-SKY target architecture. The valid values are ck801, ck802, ck803, ck807, and ck810. The default is ck810 ๐Ÿ’….
  • -mcpu=cpu โ€” Specifies the C-SKY target processor. Valid values for cpu are: ck801, ck801t, ck802, ck802t, ck802j, ck803, ck803h, ck803t, ck803ht, ck803f, ck803fh, ck803e, ck803eh, ck803et, ck803eht, ck803ef, ck803efh, ck803ft, ck803eft, ck803efht, ck803r1, ck803hr1, ck803tr1, ck803htr1, ck803fr1, ck803fhr1, ck803er1, ck803ehr1, ck803etr1, ck803ehtr1, ck803efr1, ck803efhr1, ck803ftr1, ck803eftr1, ck803efhtr1, ck803s, ck803st, ck803se, ck803sf, ck803sef, ck803seft, ck807e, ck807ef, ck807, ck807f, ck810e, ck810et, ck810ef, ck810eft, ck810, ck810v, ck810f, ck810t, ck810fv, ck810tv, ck810ft, and ck810ftv ๐ŸŽ€.
  • -mbig-endian, -EB, -mlittle-endian, -EL โ€” Picks big-endian or little-endian code ๐Ÿซถ. Little-endian is the default.
  • -mfloat-abi=name โ€” Picks which floating-point ABI to use. The options are soft, softfp, and hard ๐Ÿ”ฅ.

soft makes GCC generate output that calls library functions for floating-point math. softfp lets it use hardware floating-point instructions but still follows the soft-float calling conventions. hard gives you floating-point instructions and uses FPU-specific calling conventions ๐Ÿ’œ.

The default depends on your specific target configuration. Heads up though: the hard-float and soft-float ABIs are not link-compatible ๐Ÿ˜ญ. You have to compile your whole program with the same ABI and link it against a compatible set of libraries.

  • -mhard-float, -msoft-float โ€” Picks hardware or software floating-point implementations ๐Ÿฆ‹. The default is soft float.
  • -mdouble-float, -mno-double-float โ€” When -mhard-float is on, this enables double-precision float instructions โœจ. It's the default except when compiling for CK803.
  • -mfdivdu, -mno-fdivdu โ€” When -mhard-float is on, this enables the "frecipd", "fsqrtd", and "fdivd" instructions ๐Ÿงš. It's the default except when compiling for CK803.
  • -mfpu=fpu โ€” Picks the floating-point processor ๐Ÿ’…. This one only works with -mhard-float. The values for fpu are fpv2_sf (which is the same as -mno-double-float -mno-fdivdu), fpv2 (which is -mdouble-float -mno-divdu), and fpv2_divd (which is -mdouble-float -mdivdu).
  • -melrw, -mno-elrw โ€” Enables the extended "lrw" instruction ๐ŸŒˆ. It defaults to on for CK801 and off for everything else.
  • -mistack, -mno-istack โ€” Enables interrupt stack instructions ๐Ÿ”ฅ. The default is off.
The -mistack option is required if you want to handle the "interrupt" and "isr" function attributes ๐Ÿ’€.
  • -mmp โ€” Enables multiprocessor instructions. Default is off ๐Ÿซถ.
  • -mcp โ€” Enables coprocessor instructions. Default is off ๐ŸŽ€.
  • -mcache โ€” Also enables coprocessor instructions. Default is off ๐Ÿ™ˆ.
  • -msecurity โ€” Enables C-SKY security instructions. Default is off ๐Ÿ”.
  • -mtrust โ€” Enables C-SKY trust instructions. Default is off โœจ.
  • -mdsp, -medsp, -mvdsp โ€” These turn on C-SKY DSP, Enhanced DSP, or Vector DSP instructions, in that order ๐Ÿ’œ. All of them default to off.
  • -mdiv, -mno-div โ€” Generates divide instructions. Default is off ๐Ÿฆ‹.
  • -msmart, -mno-smart โ€” Generates code for Smart Mode, which only uses registers numbered 0-7 so you can use 16-bit instructions ๐Ÿงš. This gets ignored for CK801, where that's already the required behavior, and it defaults to on for CK802. For other targets the default is off.
  • -mhigh-registers, -mno-high-registers โ€” Generates code that uses the high registers numbered 16-31 ๐ŸŒŸ. This isn't supported on CK801, CK802, or CK803, and it's enabled by default on other processors.
  • -manchor, -mno-anchor โ€” Generates code that uses global anchor symbol addresses โญ.
  • -mpushpop, -mno-pushpop โ€” Generates code using "push" and "pop" instructions ๐Ÿ’…. It defaults to on.
  • -mmultiple-stld, -mstm, -mno-multiple-stld, -mno-stm โ€” Generates code using "stm" and "ldm" instructions ๐ŸŽ€. This isn't supported on CK801, but it's enabled by default on other processors.
  • -mconstpool, -mno-constpool โ€” Creates constant pools in the compiler, instead of leaving it to the assembler ๐Ÿซถ. This is the default, and it's required for correct code generation on CK801 and CK802. It's optional on other processors.
  • -mstack-size, -mno-stack-size โ€” Emits ".stack_size" directives for each function in the assembly output ๐ŸŒˆ. It defaults to off.
  • -mccrt, -mno-ccrt โ€” Generates code for the C-SKY compiler runtime instead of libgcc ๐Ÿ’–. It defaults to off.
  • -mbranch-cost=n โ€” Sets the branch costs to roughly n instructions ๐Ÿ“. The default is 1.
  • -msched-prolog, -mno-sched-prolog โ€” okay so these let you turn on scheduling for function prologue and epilogue sequences ๐Ÿงš Heads up, though: this can produce code that doesn't follow the C-SKY V2 ABI prologue rules, and you won't be able to debug it or backtrace through it ๐Ÿ˜ญ It's off by default, so you'd have to opt in on purpose.
  • -msim โ€” this links in the library libsemi.a, which is compatible with the simulator ๐Ÿ’ป It only applies to the ELF compiler, so don't expect it anywhere else ๐ŸŒธ

โœจ Cygwin and MinGW Options โœจ

These extra options are for Microsoft Windows targets ๐ŸชŸ๐Ÿ’–

  • -mconsole โ€” this tells the linker to set the PE header subsystem type to console, so you get a console application ๐Ÿ–ฅ๏ธ It works for Cygwin and MinGW targets and is already on by default there ๐Ÿซถ
  • -mcrtdll=library โ€” this one is kind of a big deal ๐Ÿ‘‘ It preprocesses, compiles or links with the C RunTime DLL library you name. It changes the predefined macros "__CRTDLL__", "__MSVCRT__", "_UCRT" and "__MSVCRT_VERSION__" for that CRT library, picks the start file for it, and links against it too. The CRT library names the preprocessor recognises are "crtdll*", "msvcrt10*", "msvcrt20*", "msvcrt40*", "msvcr40*", "msvcrtd*", "msvcrt-os*", "msvcr70*", "msvcr71*", "msvcr80*", "msvcr90*", "msvcr100*", "msvcr110*", "msvcr120*" and "ucrt*" โœจ If you don't use this option, the default MinGW import library "msvcrt" gets used for linking, and the preprocessor doesn't get any adjustments. That "msvcrt" library is just a symlink to, or a copy of, another MinGW CRT import library that gets picked when MinGW is compiled ๐Ÿ”— The "msvcrt-os" library is for the Windows system CRT DLL "msvcrt.dll", and it's usually the default MinGW import library ๐Ÿ  Generally speaking, switching the CRT DLL means recompiling the entire MinGW CRT, so this is strictly for experiments and testing ๐Ÿงช It's only for MinGW targets.
  • -mdll โ€” this one's for Cygwin and MinGW targets ๐Ÿฆ‹ It makes a DLL, which is a dynamic link library, and lets you pick the runtime startup object and entry point you need.
  • -mnop-fun-dllimport โ€” available for Cygwin and MinGW targets ๐Ÿ™ˆ It tells GCC to ignore the "dllimport" attribute.
  • -mthreads โ€” for MinGW targets only ๐Ÿงต It turns on MinGW-specific thread support.
  • -municode โ€” for MinGW-w64 targets ๐ŸŒˆ It predefines the "UNICODE" preprocessor macro and picks Unicode-capable runtime startup code.
  • -mwin32 โ€” for Cygwin and MinGW targets ๐ŸŽ€ It sets the typical Microsoft Windows predefined macros in the preprocessor, but it doesn't change which runtime library or startup code gets chosen.
  • -mwindows โ€” for Cygwin and MinGW targets ๐ŸชŸ It makes a GUI application by telling the linker to set the PE header subsystem type to match.
  • -fno-set-stack-executable โ€” for MinGW targets ๐Ÿ”’ It means the executable flag isn't set for the stack that nested functions use. This matters for binaries running in Microsoft Windows kernel mode, because the User32 API that sets executable privileges isn't available there ๐Ÿ’€
  • -fwritable-relocated-rdata โ€” for MinGW and Cygwin targets ๐Ÿ’œ It puts relocated data from a read-only section into the ".data" section. This is needed for older runtimes that can't modify ".rdata" sections for pseudo-relocation.
  • -mpe-aligned-commons โ€” for Cygwin and MinGW targets โœจ It uses the GNU extension to the PE file format that lets COMMON variables be aligned correctly when generating code. It's on by default if GCC detects that the target assembler found during configuration supports the feature ๐Ÿซถ

See also under x86 Options for standard options ๐Ÿ”—


๐ŸŽ Darwin Options ๐ŸŽ

These options are defined for every architecture running the Darwin operating system ๐ŸŒธ

FSF GCC on Darwin doesn't make "fat" object files ๐Ÿ™…โ€โ™€๏ธ It makes an object file for the single architecture that GCC was built to target. Apple's GCC on Darwin does make "fat" files when you use multiple -arch options ๐Ÿ’– It does this by running the compiler or linker several times and joining the results together with lipo.

The subtype of the file (like ppc7400, ppc970 or i686) comes from the flags that specify the ISA GCC is targeting, like -mcpu or -march โœจ The -force_cpusubtype_ALL option can override that.

The Darwin tools act differently when they hit an ISA mismatch ๐Ÿ‘€ The assembler, as, only lets you use instructions that are valid for the subtype of the file it's making, so you can't put 64-bit instructions in a ppc750 object file ๐Ÿšซ The linker for shared libraries, /usr/bin/libtool, fails and prints an error if you ask it to make a shared library with a less restrictive subtype than its input files. For instance, trying to put a ppc970 object file in a ppc7400 library is a no-go ๐Ÿ˜ญ The linker for executables, ld, quietly gives the executable the most restrictive subtype of any of its input files ๐Ÿคซ

  • -Fdir โ€” this adds the framework directory dir to the front of the list of directories searched for header files ๐Ÿ“‚ These directories get mixed in with the ones from -I options, and they're scanned left to right ๐Ÿ‘ˆ

A framework directory is just a directory full of frameworks ๐Ÿงš A framework is a directory with a Headers and/or PrivateHeaders directory right inside it, and its name ends in .framework. The framework's name is that directory's name without the .framework part. Headers for the framework live in one of those two directories, and Headers gets searched first ๐Ÿฅฐ A subframework is a framework directory that sits inside a framework's Frameworks directory. Including subframework headers only works from a header inside the framework that holds the subframework, or from a sibling subframework header. Two subframeworks are siblings if they live in the same framework ๐Ÿ‘ฏ A subframework shouldn't share a name with a framework, and if it does, you'll get a warning โš ๏ธ Right now a subframework can't have subframeworks of its own, but that might get added later ๐Ÿ”ฎ The standard frameworks live in /System/Library/Frameworks and /Library/Frameworks ๐Ÿ›๏ธ An example include looks like #include <Framework/header.h>, where Framework is the framework's name and header.h is found in the PrivateHeaders or Headers directory ๐Ÿ’…

  • -iframeworkdir โ€” this is like -F, except the directory gets treated as a system directory ๐Ÿ› ๏ธ The big difference is that with -iframework, the compiler won't warn you about stuff inside header files found through dir ๐Ÿ™ˆ This only works for the C family of languages.
  • -gused โ€” this emits debugging info for symbols that are actually used ๐Ÿ” For the stabs debugging format, it turns on -feliminate-unused-debug-symbols. It's ON by default โœ…
  • -gfull โ€” this emits debugging info for every symbol and type ๐Ÿ“š
  • -fconstant-cfstrings โ€” this is just an alias for -mconstant-cfstrings ๐ŸŽ€
  • -mconstant-cfstrings โ€” when the NeXT runtime is in use (which is the default on these systems), this overrides any -fconstant-string-class setting and makes "@"..."" literals get laid out as constant CoreFoundation strings ๐Ÿ’œ
  • -mmacosx-version-min=version โ€” this sets the earliest version of MacOS X that the executable will run on ๐Ÿ Typical values for version include 12, 10.12, and 10.5.8 ๐ŸŒˆ

If the compiler was built to use the system's headers by default, the default for this option is the system version the compiler is running on โœจ Otherwise, the default is to make choices that are compatible with as many systems and code bases as possible ๐Ÿซถ

  • -mkernel โ€” this turns on kernel development mode ๐Ÿง‘โ€๐Ÿ’ป It sets -static, -fno-common, -fno-use-cxa-atexit, -fno-exceptions, -fno-non-call-exceptions, -fapple-kext, -fno-weak and -fno-rtti where they apply. It also sets -mno-altivec, -msoft-float, -fno-builtin and -mlong-branch for PowerPC targets ๐Ÿ’…
  • -mone-byte-bool โ€” this overrides the defaults for "bool" so that sizeof(bool)==1 ๐ŸŽฏ By default sizeof(bool) is 4 when compiling for Darwin/PowerPC and 1 when compiling for Darwin/x86, so this option does nothing on x86.
Warning: The -mone-byte-bool switch makes GCC generate code that isn't binary compatible with code generated without it ๐Ÿ˜ฑ You may have to recompile all the other modules in a program, including system libraries, if you use it. Only use this switch when you need to match a non-default data model ๐Ÿ’€
  • -mfix-and-continue, -ffix-and-continue, -findirect-data โ€” these generate code built for quick turnaround development ๐Ÿ”ฅ For example, they let GDB dynamically load .o files into programs that are already running โšก -findirect-data and -ffix-and-continue are only here for backwards compatibility ๐Ÿ•ฐ๏ธ
  • -all_load โ€” this loads every member of static archive libraries ๐Ÿ“ฆ See man ld(1) for more information ๐Ÿ”—
  • -arch_errors_fatal โ€” this makes errors about files with the wrong architecture fatal ๐Ÿšจ
  • -bind_at_load โ€” this marks the output file so the dynamic linker binds all undefined references when the file is loaded or launched ๐Ÿช„
  • -bundle โ€” this produces a Mach-o bundle format file ๐ŸŽ See man ld(1) for more information.
  • -bundle_loader executable โ€” this names the executable that will load the build output file being linked ๐Ÿซถ See man ld(1) for more information.
  • -dynamiclib โ€” when you pass this, GCC makes a dynamic library instead of an executable when linking, using the Darwin libtool command ๐Ÿฆ‹
  • -force_cpusubtype_ALL โ€” this gives GCC's output file the ALL subtype, instead of the one that -mcpu or -march would pick ๐ŸŒŸ

Okay so the Darwin linker bit is next, and bestie, there are SO many of these ๐Ÿ’…๐ŸŒธ

Quick heads up: all of these get handed straight to the Darwin linker, so if you want the full lowdown on each one, go read that linker's man page ๐Ÿงš


  • -nodefaultrpaths โ€” Ngl this one is kinda important ๐Ÿ™ˆ It stops GCC from tossing default run paths for the compiler library directories into your executables, modules, or dynamic libraries. On macOS 10.5 and later, the embedded runpath gets added automatically unless you add -nodefaultrpaths to your link line. Run paths are needed (so they're enforced!) to build on macOS 10.11 or later โœจ

Now here's the whole squad of Darwin linker options, and they all share the same vibe ๐Ÿ‘‘

  • -allowable_client client_name
  • -client_name
  • -compatibility_version
  • -current_version
  • -dead_strip
  • -dependency-file
  • -dylib_file
  • -dylinker_install_name
  • -dynamic
  • -exported_symbols_list
  • -filelist
  • -flat_namespace
  • -force_flat_namespace
  • -headerpad_max_install_names
  • -image_base
  • -init
  • -install_name
  • -keep_private_externs
  • -multi_module
  • -multiply_defined
  • -multiply_defined_unused
  • -noall_load
  • -no_dead_strip_inits_and_terms
  • -nofixprebinding
  • -nomultidefs
  • -noprebind
  • -noseglinkedit
  • -pagezero_size
  • -prebind
  • -prebind_all_twolevel_modules
  • -private_bundle
  • -read_only_relocs
  • -sectalign
  • -sectobjectsymbols
  • -whyload
  • -seg1addr
  • -sectcreate
  • -sectobjectsymbols
  • -sectorder
  • -segaddr
  • -segs_read_only_addr
  • -segs_read_write_addr
  • -seg_addr_table
  • -seg_addr_table_filename
  • -seglinkedit
  • -segprot
  • -segs_read_only_addr
  • -segs_read_write_addr
  • -single_module
  • -static
  • -sub_library
  • -sub_umbrella
  • -twolevel_namespace
  • -umbrella
  • -undefined
  • -unexported_symbols_list
  • -weak_reference_mismatches
  • -whatsloaded

These get passed straight to the Darwin linker, which has the full deep dive on each one ๐Ÿฆ‹


โœจ DEC Alpha Options โœจ

These -m options are defined for the DEC Alpha implementations ๐Ÿ’œ

  • -mno-soft-float
  • -msoft-float โ€” Okay so these two are opposites bestie ๐Ÿซถ One uses the hardware floating-point instructions for floating-point ops, the other doesn't. With -msoft-float, functions in libgcc.a do the floating-point work instead. Unless those get replaced with routines that emulate the floating-point operations, or get compiled to call those emulation routines, these routines issue floating-point operations. So if you're compiling for an Alpha without floating-point operations, you have to make sure the library is built so it doesn't call them ๐Ÿ˜ญ

Also, Alpha implementations without floating-point operations are still required to have floating-point registers, so keep that in mind ๐ŸŒธ

  • -mfp-reg
  • -mno-fp-regs โ€” Generates code that uses (or doesn't use) the floating-point register set. -mno-fp-regs implies -msoft-float. If the set isn't used, floating-point operands get passed in integer registers as if they were integers, and floating-point results get passed in $0 instead of $f0. This is a non-standard calling sequence, so any function with a floating-point argument or return value that gets called by code compiled with -mno-fp-regs also has to be compiled with that option. Big rule, bestie ๐Ÿšจ

A typical use is building a kernel that doesn't use (and therefore doesn't need to save and restore) any floating-point registers ๐Ÿงš

  • -mieee โ€” The Alpha architecture has floating-point hardware that's optimized for max performance, and it's mostly IEEE compliant. For full compliance though, it needs software help ๐Ÿ™„ This option makes fully IEEE-compliant code, EXCEPT the inexact-flag isn't maintained (see below). When it's on, the preprocessor macro "_IEEE_FP" gets defined during compilation. The code is less efficient, but it can correctly handle denormalized numbers and exceptional IEEE values like not-a-number and plus/minus infinity. Other Alpha compilers call this option -ieee_with_no_inexact ๐Ÿ’…

DEBIAN SPECIFIC: This option is on by default for alpha-linux-gnu, unless -ffinite-math-only (which is part of the -ffast-math set) is specified. Why? Because the software functions in the GNU libc math libraries generate denormalized numbers, NaNs, and infs, and all of those will make a program SIGFPE when it tries to use the results without -mieee ๐Ÿ’€

  • -mieee-with-inexact โ€” This is like -mieee, except the generated code also maintains the IEEE inexact-flag. Turn this on and the code implements fully-compliant IEEE math. On top of "_IEEE_FP", the preprocessor macro "_IEEE_FP_EXACT" also gets defined. On some Alpha implementations the code might run way slower than the default, and since there's very little code that depends on the inexact-flag, you should normally NOT specify this one ๐Ÿ™ˆ Other Alpha compilers call it -ieee_with_inexact โœจ
  • -mfp-trap-mode=trap-mode โ€” Controls which floating-point related traps are enabled ๐Ÿ”ฅ Other Alpha compilers call this option -fptm trap-mode. The trap mode can be one of four values:
    • n โ€” The default (normal) setting. The only traps enabled are the ones that can't be disabled in software (like the division by zero trap).
    • u โ€” On top of everything n enables, underflow traps get enabled too.
    • su โ€” Like u, but the instructions are marked as safe for software completion (see the Alpha architecture manual for details).
    • sui โ€” Like su, but inexact traps are enabled as well.
  • -mfp-rounding-mode=rounding-mode โ€” Selects the IEEE rounding mode ๐ŸŒˆ Other Alpha compilers call this one -fprm rounding-mode. The rounding-mode can be one of:
    • n โ€” Normal IEEE rounding mode. Floating-point numbers get rounded towards the nearest machine number, or towards the even machine number in case of a tie.
    • m โ€” Round towards minus infinity.
    • c โ€” Chopped rounding mode. Floating-point numbers get rounded towards zero.
    • d โ€” Dynamic rounding mode. A field in the floating-point control register (fpcr, see the Alpha architecture reference manual) controls the rounding mode in effect. The C library sets this register up for rounding towards plus infinity. So unless your program messes with the fpcr, d means round towards plus infinity ๐Ÿฆ‹
  • -mtrap-precision=trap-precision โ€” Okay so on the Alpha architecture, floating-point traps are imprecise ๐Ÿ˜ญ Without software help, you can't recover from a floating trap, and the program usually has to be terminated. GCC can generate code that helps OS trap handlers figure out the exact location that caused a floating-point trap. Depending on what your app needs, you can pick one of these precision levels:
    • p โ€” Program precision. This is the default, and it means a trap handler can only tell which program caused the floating-point exception.
    • f โ€” Function precision. The trap handler can figure out the function that caused a floating-point exception.
    • i โ€” Instruction precision. The trap handler can figure out the exact instruction that caused a floating-point exception.

Other Alpha compilers have the equivalent options called -scope_safe and -resumption_safe โœจ

  • -mieee-conformant โ€” This marks the generated code as IEEE conformant ๐Ÿ“œ You must NOT use this unless you also specify -mtrap-precision=i AND either -mfp-trap-mode=su or -mfp-trap-mode=sui. Its only effect is to emit the line .eflag 48 in the function prologue of the generated assembly file.
  • -mbuild-constants โ€” Normally GCC looks at a 32- or 64-bit integer constant to see if it can build it from smaller constants in two or three instructions. If it can't, it outputs the constant as a literal and generates code to load it from the data segment at run time ๐Ÿงฉ

Use this option to make GCC build ALL integer constants using code, even if that takes more instructions (the max is six). You'd typically use this to build a shared library dynamic loader. Since it's a shared library itself, it has to relocate itself in memory before it can find the variables and constants in its own data segment ๐Ÿ’–

  • -mbwx
  • -mno-bwx
  • -mcix
  • -mno-cix
  • -mfix
  • -mno-fix
  • -mmax
  • -mno-max โ€” Says whether GCC should generate code that uses the optional BWX, CIX, FIX, and MAX instruction sets ๐Ÿ“ By default it uses whichever instruction sets the CPU type from -mcpu= supports, or the CPU GCC was built on if you don't specify one.
  • -msafe-bwa
  • -mno-safe-bwa โ€” Says whether, when the optional BWX instruction set is missing, GCC should generate multi-thread and async-signal safe code for byte and aligned word memory accesses ๐Ÿ”
  • -msafe-partial
  • -mno-safe-partial โ€” Says whether GCC should generate multi-thread and async-signal safe code for partial memory accesses. That includes piecemeal accesses to unaligned data, plus block accesses to the leading and trailing parts of aggregate types or other objects in memory that don't respectively start and end on an aligned 64-bit data boundary ๐Ÿซถ
  • -mfloat-vax
  • -mfloat-ieee โ€” Generates code that uses (or doesn't use) VAX F and G floating-point arithmetic instead of IEEE single and double precision ๐ŸŽ€
  • -mexplicit-relocs
  • -mno-explicit-relocs โ€” Okay so older Alpha assemblers had no way to generate symbol relocations except through assembler macros, and those macros don't allow optimal instruction scheduling ๐Ÿ˜ญ GNU binutils as of version 2.12 supports a new syntax that lets the compiler explicitly mark which relocations apply to which instructions. This option is mostly useful for debugging, since GCC detects the assembler's capabilities when it's built and sets the default accordingly ๐Ÿงš
  • -msmall-data
  • -mlarge-data โ€” When -mexplicit-relocs is in effect, static data gets accessed via gp-relative relocations. With -msmall-data, objects 8 bytes long or smaller get placed in a small data area (the ".sdata" and ".sbss" sections) and get accessed through 16-bit relocations off of the $gp register. This limits the small data area to 64KB, but the upside is the variables can be accessed directly with a single instruction, which is iconic ๐Ÿ‘‘

The default is -mlarge-data, bestie ๐Ÿ’– With this on, the data area is capped at just under 2GB. If your program needs more than 2GB of data, it has to use malloc or mmap to grab memory from the heap instead of stuffing it into the program's data segment ๐Ÿง โœจ

When you're generating code for shared libraries, -fpic implies -msmall-data, and -fPIC implies -mlarge-data ๐ŸŽ€

  • -msmall-text, -mlarge-text โ€” ๐Ÿฆ‹ With -msmall-text, the compiler assumes the code for the whole program (or shared library) fits in 4MB, so everything is reachable with a branch instruction. With -msmall-data, the compiler can assume all local symbols share the same $gp value, which cuts the instructions needed for a function call from 4 down to 1. It's giving efficiency ๐Ÿ˜ญ

The default is -mlarge-text ๐Ÿ’…

  • -mcpu=cpu_type โ€” ๐Ÿ‘‘ Sets the instruction set and instruction scheduling parameters for the machine type cpu_type. You can use either the EV style name or the matching chip number. GCC has scheduling parameters for the EV4, EV5, and EV6 families, and it picks the default instruction set based on the processor you name. If you don't name a processor type, GCC just goes with whatever processor the compiler was built on ๐ŸŒธ

Supported values for cpu_type are:

  • ev4, ev45, 21064 โ€” Schedules as an EV4 and has no instruction set extensions ๐Ÿ“
  • ev5, 21164 โ€” Schedules as an EV5 and has no instruction set extensions ๐Ÿ“
  • ev56, 21164a โ€” Schedules as an EV5 and supports the BWX extension โœจ
  • pca56, 21164pc, 21164PC โ€” Schedules as an EV5 and supports the BWX and MAX extensions โœจ
  • ev6, 21264 โ€” Schedules as an EV6 and supports the BWX, FIX, and MAX extensions ๐Ÿ”ฅ
  • ev67, 21264a โ€” Schedules as an EV6 and supports the BWX, CIX, FIX, and MAX extensions ๐Ÿ”ฅ

Native toolchains also support the value native, which picks the best architecture option for the host processor. -mcpu=native does nothing if GCC doesn't recognize the processor ๐Ÿ™ˆ

  • -mtune=cpu_type โ€” ๐ŸŽฏ Only sets the instruction scheduling parameters for the machine type cpu_type. The instruction set stays exactly the same.

Native toolchains also support the value native, which picks the best architecture option for the host processor. -mtune=native does nothing if GCC doesn't recognize the processor ๐Ÿ’œ

  • -mmemory-latency=time โ€” ๐Ÿงš Sets the latency the scheduler should assume for typical memory references as the application sees them. This number really depends on the memory access patterns the application uses and how big the external cache is on the machine. Lowkey a vibe-check number ๐Ÿ“Š

Valid options for time are:

  • number โ€” A decimal number representing clock cycles ๐Ÿ”ข
  • L1, L2, L3, main โ€” The compiler has estimates of clock cycles for "typical" EV4 & EV5 hardware for the Level 1, 2, and 3 caches (also called Dcache, Scache, and Bcache), plus main memory. Note that L3 is only valid for EV5 ๐Ÿซถ

โœจ eBPF Options โœจ

  • -mframe-limit=bytes โ€” ๐Ÿšง Specifies the hard limit for frame sizes, in bytes. Right now the value you pass should be less than or equal to 32767. It defaults to whatever limit the targeted version of the Linux kernel imposes ๐Ÿง
  • -mbig-endian โ€” ๐ŸŒˆ Generates code for a big-endian target ๐Ÿ’…
  • -mlittle-endian โ€” ๐ŸŒˆ Generates code for a little-endian target. This is the default ๐Ÿ’–
  • -mjmpext, -mno-jmpext โ€” ๐Ÿฆ‹ Enables or disables generation of extra conditional-branch instructions. Enabled for CPU v2 and above ๐Ÿ”ฅ
  • -mjmp32, -mno-jmp32 โ€” ๐Ÿฆ‹ Enables or disables generation of 32-bit jump instructions. Enabled for CPU v3 and above ๐Ÿ”ฅ
  • -malu32, -mno-alu32 โ€” ๐Ÿฆ‹ Enables or disables generation of 32-bit ALU instructions. Enabled for CPU v3 and above ๐Ÿ”ฅ
  • -mv3-atomics, -mno-v3-atomics โ€” โš›๏ธ Enables or disables instructions for general atomic operations introduced in CPU v3. Enabled for CPU v3 and above ๐Ÿ”ฅ
  • -mbswap, -mno-bswap โ€” ๐Ÿ”„ Enables or disables byte swap instructions. Enabled for CPU v4 and above ๐Ÿ‘‘
  • -msdiv, -mno-sdiv โ€” โž— Enables or disables signed division and modulus instructions. Enabled for CPU v4 and above ๐Ÿ‘‘
  • -msmov, -mno-smov โ€” ๐Ÿšš Enables or disables sign-extending move and memory load instructions. Enabled for CPU v4 and above ๐Ÿ‘‘
  • -mcpu=version โ€” ๐ŸŽ€ Specifies which version of the eBPF ISA to target. Newer versions might not be supported by every kernel. The default is v4.

Supported values for version are:

  • v1 โ€” The first stable eBPF ISA, with no special features or extensions ๐Ÿผ
  • v2 โ€” Supports the jump extensions, as in -mjmpext ๐ŸŒฑ
  • v3 โ€” All features of v2, plus:
    • -<32-bit jump operations, as in -mjmp32>
    • -<32-bit ALU operations, as in -malu32>
    • -<general atomic operations, as in -mv3-atomics>
  • v4 โ€” All features of v3, plus:
    • -<Byte swap instructions, as in -mbswap>
    • -<Signed division and modulus instructions, as in -msdiv>
    • -<Sign-extending move and memory load instructions, as in -msmov>
  • -mco-re โ€” ๐Ÿง  Enables BPF Compile Once - Run Everywhere (CO-RE) support. It requires and is implied by -gbtf ๐Ÿซถ
  • -mno-co-re โ€” ๐Ÿ™… Disables BPF Compile Once - Run Everywhere (CO-RE) support. BPF CO-RE support is enabled by default when generating BTF debug information for the BPF target ๐Ÿ™ˆ
  • -mxbpf โ€” ๐Ÿš€ Generates code for an expanded version of BPF, which relaxes some of the restrictions the BPF architecture imposes. It can:
    • -<Save and restore callee-saved registers at function entry and exit, respectively.>
  • -masm=dialect โ€” ๐Ÿ“ Outputs assembly instructions using the selected eBPF dialect. The default is pseudoc ๐Ÿ’…

Supported values for dialect are:

  • normal โ€” Outputs the normal assembly dialect ๐Ÿ“
  • pseudoc โ€” Outputs the pseudo-C assembly dialect ๐Ÿ“
  • -minline-memops-threshold=bytes โ€” ๐Ÿ“ Specifies a size threshold in bytes at or below which memmove, memcpy, and memset are always expanded inline. Operations dealing with sizes bigger than this threshold would have to use a library call instead of being expanded inline. BPF doesn't allow libcalls, though, so going over this threshold is a compile-time error ๐Ÿ’€ The default is 1024 bytes.

๐Ÿงš FR30 Options ๐Ÿงš

These options are defined specifically for the FR30 port ๐ŸŒธ

  • -msmall-model โ€” ๐Ÿ”ฌ Uses the small address space model. This can produce smaller code, but it does assume that all symbolic values and addresses fit into a 20-bit range.
  • -mno-lsim โ€” ๐Ÿ› ๏ธ Assumes runtime support has been provided, so there's no need to include the simulator library (libsim.a) on the linker command line.

๐ŸŒท FT32 Options ๐ŸŒท

These options are defined specifically for the FT32 port ๐Ÿฆ‹

  • -msim โ€” ๐Ÿ–ฅ๏ธ Specifies that the program will run on the simulator. This links in an alternate runtime startup and library. You must not use this option when generating programs for real hardware. You have to provide your own runtime library for whatever I/O functions are needed ๐Ÿšจ
  • -mlra โ€” ๐Ÿคท Does nothing. Preserved for backward compatibility.
  • -mnodiv โ€” โž— Do not use div and mod instructions.
  • -mft32b โ€” โœจ Enables use of the extended instructions of the FT32B processor.
  • -mcompress โ€” ๐Ÿ—œ๏ธ Compresses all code using the Ft32B code compression scheme.
  • -mnopm โ€” ๐Ÿ™… Do not generate code that reads program memory.

๐Ÿฆ‹ FRV Options ๐Ÿฆ‹

  • -mgpr-32 โ€” Only uses the first 32 general-purpose registers ๐Ÿงบ
  • -mgpr-64 โ€” Uses all 64 general-purpose registers ๐Ÿงบ
  • -mfpr-32 โ€” Uses only the first 32 floating-point registers ๐ŸŒŠ
  • -mfpr-64 โ€” Uses all 64 floating-point registers ๐ŸŒŠ
  • -mhard-float โ€” Uses hardware instructions for floating-point operations ๐Ÿ”ฅ
  • -msoft-float โ€” Uses library routines for floating-point operations ๐Ÿงธ
  • -malloc-cc โ€” Dynamically allocates condition code registers ๐ŸŽฒ
  • -mfixed-cc โ€” Does not try to dynamically allocate condition code registers. It only uses icc0 and fcc0 ๐Ÿ“Œ
  • -mdword โ€” Changes the ABI to use double word instructions ๐Ÿ‘ฏ
  • -mno-dword โ€” Does not use double word instructions ๐Ÿ™…
  • -mdouble โ€” Uses floating-point double instructions ๐Ÿ’œ
  • -mno-double โ€” Does not use floating-point double instructions ๐Ÿ™…
  • -mmedia โ€” Uses media instructions ๐ŸŽฌ
  • -mno-media โ€” Does not use media instructions ๐Ÿ™…
  • -mmuladd โ€” Uses multiply and add/subtract instructions โž•
  • -mno-muladd โ€” Does not use multiply and add/subtract instructions ๐Ÿ™…
  • -mfdpic โ€” ๐Ÿงฉ Selects the FDPIC ABI, which uses function descriptors to represent pointers to functions. Without any PIC/PIE-related options, it implies -fPIE. With -fpic or -fpie, it assumes GOT entries and small data are within a 12-bit range from the GOT base address. With -fPIC or -fPIE, GOT offsets are computed with 32 bits. With a bfin-elf target, this option implies -msim ๐Ÿซถ
  • -minline-plt โ€” ๐Ÿช„ Enables inlining of PLT entries in function calls to functions that are not known to bind locally. It has no effect without -mfdpic. It's enabled by default when optimizing for speed and compiling for shared libraries (i.e., -fPIC or -fpic), or when an optimization option such as -O3 or above is on the command line ๐Ÿš€
  • -mTLS โ€” Assumes a large TLS segment when generating thread-local code ๐Ÿงต
  • -mtls โ€” Does not assume a large TLS segment when generating thread-local code ๐Ÿงต
  • -mgprel-ro โ€” ๐Ÿ” Enables the use of GPREL relocations in the FDPIC ABI for data known to be in read-only sections. It's enabled by default, except for -fpic or -fpie: even though it may make the global offset table smaller, it trades 1 instruction for 4. With -fPIC or -fPIE, it trades 3 instructions for 4, one of which may be shared by multiple symbols. It also avoids the need for a GOT entry for the referenced symbol, so it's more likely to be a win. If it isn't, -mno-gprel-ro can be used to disable it ๐Ÿ™Œ
  • -multilib-library-pic โ€” ๐Ÿ“š Links with the (library, not FD) pic libraries. It's implied by -mlibrary-pic, as well as by -fPIC and -fpic without -mfdpic. You should never have to use it explicitly ๐Ÿ™ˆ
  • -mlinked-fp โ€” Sticks to the EABI rule that a frame pointer must always be created whenever a stack frame gets allocated. It's on by default, and you can turn it off with -mno-linked-fp ๐Ÿฆ‹
  • -mlong-calls โ€” Calls functions outside the current compilation unit through indirect addressing, so those functions can live anywhere in the 32-bit address space โœจ
  • -malign-labels โ€” Tries to align labels to an 8-byte boundary by slipping NOPs into the previous packet ๐ŸŒธ. This only does anything when VLIW packing is on. It never makes new packets, it just adds NOPs to packets that already exist ๐Ÿ™ˆ
  • -mlibrary-pic โ€” Generates position-independent EABI code ๐Ÿงš
  • -macc-4 โ€” Only uses the first four media accumulator registers ๐Ÿ’œ
  • -macc-8 โ€” Uses all eight media accumulator registers ๐Ÿ”ฅ
  • -mpack โ€” Packs VLIW instructions ๐Ÿ“ฆ
  • -mno-pack โ€” Does not pack VLIW instructions ๐Ÿ™…โ€โ™€๏ธ
  • -mno-eflags โ€” Doesn't mark ABI switches in e_flags ๐Ÿซถ
  • -mcond-move โ€” Turns on conditional-move instructions. This is the default ๐Ÿ’…
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mno-cond-move โ€” Turns off conditional-move instructions ๐Ÿ™ˆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mscc โ€” Turns on conditional set instructions. This is the default โœจ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mno-scc โ€” Turns off conditional set instructions ๐Ÿ™ˆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mcond-exec โ€” Turns on conditional execution. This is the default ๐ŸŽ€
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mno-cond-exec โ€” Turns off conditional execution ๐Ÿ™ˆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mvliw-branch โ€” Runs a pass that packs branches into VLIW instructions. This is the default ๐Ÿฆ‹
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mno-vliw-branch โ€” Skips that pass, so branches don't get packed into VLIW instructions ๐Ÿ™ˆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mmulti-cond-exec โ€” Turns on optimization of "&&" and "||" in conditional execution. This is the default ๐ŸŒˆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mno-multi-cond-exec โ€” Turns off optimization of "&&" and "||" in conditional execution ๐Ÿ™ˆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mnested-cond-exec โ€” Turns on nested conditional execution optimizations. This is the default ๐Ÿ“
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -mno-nested-cond-exec โ€” Turns off nested conditional execution optimizations ๐Ÿ™ˆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ’€
  • -moptimize-membar โ€” Removes redundant "membar" instructions from the compiler-generated code. It's on by default ๐Ÿงน
  • -mno-optimize-membar โ€” Stops the automatic removal of redundant "membar" instructions from the generated code ๐Ÿ™…โ€โ™€๏ธ
  • -mtomcat-stats โ€” Makes gas print out tomcat statistics ๐Ÿ“Š
  • -mcpu=cpu โ€” Picks the processor type you want code generated for. The options are frv, fr550, tomcat, fr500, fr450, fr405, fr400, fr300 and simple ๐Ÿ‘‘

๐Ÿง GNU/Linux Options ๐Ÿง

These -m options are defined for GNU/Linux targets ๐Ÿ’–

  • -mglibc โ€” Uses the GNU C library. This is the default, except on *-*-linux-*uclibc*, *-*-linux-*musl* and *-*-linux-*android* targets ๐ŸŒธ
  • -muclibc โ€” Uses the uClibc C library. This is the default on *-*-linux-*uclibc* targets โœจ
  • -mmusl โ€” Uses the musl C library. This is the default on *-*-linux-*musl* targets ๐Ÿฆ‹
  • -mbionic โ€” Uses the Bionic C library. This is the default on *-*-linux-*android* targets ๐Ÿค–
  • -mandroid โ€” Compiles code that's compatible with the Android platform. This is the default on *-*-linux-*android* targets ๐Ÿ“ฑ

When compiling, this option turns on -mbionic, -fPIC, -fno-exceptions and -fno-rtti by default. When linking, it makes the GCC driver pass Android-specific options to the linker. And finally, it causes the preprocessor macro "__ANDROID__" to get defined ๐Ÿซถ

  • -tno-android-cc โ€” Undoes the compilation effects of -mandroid. That means it won't turn on -mbionic, -fPIC, -fno-exceptions and -fno-rtti by default ๐Ÿ™ˆ
  • -tno-android-ld โ€” Undoes the linking effects of -mandroid, so the linker gets standard Linux linking options instead ๐Ÿง

๐Ÿ H8/300 Options ๐Ÿ

These -m options are defined for the H8/300 implementations ๐Ÿ’œ

  • -mrelax โ€” Shortens some address references at link time when it's possible. It uses the linker option -relax ๐Ÿง˜โ€โ™€๏ธ
  • -mh โ€” Generates code for the H8/300H ๐ŸŒท
  • -ms โ€” Generates code for the H8S ๐Ÿ’ซ
  • -mn โ€” Generates code for the H8S and H8/300H in the normal mode. This switch has to be used with either -mh or -ms ๐ŸŽ€
  • -ms2600 โ€” Generates code for the H8S/2600. This switch has to be used with -ms ๐Ÿ”ฅ
  • -mexr โ€” Extended registers get stored on the stack before a function with the monitor attribute runs. The default option is -mexr. This option is only valid for H8S targets ๐Ÿ“š
  • -mno-exr โ€” Extended registers do not get stored on the stack before a function with the monitor attribute runs. The default option is -mno-exr. This option is only valid for H8S targets ๐Ÿ™…โ€โ™€๏ธ
  • -mint32 โ€” Makes "int" data 32 bits by default ๐Ÿ’…
  • -malign-300 โ€” On the H8/300H and H8S, uses the same alignment rules as the H8/300. By default, the H8/300H and H8S align longs and floats on 4-byte boundaries, but -malign-300 makes them align on 2-byte boundaries instead. This option does nothing on the H8/300 ๐ŸŒธ

๐ŸŒฟ HPPA Options ๐ŸŒฟ

These -m options are defined for the HPPA family of computers ๐Ÿ–ฅ๏ธ

  • -march=architecture-type โ€” Generates code for the architecture you specify. The choices for architecture-type are 1.0 for PA 1.0, 1.1 for PA 1.1, and 2.0 for PA 2.0 processors. To find the right architecture option for your machine on an HP-UX system, check /usr/lib/sched.models ๐Ÿ”. Code compiled for lower-numbered architectures runs on higher-numbered ones, but not the other way around ๐Ÿซถ
  • -mpa-risc-1-0, -mpa-risc-1-1, -mpa-risc-2-0 โ€” These are synonyms for -march=1.0, -march=1.1, and -march=2.0 respectively ๐Ÿฆ‹
  • -matomic-libcalls โ€” Generates libcalls for atomic loads and stores when sync libcalls are disabled. This is on by default, and it only affects how the HPPA backend generates atomic libcalls โœจ

Both the sync and libatomic libcall implementations use locking. Because of that, processor stores aren't atomic with respect to other atomic operations. Processor loads up to DImode are atomic with respect to other atomic operations, as long as they're implemented as a single access ๐Ÿ”’

The PA-RISC architecture doesn't support any atomic operations in hardware, except for the "ldcw" instruction. So all atomic support is implemented using sync and atomic libcalls. Sync libcall support lives in libgcc.a, and atomic libcall support lives in libatomic ๐Ÿ“š

This option generates "__atomic_exchange" calls for atomic stores. It also gives special handling to atomic DImode accesses on 32-bit targets ๐ŸŽ€

  • -mbig-switch โ€” Does nothing. It's only kept around for backward compatibility ๐Ÿ•ฐ๏ธ
  • -mcaller-copies โ€” The caller copies function arguments that get passed by hidden reference. Be careful with this one, because it isn't compatible with the default 32-bit runtime ๐Ÿšจ. The only aggregates passed by hidden reference are the ones larger than eight bytes, and this option gives you better compatibility with OpenMP ๐ŸŒˆ
  • -mcoherent-ldcw โ€” Uses the ldcw/ldcd coherent cache-control hint ๐ŸงŠ
  • -mdisable-fpregs โ€” Disables the floating-point registers. It's equivalent to "-msoft-float" ๐Ÿ™ˆ
  • -mdisable-indexing โ€” Stops the compiler from using indexing address modes. This avoids some rather obscure problems when compiling MIG generated code under MACH ๐Ÿค“
  • -mfast-indirect-calls โ€” Generates code that assumes calls never cross space boundaries. That lets GCC emit code that does indirect calls faster โšก
This option does not work in the presence of shared libraries or nested functions. ๐Ÿ’€
  • -mfixed-range=register-range โ€” Generates code that treats the given register range as fixed registers. A fixed register is one the register allocator can't use, which is handy when you're compiling kernel code ๐Ÿง‘โ€๐Ÿ’ป. You specify a register range as two registers separated by a dash, and you can list several ranges separated by a comma ๐ŸŽ€
  • -mgas โ€” Enables the use of assembler directives that only GAS understands ๐Ÿฆ‹
  • -mgnu-ld โ€” Uses options specific to GNU ld. It passes -shared to ld when building a shared library. This is the default when GCC is configured, explicitly or implicitly, with the GNU linker. This option doesn't affect which ld gets called. It only changes which parameters get passed to that ld ๐Ÿ”—. The ld that's called is decided by the --with-ld configure option, GCC's program search path, and finally the user's PATH ๐Ÿ—บ๏ธ. You can see which linker GCC uses by running which gcc -print-prog-name=ld``. This option is only available on the 64-bit HP-UX GCC, i.e. configured with hppa*64*-*-hpux* ๐Ÿ‘‘
  • -mhp-ld โ€” Uses options specific to HP ld. It passes -b to ld when building a shared library, and it passes +Accept TypeMismatch to ld on all links. This is the default when GCC is configured, explicitly or implicitly, with the HP linker. Like -mgnu-ld, it doesn't affect which ld gets called, only which parameters get passed to it ๐Ÿซง. The ld that's called is decided by the --with-ld configure option, GCC's program search path, and finally the user's PATH. You can print the linker GCC uses with which gcc -print-prog-name=ld``. This option is only available on the 64-bit HP-UX GCC, i.e. configured with hppa*64*-*-hpux* ๐Ÿ’œ
  • -mlinker-opt โ€” Turns on the optimization pass in the HP-UX linker. Heads up, though: this makes symbolic debugging impossible ๐Ÿ˜ญ
  • -mlong-calls โ€” okay so this one tells GCC to generate long call sequences โœจ, which guarantees a call can always reach linker-generated stubs ๐Ÿงš. By default, long calls only show up when the distance from the call site to the start of the function or translation unit (depending on the case) goes past a limit set by the branch type in use. For normal calls those limits are 7,600,000 bytes on PA 2.0 and 240,000 bytes on PA 1.X. Sibcalls are always capped at 240,000 bytes ๐Ÿ™ˆ.

Distances get measured from the beginning of functions when you use -ffunction-sections, or when you combine -mgas and -mno-portable-runtime under HP-UX with the SOM linker ๐Ÿ’œ.

Honestly? It's normally not a vibe to use this one, because it degrades performance ๐Ÿ˜ญ. It can still be useful in large applications, especially when partial linking is used to build the app ๐Ÿซถ.

The kind of long calls you get depends on what the assembler and linker can do, plus the type of code being generated. Systems that support long absolute calls, and long PIC symbol-difference or pc-relative calls, should only feel a small impact. BUT an indirect call is used on 32-bit ELF systems in PIC code, and it is quite long ๐Ÿ’€.

  • -mlong-load-store โ€” generates 3-instruction load and store sequences, which the HP-UX 10 linker sometimes needs ๐Ÿ“. It's the same thing as the +k option on the HP compilers.
  • -mjump-in-delay โ€” this one is ignored ๐Ÿ™ˆ. It's only here for compatibility purposes.
  • -mno-space-regs โ€” tells GCC to assume the target has no space registers ๐ŸŒธ. That lets GCC generate faster indirect calls and use unscaled index address modes โšก. This kind of code is suitable for level 0 PA systems and kernels ๐Ÿ‘‘.
  • -mordered โ€” assumes memory references are ordered, so barriers aren't needed ๐Ÿฆ‹.
  • -mportable-runtime โ€” uses the portable calling conventions HP proposed for ELF systems ๐ŸŒˆ.
  • -mschedule=cpu-type โ€” schedules code according to the constraints for the machine type cpu-type ๐ŸŽ€. The choices are 700, 7100, 7100LC, 7200, 7300 and 8000. If you're on an HP-UX system and want to figure out the right scheduling option for your machine, check out /usr/lib/sched.models ๐Ÿ”. The default scheduling is 8000.
  • -msio โ€” generates the predefine _SIO for server IO ๐Ÿ–ฅ๏ธ. The default is -mwsio, which instead generates the predefines __hp9000s700, __hp9000s700__ and _WSIO for workstation IO ๐Ÿ’ป. These options are available under HP-UX and HI-UX.
  • -msoft-float โ€” generates output that contains library calls for floating point ๐Ÿ”ข.
Warning: the requisite libraries are NOT available for all HPPA targets ๐Ÿšจ. Normally the facilities of the machine's usual C compiler get used, but that can't be done directly in cross-compilation, so you need to make your own arrangements to provide suitable library functions for cross-compilation ๐Ÿ˜ฌ.

-msoft-float changes the calling convention in the output file, so it's only useful if you compile all of a program with this option ๐Ÿซ . In particular, you need to compile libgcc.a (the library that comes with GCC) with -msoft-float for this to work.

  • -msoft-mult โ€” uses software integer multiplication โœ–๏ธ. This disables the use of the xmpyu instruction.
  • -munix=unix-std โ€” generates compiler predefines and selects a startfile for the specified UNIX standard ๐Ÿ“œ. The choices for unix-std are 93, 95 and 98. 93 is supported on all HP-UX versions. 95 is available on HP-UX 10.10 and later. 98 is available on HP-UX 11.11 and later. The defaults are 93 for HP-UX 10.00, 95 for HP-UX 10.10 through 11.00, and 98 for HP-UX 11.11 and later ๐Ÿ—“๏ธ.

-munix=93 provides the same predefines as GCC 3.3 and 3.4 ๐Ÿง. -munix=95 gives you extra predefines for XOPEN_UNIX and _XOPEN_SOURCE_EXTENDED, plus the startfile unix95.o. -munix=98 adds predefines for _XOPEN_UNIX, _XOPEN_SOURCE_EXTENDED, _INCLUDE__STDC_A1_SOURCE and _INCLUDE_XOPEN_SOURCE_500, plus the startfile unix98.o โœจ.

It is super important to know that this option changes the interfaces for various library routines ๐Ÿ™€. It also affects the operational behavior of the C library, so extreme care is needed when using it ๐Ÿซฃ.

Library code that's meant to work with more than one UNIX standard has to test, set and restore the variable __xpg4_extended_mask as appropriate ๐Ÿง . Most GNU software doesn't provide this capability, btw.

  • -nolibdld โ€” suppresses the generation of link options that search libdld.sl when -static is specified on HP-UX 10 and later ๐Ÿšซ.
  • -static โ€” okay so HP-UX's implementation of setlocale in libc depends on libdld.sl ๐Ÿงฉ, and there isn't an archive version of libdld.sl. That means when -static is specified, special link options are needed to resolve this dependency.

On HP-UX 10 and later, the GCC driver adds the necessary options to link with libdld.sl when -static is specified ๐Ÿ”—. This makes the resulting binary dynamic. On the 64-bit port, the linkers generate dynamic binaries by default anyway. If you want to stop the GCC driver from adding these link options, use -nolibdld ๐Ÿ™…โ€โ™€๏ธ.

  • -threads โ€” adds support for multithreading with the DCE thread library under HP-UX ๐Ÿงต. This option sets flags for both the preprocessor and the linker.

โœจ IA-64 Options โœจ

These are the -m options defined for the Intel IA-64 architecture ๐ŸŒŸ.

  • -mbig-endian โ€” generates code for a big-endian target ๐Ÿ˜. This is the default for HP-UX.
  • -mlittle-endian โ€” generates code for a little-endian target ๐Ÿฃ. This is the default for AIX5 and GNU/Linux.
  • -mgnu-as, -mno-gnu-as โ€” generates (or doesn't) code for the GNU assembler ๐Ÿ› ๏ธ. This is the default.
  • -mgnu-ld, -mno-gnu-ld โ€” generates (or doesn't) code for the GNU linker ๐Ÿ”ง. This is the default.
  • -mno-pic โ€” generates code that does not use a global pointer register ๐Ÿšซ. The result is not position independent code, and it violates the IA-64 ABI ๐Ÿ˜ค.
  • -mvolatile-asm-stop, -mno-volatile-asm-stop โ€” generates (or doesn't) a stop bit immediately before and after volatile asm statements ๐Ÿ›‘.
  • -mregister-names, -mno-register-names โ€” generates (or doesn't) in, loc, and out register names for the stacked registers ๐Ÿ“›. This may make assembler output more readable ๐Ÿ“–.
  • -mno-sdata, -msdata โ€” disables (or enables) optimizations that use the small data section ๐Ÿ“ฆ. This may be useful for working around optimizer bugs ๐Ÿ›.
  • -mconstant-gp โ€” generates code that uses a single constant global pointer value ๐Ÿ“Œ. This is useful when compiling kernel code ๐Ÿ‘‘.
  • -mauto-pic โ€” generates code that is self-relocatable ๐Ÿช„. This implies -mconstant-gp. It's useful when compiling firmware code ๐Ÿง‘โ€๐Ÿ’ป.
  • -minline-float-divide-min-latency โ€” generates code for inline divides of floating-point values using the minimum latency algorithm โฑ๏ธ.
  • -minline-float-divide-max-throughput โ€” generates code for inline divides of floating-point values using the maximum throughput algorithm ๐Ÿš€.
  • -mno-inline-float-divide โ€” does not generate inline code for divides of floating-point values ๐Ÿ™….
  • -minline-int-divide-min-latency โ€” generates code for inline divides of integer values using the minimum latency algorithm โฑ๏ธ.
  • -minline-int-divide-max-throughput โ€” generates code for inline divides of integer values using the maximum throughput algorithm ๐Ÿš€.
  • -mno-inline-int-divide โ€” does not generate inline code for divides of integer values ๐Ÿ™….
  • -minline-sqrt-min-latency โ€” generates code for inline square roots using the minimum latency algorithm โฑ๏ธ.
  • -minline-sqrt-max-throughput โ€” generates code for inline square roots using the maximum throughput algorithm ๐Ÿš€.
  • -mno-inline-sqrt โ€” does not generate inline code for sqrt ๐Ÿ™….
  • -mfused-madd, -mno-fused-madd โ€” does (or doesn't) generate code that uses the fused multiply/add or multiply/subtract instructions ๐Ÿงฎ. The default is to use these instructions ๐Ÿ’–.
  • -mno-dwarf2-asm, -mdwarf2-asm โ€” doesn't (or does) generate assembler code for the DWARF line number debugging info ๐Ÿ”. This may be useful when you're not using the GNU assembler.
  • -mearly-stop-bits, -mno-early-stop-bits โ€” allows stop bits to be placed earlier than immediately preceding the instruction that triggered the stop bit ๐Ÿ•ฐ๏ธ. This can improve instruction scheduling, but it doesn't always do so ๐Ÿคทโ€โ™€๏ธ.
  • -mfixed-range=register-range โ€” generates code treating the given register range as fixed registers ๐Ÿ“. A fixed register is one that the register allocator cannot use. This is useful when compiling kernel code. A register range is specified as two registers separated by a dash, and you can specify multiple register ranges separated by a comma ๐Ÿงฉ.
  • -mtls-size=tls-size โ€” specifies the bit size of immediate TLS offsets ๐Ÿ“. Valid values are 14, 22, and 64.
  • -mtune=cpu-type โ€” tunes the instruction scheduling for a particular CPU ๐ŸŽฏ. Valid values are itanium, itanium1, merced, itanium2, and mckinley.
  • -milp32, -mlp64 โ€” generates code for a 32-bit or 64-bit environment ๐ŸŒ™. The 32-bit environment sets int, long and pointer to 32 bits. The 64-bit environment sets int to 32 bits and long and pointer to 64 bits. These are HP-UX specific flags ๐ŸŽ.
  • -mno-sched-br-data-spec, -msched-br-data-spec โ€” (dis/en)ables data speculative scheduling before reload ๐Ÿ”ฎ. This results in generation of ld.a instructions and the corresponding check instructions (ld.c / chk.a). The default setting is disabled.
  • -msched-ar-data-spec, -mno-sched-ar-data-spec โ€” (en/dis)ables data speculative scheduling after reload ๐Ÿ”. This results in generation of ld.a instructions and the corresponding check instructions (ld.c / chk.a). The default setting is enabled ๐Ÿ’….
  • -mno-sched-control-spec, -msched-control-spec โ€” (dis/en)ables control speculative scheduling ๐Ÿง . This feature is only available during region scheduling (i.e. before reload). It results in generation of the ld.s instructions and the corresponding check instructions chk.s. The default setting is disabled ๐Ÿ™ˆ.

-msched-br-in-data-spec, -mno-sched-br-in-data-spec Okay so these two let you flip speculative scheduling for instructions that depend on data speculative loads before reload ๐Ÿ”ฅ. It only does anything if -msched-br-data-spec is turned on too. Default is enabled โœจ

  • -msched-ar-in-data-spec, -mno-sched-ar-in-data-spec โ€” Same energy, but this one controls speculative scheduling of those data-speculation-dependent instructions after reload ๐Ÿงš. It only works when -msched-ar-data-spec is enabled. Default is enabled ๐Ÿ’…
  • -msched-in-control-spec, -mno-sched-in-control-spec โ€” Toggles speculative scheduling for instructions that depend on the control speculative loads ๐Ÿฆ‹. This is only effective with -msched-control-spec enabled. Default is enabled ๐Ÿ’–
  • -mno-sched-prefer-non-data-spec-insns, -msched-prefer-non-data-spec-insns โ€” When enabled, data-speculative instructions only get picked for scheduling if there's literally nothing else on the table right now ๐Ÿ™ˆ. It makes data speculation way more conservative. Default is disabled.
  • -mno-sched-prefer-non-control-spec-insns, -msched-prefer-non-control-spec-insns โ€” Same vibe as above, but for control-speculative instructions ๐ŸŒˆ. They only get chosen when there are no other options at the moment, so control speculation gets way more cautious. Default is disabled.
  • -mno-sched-count-spec-in-critical-path, -msched-count-spec-in-critical-path โ€” When enabled, speculative dependencies get counted when computing instruction priorities ๐ŸŽ€. It makes speculation a bit more conservative. Default is disabled.
  • -msched-spec-ldc โ€” Uses a simple data speculation check ๐Ÿ“. On by default.
  • -msched-control-spec-ldc โ€” Uses a simple check for control speculation ๐ŸŒธ. On by default.
  • -msched-stop-bits-after-every-cycle โ€” Drops a stop bit after every cycle when scheduling โญ. On by default.
  • -msched-fp-mem-deps-zero-cost โ€” Assumes floating-point stores and loads aren't likely to conflict when they land in the same instruction group ๐Ÿซถ. Disabled by default.
  • -msel-sched-dont-check-control-spec โ€” Generates checks for control speculation in selective scheduling ๐Ÿง . Disabled by default.
  • -msched-max-memory-insns=max-insns โ€” Sets a limit on how many memory insns can go in one instruction group โœจ. Later memory insns trying to squeeze into the same group get lower priority. This is super handy for dodging cache bank conflicts ๐Ÿ’œ. The default value is 1.
  • -msched-max-memory-insns-hard-limit โ€” Turns the limit from -msched-max-memory-insns into a hard limit ๐Ÿ”’, so you can't go over that number in an instruction group. Without it, the limit is "soft": non-memory operations get preferred once the limit is hit, but memory operations can still get scheduled anyway ๐Ÿฆ‹.

๐ŸŒธ LM32 Options ๐ŸŒธ

These -m options are defined for the LatticeMico32 architecture ๐Ÿ“:

  • -mbarrel-shift-enabled โ€” Enables barrel-shift instructions ๐Ÿ’….
  • -mdivide-enabled โ€” Enables divide and modulus instructions โœจ.
  • -mmultiply-enabled โ€” Enables multiply instructions ๐Ÿ”ฅ.
  • -msign-extend-enabled โ€” Enables sign extend instructions ๐ŸŒˆ.
  • -muser-enabled โ€” Enables user-defined instructions ๐Ÿ‘‘.

๐Ÿฆ‹ LoongArch Options ๐Ÿฆ‹

These command-line options are defined for LoongArch targets ๐Ÿ’–:

  • -march=arch-type โ€” Generates instructions for the machine type arch-type ๐Ÿงš. Heads up bestie: -march=arch-type lets GCC generate code that might not run at all on processors other than the one you named ๐Ÿ™ˆ.
Be careful with this one, the code may not run at all on other chips!

The choices for arch-type are:

  • native โ€” The local processor type, detected by the native compiler ๐Ÿ’ป.
  • loongarch64 โ€” Generic LoongArch 64-bit processor ๐ŸŒธ.
  • la464 โ€” LoongArch LA464-based processor with LSX and LASX ๐Ÿ’œ.
  • la664 โ€” LoongArch LA664-based processor with LSX, LASX, and all LoongArch v1.1 instructions ๐Ÿ”ฅ.
  • la64v1.0 โ€” LoongArch64 ISA version 1.0 ๐ŸŽ€.
  • la64v1.1 โ€” LoongArch64 ISA version 1.1 โœจ.

More info on the LoongArch ISA versions is over at https://github.com/loongson/la-toolchain-conventions ๐Ÿ“š.

  • -mtune=tune-type โ€” Optimizes the generated code for the given processor target ๐Ÿ“. The choices for tune-type are:
    • native โ€” The local processor type, detected by the native compiler ๐Ÿ’….
    • generic โ€” Generic LoongArch processor ๐ŸŒˆ.
    • loongarch64 โ€” Generic LoongArch 64-bit processor ๐Ÿ‘‘.
    • la464 โ€” LoongArch LA464 core โญ.
    • la664 โ€” LoongArch LA664 core ๐Ÿฆ‹.
  • -mabi=base-abi-type โ€” Generates code for the calling convention you specify ๐Ÿซถ. base-abi-type can be one of:
    • lp64d โ€” Uses 64-bit general purpose registers and 32/64-bit floating-point registers for parameter passing ๐Ÿ’–. The data model is LP64, where int is 32 bits, while long int and pointers are 64 bits.
    • lp64f โ€” Uses 64-bit general purpose registers and 32-bit floating-point registers for parameter passing ๐ŸŒธ. The data model is LP64, where int is 32 bits, while long int and pointers are 64 bits.
    • lp64s โ€” Uses 64-bit general purpose registers and no floating-point registers for parameter passing ๐Ÿ™ˆ. The data model is LP64, where int is 32 bits, while long int and pointers are 64 bits.
  • -mfpu=fpu-type โ€” Generates code for the FPU type you pick, which can be one of ๐Ÿ”ฅ:
    • 64 โ€” Allows hardware floating-point instructions for 32-bit and 64-bit operations ๐Ÿ’œ.
    • 32 โ€” Allows hardware floating-point instructions for 32-bit operations ๐Ÿงš.
    • none or 0 โ€” Prevents the use of hardware floating-point instructions ๐Ÿšซ.
  • -msimd=simd-type โ€” Turns on LoongArch SIMD instructions for vectorization and builtin functions ๐ŸŒˆ. The value can be one of:
    • lasx โ€” Enables instructions from the 256-bit LoongArch Advanced SIMD Extension (LASX) and the 128-bit LoongArch SIMD Extension (LSX) โœจ.
    • lsx โ€” Enables instructions from the 128-bit LoongArch SIMD Extension (LSX) only ๐ŸŽ€.
    • none โ€” No LoongArch SIMD instruction may be generated ๐Ÿ™….
  • -msoft-float โ€” Forces -mfpu=none and stops floating-point registers from being used for parameter passing ๐Ÿซง. This can change the target ABI.
  • -msingle-float โ€” Forces -mfpu=32 and lets you use 32-bit floating-point registers for parameter passing ๐Ÿฆ‹. This can change the target ABI.
  • -mdouble-float โ€” Forces -mfpu=64 and lets you use 32/64-bit floating-point registers for parameter passing ๐Ÿ’…. This can change the target ABI.
  • -mlasx, -mno-lasx, -mlsx, -mno-lsx โ€” These tweak the SIMD extension scope (none / LSX / LASX) the compiler can use for code generation, one step at a time ๐Ÿ“. Turning on LASX with -mlasx automatically turns on LSX too, and turning off LSX with -mno-lsx automatically turns off LASX. These are driver-only options ๐Ÿš—: they act on the final -msimd configuration and make incremental changes in the order they show up on the GCC driver's command line. From that, they figure out the final, canonicalized -msimd option that gets passed to the compiler proper ๐Ÿง .
  • -mbranch-cost=n โ€” Sets the cost of branches to roughly n instructions โญ.
  • -maddr-reg-reg-cost=n โ€” Sets the cost of ADDRESS_REG_REG to the value calculated by n ๐ŸŒธ.
  • -mcheck-zero-division, -mno-check-zero-divison โ€” Traps (or doesn't trap) on integer division by zero ๐Ÿ’€. The default is -mcheck-zero-division for -O0 or -Og, and -mno-check-zero-division for other optimization levels โœจ.
  • -mcond-move-int, -mno-cond-move-int โ€” Conditional moves for integral data in general-purpose registers get enabled (or disabled) ๐ŸŒˆ. The default is -mcond-move-int.
  • -mcond-move-float, -mno-cond-move-float โ€” Conditional moves for floating-point registers get enabled (or disabled) ๐Ÿฆ‹. The default is -mcond-move-float.
  • -mmemcpy, -mno-memcpy โ€” Forces (or doesn't force) the use of memcpy for non-trivial block moves ๐Ÿ’–. The default is -mno-memcpy, which lets GCC inline most constant-sized copies. Setting the optimization level to -Os also forces the use of memcpy, but -mno-memcpy can override that if you explicitly set it, no matter what order these options show up in on the command line ๐Ÿซถ.
  • -mstrict-align, -mno-strict-align โ€” Avoids (or allows) generating memory accesses that may not be aligned on a natural object boundary, as described in the architecture specification ๐Ÿ™ˆ. The default is -mno-strict-align.
  • -G num โ€” Puts global and static data smaller than num bytes into a small data section ๐Ÿ“. The default value is 0.
  • -mmax-inline-memcpy-size=n โ€” Inlines all block moves (like calls to memcpy or structure copies) that are less than or equal to n bytes ๐Ÿ”ฅ. The default value of n is 1024.
  • -mcmodel=code-model โ€” Sets the code model to one of these ๐Ÿ‘‘:
    • tiny-static โ€” (Not implemented yet) ๐Ÿšง
    • tiny โ€” (Not implemented yet) ๐Ÿšง
    • normal โ€” The text segment must be within 128MB of addressing space ๐ŸŒธ. The data segment must be within 2GB of addressing space.
    • medium โ€” The text segment and data segment must both be within 2GB of addressing space ๐Ÿ’œ.
    • large โ€” (Not implemented yet) ๐Ÿšง
    • extreme โ€” This mode doesn't limit the size of the code segment or the data segment ๐ŸŒˆ. -mcmodel=extreme is incompatible with -fplt and/or -mexplicit-relocs=none.
The -mcmodel=extreme option clashes with -fplt and/or -mexplicit-relocs=none, so don't pair them! ๐Ÿ’€

The default code model is normal ๐Ÿ’–.

  • -mexplicit-relocs=style โ€” okay so this one decides when GCC uses assembler relocation operators for symbolic addresses ๐Ÿ’… The other option is assembler macros, which can limit instruction scheduling but still let the linker relax things โœจ
    • -mexplicit-relocs=none means the assembler macros are always used.
    • -mexplicit-relocs=always means the relocation operators are always used.
    • -mexplicit-relocs=auto means the compiler uses relocation operators wherever linker relaxation is impossible, to improve code quality, and macros everywhere else.
    • The default is picked from what the assembler could do when GCC was built, plus the setting of -mrelax. It's -mexplicit-relocs=none if the assembler doesn't support relocation operators at all, -mexplicit-relocs=always if it supports them but -mrelax isn't enabled, and -mexplicit-relocs=auto if it supports them and -mrelax is enabled.
  • -mexplicit-relocs โ€” an alias of -mexplicit-relocs=always, kept around for backward compatibility ๐Ÿซถ
  • -mno-explicit-relocs โ€” an alias of -mexplicit-relocs=none, also just for backward compatibility ๐Ÿ™ˆ
  • -mdirect-extern-access, -mno-direct-extern-access โ€” these decide whether the GOT gets used to access external symbols ๐Ÿฆ‹ The default is -mno-direct-extern-access, so the GOT is used for external symbols with default visibility but not for other external symbols.
    • With -mdirect-extern-access, the GOT isn't used at all and every external symbol is addressed PC-relatively. This only suits environments where no dynamic linking happens, like firmware, OS kernels, or executables linked with -static or -static-pie.
    • -mdirect-extern-access is not compatible with -fPIC or -fpic.
Heads up bestie: -mdirect-extern-access doesn't work alongside -fPIC or -fpic, so don't mix them ๐Ÿ’€
  • -mrelax, -mno-relax โ€” take advantage of linker relaxations, or don't ๐ŸŒธ If -mpass-mrelax-to-as is enabled, this option also gets passed to the assembler. The default is worked out when GCC is built by checking for assembler support. It's -mrelax if the assembler supports both the -mrelax option and conditional branch relaxation (you actually need that part, because the .align directives and conditional branch instructions in GCC's assembly output can get rejected with a relocation overflow otherwise). It's -mno-relax otherwise.
  • -mpass-mrelax-to-as, -mno-pass-mrelax-to-as โ€” pass (or don't pass) the -mrelax or -mno-relax option along to the assembler โœจ The default is decided at GCC build-time by checking assembler support: -mpass-mrelax-to-as if the assembler supports the -mrelax option, and -mno-pass-mrelax-to-as otherwise. This one is mostly handy for debugging, or for working with assemblers that aren't the one from build-time ๐Ÿงš
  • -mrecip โ€” this turns on the reciprocal estimate and reciprocal square root estimate instructions, with extra Newton-Raphson steps to boost precision, instead of doing a divide or a square root and divide on floating-point arguments ๐Ÿ”ฅ These instructions only get generated when -funsafe-math-optimizations is enabled together with -ffinite-math-only and -fno-trapping-math. It's off by default. Before you use it, you need to make sure the target CPU supports frecipe and frsqrte instructions. Also note that although the sequence has higher throughput than the non-reciprocal instruction, its precision can drop by up to 2 ulp (for example, the inverse of 1.0 comes out as 0.99999994) ๐Ÿ˜ญ
  • -mrecip=opt โ€” this controls which reciprocal estimate instructions may be used ๐Ÿ‘‘ opt is a comma-separated list, and any item can be preceded by a ! to invert it:
    • all โ€” enable all estimate instructions.
    • default โ€” enable the default instructions, which is the same as -mrecip.
    • none โ€” disable all estimate instructions, the same as -mno-recip.
    • div โ€” enable the approximation for scalar division.
    • vec-div โ€” enable the approximation for vectorized division.
    • sqrt โ€” enable the approximation for scalar square root.
    • vec-sqrt โ€” enable the approximation for vectorized square root.
    • rsqrt โ€” enable the approximation for scalar reciprocal square root.
    • vec-rsqrt โ€” enable the approximation for vectorized reciprocal square root.

So, for example, -mrecip=all,!sqrt turns on every reciprocal approximation except scalar square root ๐Ÿ’–

  • -mfrecipe, -mno-frecipe โ€” use (or don't use) the "frecipe.{s/d}" and "frsqrte.{s/d}" instructions โœจ When building with -march=la664, it's enabled by default. The default is -mno-frecipe.
  • -mdiv32, -mno-div32 โ€” use (or don't use) the "div.w[u]" and "mod.w[u]" instructions with input that isn't sign-extended ๐ŸŒˆ When building with -march=la664, it's enabled by default. The default is -mno-div32.
  • -mlam-bh, -mno-lam-bh โ€” use (or don't use) the "am{swap/add}[_db].{b/h}" instructions ๐Ÿ“ When building with -march=la664, it's enabled by default. The default is -mno-lam-bh.
  • -mlamcas, -mno-lamcas โ€” use (or don't use) the "amcas[_db].{b/h/w/d}" instructions ๐Ÿ’œ When building with -march=la664, it's enabled by default. The default is -mno-lamcas.
  • -mld-seq-sa, -mno-ld-seq-sa โ€” this decides whether a same-address load-load barrier ("dbar 0x700") is needed ๐Ÿ™ˆ When building with -march=la664, it's enabled by default. The default is -mno-ld-seq-sa, which means the load-load barrier is needed.
  • -mtls-dialect=opt โ€” controls which TLS dialect gets used for the general dynamic and local dynamic TLS models ๐Ÿฆ‹
    • trad โ€” use traditional TLS. This is the default.
    • desc โ€” use TLS descriptors.
  • -mannotate-tablejump, -mno-annotate-tablejump โ€” creates an annotation section called ".discard.tablejump_annotate" that correlates the jirl instruction with the jump table, for when a jump table is used to optimize a switch statement ๐ŸŽ€ Some external tools, like objtool from the Linux kernel build system, need that annotation to analyze control flow. The default is -mno-annotate-tablejump.
  • --param loongarch-vect-unroll-limit=n โ€” the vectorizer uses available tuning info to decide whether unrolling the main vectorized loop would be worth it, and by how much ๐Ÿš€ This parameter sets the upper bound on how much the vectorizer will unroll that main loop. The default value is six.

โœจ M32C Options โœจ

  • -mcpu=name โ€” pick the CPU you're generating code for ๐Ÿ’… name can be r8c for the R8C/Tiny series, m16c for the M16C (up to /60) series, m32cm for the M16C/80 series, or m32c for the M32C/80 series.
  • -msim โ€” tells GCC the program will run on the simulator ๐Ÿงš This links in an alternate runtime library that supports things like file I/O. You must not use this option for programs that will run on real hardware! For that, you need to provide your own runtime library for whatever I/O functions you need ๐Ÿ’€
  • -memregs=number โ€” sets how many memory-based pseudo-registers GCC uses during code generation ๐Ÿซถ These get used like real registers, so there's a tradeoff between GCC fitting your code into the available registers and the performance cost of using memory instead. Every module in a program must be compiled with the same value for this option. Because of that, you must not use this option with GCC's default runtime libraries ๐Ÿ˜ญ

โœจ M32R/D Options โœจ

These -m options are defined for Renesas M32R/D architectures ๐ŸŒธ

  • -m32r2 โ€” generate code for the M32R/2 ๐Ÿฆ‹
  • -m32rx โ€” generate code for the M32R/X ๐Ÿฆ‹
  • -m32r โ€” generate code for the M32R. This is the default ๐Ÿฆ‹
  • -mmodel=small โ€” assumes all objects live in the lower 16MB of memory (so their addresses can be loaded with the "ld24" instruction), and that all subroutines are reachable with the "bl" instruction. This is the default ๐Ÿ’–
    • The addressability of a particular object can be set with the "model" attribute.
  • -mmodel=medium โ€” assumes objects may be anywhere in the 32-bit address space (the compiler generates "seth/add3" instructions to load their addresses), and that all subroutines are reachable with the "bl" instruction ๐ŸŒˆ
  • -mmodel=large โ€” assumes objects may be anywhere in the 32-bit address space (the compiler generates "seth/add3" instructions to load their addresses), and that subroutines may not be reachable with the "bl" instruction. The compiler then generates the much slower "seth/add3/jl" instruction sequence ๐Ÿข
  • -msdata=none โ€” disables the small data area โœจ Variables get put into one of ".data", ".bss", or ".rodata" (unless the "section" attribute is specified). This is the default.
    • The small data area consists of the ".sdata" and ".sbss" sections. Objects can be explicitly put in the small data area with the "section" attribute using one of these sections.
  • -msdata=sdata โ€” puts small global and static data in the small data area, but doesn't generate any special code to reference them ๐Ÿงš
  • -msdata=use โ€” puts small global and static data in the small data area and generates special instructions to reference them ๐Ÿ”ฅ
  • -G num โ€” puts global and static objects less than or equal to num bytes into the small data or BSS sections, instead of the normal data or BSS sections ๐ŸŽ€ The default value of num is 8. The -msdata option must be set to sdata or use for this option to do anything.
    • Every module should be compiled with the same -G num value. Compiling with different values of num may or may not work. If it doesn't, the linker gives an error message, so incorrect code is never generated ๐Ÿซถ
  • -mdebug โ€” makes the M32R-specific code in the compiler display some statistics that might help you debug your programs ๐Ÿ”
  • -malign-loops โ€” aligns all loops to a 32-byte boundary โœจ
  • -mno-align-loops โ€” doesn't enforce a 32-byte alignment for loops. This is the default ๐Ÿ™ˆ
  • -missue-rate=number โ€” issues number instructions per cycle. number can only be 1 or 2 โšก
  • -mbranch-cost=number โ€” number can only be 1 or 2. If it's 1, branches are preferred over conditional code. If it's 2, the opposite applies ๐ŸŒธ

-mflush-trap=number is where we start, bestie, and it's all about picking which trap number gets used to flush the cache ๐Ÿงš. The default is 12, and valid numbers run from 0 to 15 inclusive ๐Ÿ’….

  • -mflush-trap=number โ€” pick the trap number used to flush the cache ๐ŸŒธ. Default is 12, and anything from 0 to 15 inclusive is valid โœจ.
  • -mno-flush-trap โ€” tells GCC the cache can't be flushed with a trap at all ๐Ÿ™ˆ.
  • -mflush-func=name โ€” names the operating system function that gets called to flush the cache ๐Ÿฆ‹. The default is _flush_cache, but the function call only happens if a trap isn't available ๐Ÿ’€.
  • -mno-flush-func โ€” says there's no OS function for flushing the cache, so don't even look for one ๐Ÿ˜ญ.

๐ŸŽ€ M680x0 Options ๐ŸŽ€

okay so these are the -m options for M680x0 and ColdFire processors ๐Ÿ’–. The defaults depend on which architecture got picked when the compiler was configured, so I've listed the defaults for the most common choices below ๐Ÿซถ.

  • -march=arch โ€” generates code for a specific M680x0 or ColdFire instruction set architecture โœจ. For M680x0, the permissible values of arch are 68000, 68010, 68020, 68030, 68040, 68060 and cpu32. ColdFire architectures get picked using Freescale's ISA classification, so the options are isaa, isaaplus, isab and isac ๐Ÿ”ฅ.

GCC defines a macro "__mcfarch__" whenever it's generating code for a ColdFire target ๐ŸŒˆ. The arch in that macro is one of the -march arguments listed above ๐Ÿ’œ.

When you use -march and -mtune together, you get code that runs on a family of similar processors but is tuned for one particular microarchitecture ๐Ÿ‘‘.

  • -mcpu=cpu โ€” generates code for a specific M680x0 or ColdFire processor ๐Ÿงš. The M680x0 cpus are 68000, 68010, 68020, 68030, 68040, 68060, 68302, 68332 and cpu32 ๐Ÿ“. The ColdFire cpus are in the table below, which also sorts each CPU into a family ๐Ÿ’…:
Family : -mcpu arguments
51 : 51 51ac 51ag 51cn 51em 51je 51jf 51jg 51jm 51mm 51qe 51qm
5206 : 5202 5204 5206
5206e : 5206e
5208 : 5207 5208
5211a : 5210a 5211a
5213 : 5211 5212 5213
5216 : 5214 5216
52235 : 52230 52231 52232 52233 52234 52235
5225 : 5224 5225
52259 : 52252 52254 52255 52256 52258 52259
5235 : 5232 5233 5234 5235 523x
5249 : 5249
5250 : 5250
5271 : 5270 5271
5272 : 5272
5275 : 5274 5275
5282 : 5280 5281 5282 528x
53017 : 53011 53012 53013 53014 53015 53016 53017
5307 : 5307
5329 : 5327 5328 5329 532x
5373 : 5372 5373 537x
5407 : 5407
5475 : 5470 5471 5472 5473 5474 5475 547x 5480 5481 5482 5483 5484
5485

Okay, the main rule here: -mcpu=cpu overrides -march=arch if arch is compatible with cpu โœจ. Any other combos of -mcpu and -march get rejected ๐Ÿ˜ญ.

GCC defines the macro "__mcf_cpu_cpu" when a ColdFire target cpu is selected ๐Ÿฆ‹. It also defines "__mcf_family_family", where the value of family comes from the table above ๐Ÿซถ.

  • -mtune=tune โ€” tunes the code for a particular microarchitecture, but only within the limits set by -march and -mcpu ๐Ÿ’–. The M680x0 microarchitectures are 68000, 68010, 68020, 68030, 68040, 68060 and cpu32 โœจ. The ColdFire microarchitectures are cfv1, cfv2, cfv3, cfv4 and cfv4e ๐ŸŒˆ.

You can also use -mtune=68020-40 for code that needs to run pretty well on 68020, 68030 and 68040 targets ๐Ÿงš. -mtune=68020-60 is similar but also covers 68060 targets ๐Ÿ”ฅ. These two pick the same tuning decisions as -m68020-40 and -m68020-60 respectively ๐Ÿ‘‘.

GCC defines the macros "__mcarch" and "__mcarch__" when it's tuning for 680x0 architecture arch ๐Ÿ’œ. It also defines "mcarch" unless either -ansi or a non-GNU -std option is used ๐Ÿ™ˆ. If GCC is tuning for a range of architectures, like with -mtune=68020-40 or -mtune=68020-60, it defines the macros for every architecture in that range ๐Ÿ“.

GCC also defines the macro "__muarch__" when it's tuning for ColdFire microarchitecture uarch, where uarch is one of the arguments listed above ๐ŸŒธ.

  • -m68000, -mc68000 โ€” generates output for a 68000 ๐ŸŽ€. This is the default when the compiler is configured for 68000-based systems ๐Ÿฆ‹. It's equivalent to -march=68000 ๐Ÿ’….

Use this one for microcontrollers with a 68000 or EC000 core, including the 68008, 68302, 68306, 68307, 68322, 68328 and 68356 โœจ.

  • -m68010 โ€” generates output for a 68010 ๐ŸŒˆ. This is the default when the compiler is configured for 68010-based systems ๐Ÿ’–. It's equivalent to -march=68010 ๐Ÿงš.
  • -m68020, -mc68020 โ€” generates output for a 68020 ๐Ÿ”ฅ. This is the default when the compiler is configured for 68020-based systems ๐Ÿ‘‘. It's equivalent to -march=68020 ๐Ÿ’œ.
  • -m68030 โ€” generates output for a 68030 ๐Ÿ™ˆ. This is the default when the compiler is configured for 68030-based systems ๐Ÿ“. It's equivalent to -march=68030 ๐ŸŒธ.
  • -m68040 โ€” generates output for a 68040 ๐ŸŽ€. This is the default when the compiler is configured for 68040-based systems ๐Ÿฆ‹. It's equivalent to -march=68040 ๐Ÿ’….

This option stops the use of 68881/68882 instructions that have to be emulated in software on the 68040 โœจ. Use it if your 68040 doesn't have code to emulate those instructions ๐Ÿ˜ญ.

  • -m68060 โ€” generates output for a 68060 ๐ŸŒˆ. This is the default when the compiler is configured for 68060-based systems ๐Ÿ’–. It's equivalent to -march=68060 ๐Ÿงš.

This option stops the use of 68020 and 68881/68882 instructions that have to be emulated in software on the 68060 ๐Ÿ”ฅ. Use it if your 68060 doesn't have code to emulate those instructions ๐Ÿ‘‘.

  • -mcpu32 โ€” generates output for a CPU32 ๐Ÿ’œ. This is the default when the compiler is configured for CPU32-based systems ๐Ÿ™ˆ. It's equivalent to -march=cpu32 ๐Ÿ“.

Use this for microcontrollers with a CPU32 or CPU32+ core, including the 68330, 68331, 68332, 68333, 68334, 68336, 68340, 68341, 68349 and 68360 ๐ŸŒธ.

  • -m5200 โ€” generates output for a 520X ColdFire CPU ๐ŸŽ€. This is the default when the compiler is configured for 520X-based systems ๐Ÿฆ‹. It's equivalent to -mcpu=5206, and it's now deprecated in favor of that option ๐Ÿ’€.

Use this for microcontrollers with a 5200 core, including the MCF5202, MCF5203, MCF5204 and MCF5206 โœจ.

  • -m5206e โ€” generates output for a 5206e ColdFire CPU ๐ŸŒˆ. It's now deprecated in favor of the equivalent -mcpu=5206e ๐Ÿ˜ญ.
  • -m528x โ€” generates output for a member of the ColdFire 528X family ๐Ÿงš. It's now deprecated in favor of the equivalent -mcpu=528x ๐Ÿซถ.
  • -m5307 โ€” generates output for a ColdFire 5307 CPU ๐Ÿ’…. It's now deprecated in favor of the equivalent -mcpu=5307 ๐Ÿ™ˆ.
  • -m5407 โ€” generates output for a ColdFire 5407 CPU ๐Ÿ”ฅ. It's now deprecated in favor of the equivalent -mcpu=5407 ๐Ÿ’–.
  • -mcfv4e โ€” generates output for a ColdFire V4e family CPU, like 547x/548x ๐Ÿ‘‘. This includes hardware floating-point instructions ๐Ÿ’œ. It's equivalent to -mcpu=547x, and it's now deprecated in favor of that option ๐ŸŒธ.
  • -m68020-40 โ€” generates output for a 68040 without using any of the new instructions ๐Ÿฆ‹. This gives code that runs pretty efficiently on a 68020/68881, a 68030 or a 68040 โœจ. The generated code does use the 68881 instructions that get emulated on the 68040 ๐ŸŒˆ.

This option is equivalent to -march=68020 -mtune=68020-40 ๐Ÿ’….

  • -m68020-60 โ€” generates output for a 68060 without using any of the new instructions ๐ŸŽ€. This gives code that runs pretty efficiently on a 68020/68881, a 68030 or a 68040 ๐Ÿงš. The generated code does use the 68881 instructions that get emulated on the 68060 ๐Ÿ’–.

This option is equivalent to -march=68020 -mtune=68020-60 ๐Ÿ”ฅ.

  • -mhard-float, -m68881 โ€” generates floating-point instructions ๐Ÿ‘‘. This is the default for 68020 and above, and for ColdFire devices that have an FPU ๐Ÿ’œ. It defines the macro "__HAVE_68881__" on M680x0 targets and "__mcffpu__" on ColdFire targets ๐Ÿ™ˆ.
  • -msoft-float โ€” doesn't generate floating-point instructions, and uses library calls instead ๐Ÿ“. This is the default for 68000, 68010, and 68832 targets ๐ŸŒธ. It's also the default for ColdFire devices that have no FPU ๐ŸŽ€.
  • -mdiv, -mno-div โ€” generates (or doesn't generate) ColdFire hardware divide and remainder instructions ๐Ÿฆ‹. If -march is used without -mcpu, the default is "on" for ColdFire architectures and "off" for M680x0 architectures โœจ. Otherwise, the default comes from the target CPU, either the default CPU or the one picked with -mcpu ๐ŸŒˆ. For example, the default is "off" for -mcpu=5206 and "on" for -mcpu=5206e ๐Ÿ’….

GCC defines the macro "__mcfhwdiv__" when this option is enabled ๐Ÿงš.

  • -mshort โ€” considers type "int" to be 16 bits wide, like "short int" ๐Ÿ’–. Also, parameters passed on the stack get aligned to a 16-bit boundary, even on targets whose API says they should be promoted to 32-bit ๐Ÿ”ฅ.
  • -mno-short โ€” doesn't consider type "int" to be 16 bits wide ๐Ÿ‘‘. This is the default ๐Ÿ’œ.
  • -mnobitfield, -mno-bitfield โ€” doesn't use the bit-field instructions ๐Ÿ™ˆ. The -m68000, -mcpu32 and -m5200 options imply -mnobitfield ๐Ÿ“.
  • -mbitfield โ€” does use the bit-field instructions ๐ŸŒธ. The -m68020 option implies -mbitfield ๐ŸŽ€. This is the default if you use a configuration designed for a 68020 ๐Ÿฆ‹.
  • -mrtd โ€” uses a different function-calling convention โœจ. In this one, functions that take a fixed number of arguments return with the "rtd" instruction, which pops their arguments as it returns ๐ŸŒˆ. That saves one instruction in the caller, since there's no need to pop the arguments there ๐Ÿ’….
โš ๏ธ This calling convention is incompatible with the one normally used on Unix, so you can't use it if you need to call libraries compiled with the Unix compiler ๐Ÿ˜ญ.

Also, you must provide function prototypes for all functions that take variable numbers of arguments, including "printf". Otherwise, incorrect code gets generated for calls to those functions ๐Ÿ’€.

In addition, seriously incorrect code results if you call a function with too many arguments ๐Ÿ™ˆ. (Normally, extra arguments are harmlessly ignored ๐Ÿซถ.)

The "rtd" instruction is supported by the 68010, 68020, 68030, 68040, 68060 and CPU32 processors, but not by the 68000 or 5200 ๐Ÿงš.

The default is -mno-rtd ๐Ÿ’–.

โœจ MACHINE-SPECIFIC OPTIONS โœจ

  • -malign-int, -mno-align-int โ€” okay so these decide whether GCC lines up int, long, long long, float, double, and long double variables on a 32-bit boundary (-malign-int) or a 16-bit boundary (-mno-align-int) ๐ŸŽ€. Aligning to 32 bits makes the code run a bit faster on processors with 32-bit busses, but it eats more memory ๐Ÿ’พ
โš ๏ธ bestie, heads up: if you use -malign-int, GCC lines up structs holding those types differently than most published application binary interface specs for the m68k. Watch out for that one ๐Ÿ™ˆ
  • -mpcrel โ€” this one tells the 68000 to use its pc-relative addressing mode directly, instead of going through a global offset table ๐Ÿฆ‹. Right now it implies -fpic, which allows at most a 16-bit offset for pc-relative addressing. -fPIC isn't supported with -mpcrel at the moment, though that could change for 68020 and higher processors โœจ
  • -mno-strict-align, -mstrict-align โ€” -mno-strict-align means GCC does not assume unaligned memory references get handled by the system, and -mstrict-align means it does assume that ๐Ÿ’…
  • -msep-data โ€” generates code that lets the data segment live in a different area of memory from the text segment ๐Ÿงš This allows execute-in-place in an environment without virtual memory management. It implies -fPIC.
  • -mno-sep-data โ€” generates code that assumes the data segment follows the text segment. This is the default ๐Ÿ’œ
  • -mid-shared-library โ€” generates code that supports shared libraries through the library ID method ๐ŸŒธ This allows execute-in-place and shared libraries in an environment without virtual memory management. It implies -fPIC.
  • -mno-id-shared-library โ€” generates code that doesn't assume ID-based shared libraries are being used. This is the default ๐Ÿซถ
  • -mshared-library-id=n โ€” specifies the identification number of the ID-based shared library being compiled ๐Ÿ“ Specifying 0 gives you more compact code. Other values force that number onto the current library, but that's no more space- or time-efficient than just leaving the option out.
  • -mxgot, -mno-xgot โ€” when generating position-independent code for ColdFire, -mxgot makes code that works even if the GOT has more than 8192 entries ๐ŸŒˆ It's larger and slower, though. On M680x0 processors this option isn't needed, because -fPIC is enough โœจ

GCC normally uses a single instruction to load values from the GOT. That's pretty efficient, but it only works if the GOT is smaller than about 64k. Anything bigger makes the linker throw an error that looks like this:

relocation truncated to fit: R_68K_GOT16O foobar

If you see that, recompile your code with -mxgot. It should then work with very large GOTs ๐Ÿ’– Just know that code made with -mxgot is less efficient, since it takes 4 instructions to fetch the value of a global symbol ๐Ÿ˜ญ

Some linkers, including newer versions of the GNU linker, can create multiple GOTs and sort GOT entries. If you've got one of those, you should only need -mxgot when compiling a single object file that accesses more than 8192 GOT entries. Very few do ๐Ÿ‘‘

These options have no effect unless GCC is generating position-independent code ๐Ÿ™ˆ

  • -mlong-jump-table-offsets โ€” uses 32-bit offsets in switch tables. The default is 16-bit offsets ๐Ÿ“

โœจ MCore Options โœจ

These are the -m options defined for the Motorola M*Core processors ๐Ÿ’…

  • -mhardlit, -mno-hardlit โ€” inlines constants into the code stream if it can be done in two instructions or fewer ๐Ÿ”ฅ
  • -mdiv, -mno-div โ€” uses the divide instruction. It's enabled by default ๐Ÿฆ‹
  • -mrelax-immediate, -mno-relax-immediate โ€” allows arbitrary-sized immediates in bit operations ๐Ÿงš
  • -mwide-bitfields, -mno-wide-bitfields โ€” always treats bit-fields as "int"-sized ๐ŸŽ€
  • -m4byte-functions, -mno-4byte-functions โ€” forces all functions to be aligned to a 4-byte boundary ๐ŸŒธ
  • -mcallgraph-data, -mno-callgraph-data โ€” emits callgraph information ๐Ÿ’œ
  • -mslow-bytes, -mno-slow-bytes โ€” prefers word access when reading byte quantities โญ
  • -mlittle-endian, -mbig-endian โ€” generates code for a little-endian target ๐ŸŒˆ
  • -m210, -m340 โ€” generates code for the 210 processor ๐Ÿ’…
  • -mno-lsim โ€” assumes runtime support has been provided, so the simulator library (libsim.a) gets left off the linker command line ๐Ÿซถ
  • -mstack-increment=size โ€” sets the maximum amount for a single stack increment operation โœจ Big values can speed up programs with functions that need a lot of stack space, but they can also cause a segmentation fault if the stack gets extended too much ๐Ÿ’€ The default value is 0x1000.

โœจ MicroBlaze Options โœจ

  • -msoft-float โ€” uses software emulation for floating point. This is the default ๐ŸŒธ
  • -mhard-float โ€” uses hardware floating-point instructions ๐Ÿ”ฅ
  • -mmemcpy โ€” doesn't optimize block moves, and uses "memcpy" instead ๐Ÿฆ‹
  • -mno-clearbss โ€” this option is deprecated ๐Ÿ’€ Use -fno-zero-initialized-in-bss instead.
  • -mcpu=cpu-type โ€” uses features of, and schedules code for, the given CPU ๐Ÿ‘‘ Supported values are in the format vX.YY.Z, where X is a major version, YY is the minor version, and Z is compatibility code. Example values are v3.00.a, v4.00.b, v5.00.a, v5.00.b, and v6.00.a ๐ŸŽ€
  • -mxl-soft-mul โ€” uses software multiply emulation. This is the default โœจ
  • -mxl-soft-div โ€” uses software emulation for divides. This is the default ๐Ÿงš
  • -mxl-barrel-shift โ€” uses the hardware barrel shifter ๐Ÿ’œ
  • -mxl-pattern-compare โ€” uses pattern compare instructions ๐ŸŒˆ
  • -msmall-divides โ€” uses table lookup optimization for small signed integer divisions ๐Ÿ“
  • -mxl-stack-check โ€” this option is deprecated ๐Ÿ’€ Use -fstack-check instead.
  • -mxl-gp-opt โ€” uses GP-relative ".sdata"/".sbss" sections ๐Ÿซถ
  • -mxl-multiply-high โ€” uses multiply high instructions for the high part of a 32x32 multiply ๐Ÿ”ฅ
  • -mxl-float-convert โ€” uses hardware floating-point conversion instructions ๐Ÿฆ‹
  • -mxl-float-sqrt โ€” uses the hardware floating-point square root instruction ๐ŸŒธ
  • -mbig-endian โ€” generates code for a big-endian target ๐Ÿงš
  • -mlittle-endian โ€” generates code for a little-endian target ๐Ÿ’…
  • -mxl-reorder โ€” uses reorder instructions (swap and byte reversed load/store) โญ
  • -mxl-mode-app-model โ€” selects the application model app-model. Valid models are:
    • executable โ€” normal executable (default), uses startup code crt0.o.
    • xmdstub โ€” for use with the Xilinx Microprocessor Debugger (XMD) based software intrusive debug agent called xmdstub ๐Ÿ™ˆ This uses startup file crt1.o and sets the start address of the program to 0x800.
    • bootstrap โ€” for applications that get loaded using a bootloader ๐ŸŒˆ This model uses startup file crt2.o, which does not contain a processor reset vector handler. It's suited to handing control over to the bootloader on a processor reset instead of the application.
    • novectors โ€” for applications that don't need any of the MicroBlaze vectors ๐Ÿงš This option may be handy for apps running inside a monitoring application. This model uses crt3.o as a startup file.

Option -xl-mode-app-model is a deprecated alias for -mxl-mode-app-model ๐Ÿ’€

  • -mpic-data-is-text-relative โ€” assumes the displacement between the text and data segments is fixed at static link time ๐Ÿ’– This lets data get referenced by offset from the start of the text address instead of through the GOT, since PC-relative addressing isn't supported.

๐ŸŒˆ MIPS Options ๐ŸŒˆ

  • -EB โ€” generates big-endian code ๐Ÿ’œ
  • -EL โ€” generates little-endian code ๐Ÿซถ This is the default for mips*el-*-* configurations.
  • -march=arch โ€” generates code that runs on arch, which can be the name of a generic MIPS ISA or the name of a particular processor โœจ The ISA names are mips1, mips2, mips3, mips4, mips32, mips32r2, mips32r3, mips32r5, mips32r6, mips64, mips64r2, mips64r3, mips64r5, and mips64r6. The processor names are:
4kc, 4km, 4kp, 4ksc, 4kec, 4kem, 4kep, 4ksd,
5kc, 5kf, 20kc, 24kc, 24kf2_1, 24kf1_1, 24kec, 24kef2_1, 24kef1_1,
34kc, 34kf2_1, 34kf1_1, 34kn, 74kc, 74kf2_1, 74kf1_1, 74kf3_2,
1004kc, 1004kf2_1, 1004kf1_1, i6400, i6500, interaptiv, loongson2e,
loongson2f, loongson3a, gs464, gs464e, gs264e, m4k, m14k, m14kc,
m14ke, m14kec, m5100, m5101, octeon, octeon+, octeon2, octeon3,
orion, p5600, p6600, r2000, r3000, r3900, r4000, r4400, r4600,
r4650, r4700, r5900, r6000, r8000, rm7000, rm9000, r10000, r12000,
r14000, r16000, sb1, sr71000, vr4100, vr4111, vr4120, vr4130,
vr4300, vr5000, vr5400, vr5500, xlr and xlp

The special value from-abi picks the most compatible architecture for the selected ABI. That means mips1 for 32-bit ABIs and mips3 for 64-bit ABIs ๐Ÿงš

The native Linux/GNU toolchain also supports the value native, which picks the best architecture option for the host processor ๐Ÿฆ‹ -march=native has no effect if GCC doesn't recognize the processor ๐Ÿ™ˆ

In processor names, a final 000 can be abbreviated as k, so -march=r2k works ๐Ÿ’… Prefixes are optional, and vr may be written r ๐ŸŽ€

Names of the form nf2_1 refer to processors with FPUs clocked at half the rate of the core. Names of the form nf1_1 refer to processors with FPUs clocked at the same rate as the core, and names of the form nf3_2 refer to processors with FPUs clocked at a 3:2 ratio with respect to the core ๐Ÿ”ฅ For compatibility reasons, nf is accepted as a synonym for nf2_1, while nx and bfx are accepted as synonyms for nf1_1 ๐ŸŒธ

GCC defines two macros based on the value of this option ๐Ÿ’œ The first is "_MIPS_ARCH", which gives the name of the target architecture as a string. The second has the form "_MIPS_ARCH_foo", where foo is the capitalized value of "_MIPS_ARCH". For example, -march=r2000 sets "_MIPS_ARCH" to "r2000" and defines the macro "_MIPS_ARCH_R2000" โœจ

Note that the "_MIPS_ARCH" macro uses the processor names given above. In other words, it has the full prefix and doesn't abbreviate 000 as k ๐Ÿซถ In the case of from-abi, the macro names the resolved architecture (either "mips1" or "mips3"). It names the default architecture when no -march option is given ๐Ÿ‘‘

  • -mtune=arch โ€” okay so this one tunes the code for a specific chip โœจ arch can be any of the same values -march takes. It changes things like how instructions get scheduled and how expensive the math ops look to the compiler ๐Ÿ’… If you skip it, GCC tunes for whatever -march picked. Using -march and -mtune together is iconic: your code runs on a whole family of processors, but gets polished for one specific member ๐Ÿฆ‹ With -mtune, GCC also defines the macros "_MIPS_TUNE" and "_MIPS_TUNE_foo", which work the same way as the -march ones.
  • -mips1 โ€” same as -march=mips1 ๐ŸŽ€
  • -mips2 โ€” same as -march=mips2 ๐ŸŽ€
  • -mips3 โ€” same as -march=mips3 ๐ŸŽ€
  • -mips4 โ€” same as -march=mips4 ๐ŸŽ€
  • -mips32 โ€” same as -march=mips32 ๐ŸŽ€
  • -mips32r3 โ€” same as -march=mips32r3 ๐ŸŽ€
  • -mips32r5 โ€” same as -march=mips32r5 ๐ŸŽ€
  • -mips32r6 โ€” same as -march=mips32r6 ๐ŸŽ€
  • -mips64 โ€” same as -march=mips64 ๐ŸŽ€
  • -mips64r2 โ€” same as -march=mips64r2 ๐ŸŽ€
  • -mips64r3 โ€” same as -march=mips64r3 ๐ŸŽ€
  • -mips64r5 โ€” same as -march=mips64r5 ๐ŸŽ€
  • -mips64r6 โ€” same as -march=mips64r6 ๐ŸŽ€
  • -mips16, -mno-mips16 โ€” okay so -mips16 makes GCC generate MIPS16 code and -mno-mips16 says nope, don't ๐Ÿ™…โ€โ™€๏ธ If you're targeting MIPS32 or MIPS64, GCC uses the MIPS16e ASE to get it done ๐Ÿ’œ You can also flip MIPS16 on or off for a single function with the "mips16" and "nomips16" attributes, which is giving main character energy ๐Ÿ‘‘
  • -mmips16e2, -mno-mips16e2 โ€” -mmips16e2 tells GCC to use the MIPS16e2 ASE, and -mno-mips16e2 says don't. This option changes what -mips16 targets, so it aims at the MIPS16e2 ASE instead ๐Ÿงš
  • -mflip-mips16 โ€” this one makes GCC generate MIPS16 code on every other function ๐Ÿ™ˆ It exists purely for regression testing of mixed MIPS16 and non-MIPS16 code generation. It is not meant for normal code you're actually shipping, so please don't use it for your everyday projects ๐Ÿ’€
  • -minterlink-compressed, -mno-interlink-compressed โ€” -minterlink-compressed requires that code using the standard (uncompressed) MIPS ISA be link-compatible with MIPS16 and microMIPS code, and vice versa. -mno-interlink-compressed means you don't need that guarantee ๐ŸŒธ

For example, standard ISA code can't jump straight into MIPS16 or microMIPS code. It has to use a call or an indirect jump instead. So -minterlink-compressed turns off direct jumps unless GCC knows the jump target isn't compressed ๐Ÿ˜ญ

  • -minterlink-mips16, -mno-interlink-mips16 โ€” these are just aliases of -minterlink-compressed and -mno-interlink-compressed. They're older than the microMIPS ASE and only stick around so old builds don't break ๐Ÿซถ
  • -mabi=32, -mabi=o64, -mabi=n32, -mabi=64, -mabi=eabi โ€” generates code for the ABI you pick โœจ

Heads up that EABI comes in a 32-bit and a 64-bit flavor. GCC usually goes 64-bit when you pick a 64-bit architecture, but if you want 32-bit code anyway, use -mgp32 ๐Ÿฆ‹

For info on the O64 ABI, check out <https://gcc.gnu.org/projects/mipso64-abi.html> ๐Ÿ“š

GCC also supports a version of the o32 ABI where floating-point registers are 64 bits wide instead of 32. Pick that combo with -mabi=32 -mfp64 ๐Ÿ”ฅ This one leans on the "mthc1" and "mfhc1" instructions, so it only works on MIPS32R2, MIPS32R3, and MIPS32R5 processors ๐Ÿ’…

The register assignments for arguments and return values stay the same, but each scalar value gets passed in one 64-bit register instead of a pair of 32-bit ones. For example, scalar floating-point values come back in $f0 alone, not a $f0/$f1 pair. The call-saved registers stay the same too, meaning the even-numbered double-precision registers get saved ๐ŸŒˆ

There are two more o32 variants for moving from 32-bit to 64-bit registers: FPXX (-mfpxx) and FP64A (-mfp64 -mno-odd-spreg). FPXX means all code has to run correctly with either 32-bit or 64-bit registers. It can link with FP32 or FP64, but not both at once ๐Ÿงš FP64A is like FP64 but it bans odd-numbered single-precision registers. Pair it with the "FRE" mode of FPUs on MIPS32R5 processors and FP32 and FP64A code can link together and run in the same process without switching FPU modes โœจ

  • -mabicalls, -mno-abicalls โ€” -mabicalls generates code suitable for SVR4-style dynamic objects, and -mno-abicalls skips that. -mabicalls is the default on SVR4-based systems ๐ŸŒธ
  • -mshared, -mno-shared โ€” -mshared generates fully position-independent code that can be linked into shared libraries, and -mno-shared doesn't. This only affects -mabicalls ๐Ÿ’–

Historically, all -mabicalls code was position-independent no matter what you set for -fPIC or -fpic. But the GNU toolchain has an extension where executables can use absolute accesses for locally-binding symbols. It can also use shorter GP initialization sequences and make direct calls to locally-defined functions. You turn this mode on with -mno-shared ๐Ÿซถ

-mno-shared needs binutils 2.16 or higher, and it generates objects that only the GNU linker can link ๐Ÿ˜ญ It doesn't change the ABI of the final executable, only the ABI of relocatable objects. Using -mno-shared usually makes executables smaller and quicker ๐Ÿ”ฅ

-mshared is the default โœจ

  • -mplt, -mno-plt โ€” -mplt assumes the static and dynamic linkers support PLTs and copy relocations, and -mno-plt doesn't assume that ๐Ÿฆ‹ This only matters with -mno-shared -mabicalls. For the n64 ABI, it does nothing unless you also use -msym32.

You can make -mplt the default by configuring GCC with --with-mips-plt. Otherwise the default is -mno-plt ๐Ÿ’…

  • -mxgot, -mno-xgot โ€” -mxgot lifts the usual size limits on the global offset table, and -mno-xgot keeps them ๐Ÿ‘‘

Normally GCC uses one instruction to load values from the GOT. That's pretty efficient, but it only works if the GOT is smaller than about 64k. Anything bigger and the linker throws an error like this:

        relocation truncated to fit: R_MIPS_GOT16 foobar

If you see that, recompile your code with -mxgot ๐Ÿฅบ It handles really huge GOTs, but the code is a bit less efficient, since fetching a global symbol's value takes three instructions instead of one.

Some linkers can create multiple GOTs. If yours does, you usually only need -mxgot when a single object file touches more than 64k's worth of GOT entries. Very few linkers are like that ๐Ÿค“

These options do nothing unless GCC is generating position-independent code ๐Ÿ™„

  • -mgp32 โ€” assumes general-purpose registers are 32 bits wide ๐Ÿ“
  • -mgp64 โ€” assumes general-purpose registers are 64 bits wide ๐Ÿ“
  • -mfp32 โ€” assumes floating-point registers are 32 bits wide ๐ŸŒท
  • -mfp64 โ€” assumes floating-point registers are 64 bits wide ๐ŸŒท
  • -mfpxx โ€” doesn't assume any particular width for floating-point registers ๐Ÿฆ‹
  • -mhard-float โ€” uses floating-point coprocessor instructions ๐Ÿ”ฅ
  • -msoft-float โ€” doesn't use floating-point coprocessor instructions. Instead, floating-point math gets done with library calls ๐Ÿ’œ
  • -mno-float โ€” this is -msoft-float plus a promise that your program never does any floating-point operations at all ๐Ÿ™…โ€โ™€๏ธ Right now it only works on some bare-metal MIPS configs, where it might pick a special set of libraries with zero floating-point support, including things like the floating-point "printf" formats. If code built with -mno-float accidentally has floating-point ops in it, expect a link-time or run-time failure ๐Ÿ’€
  • -msingle-float โ€” assumes the floating-point coprocessor only supports single-precision operations ๐ŸŒธ
  • -mdouble-float โ€” assumes the floating-point coprocessor supports double-precision operations. This is the default ๐Ÿ’–
  • -modd-spreg, -mno-odd-spreg โ€” -modd-spreg lets GCC use odd-numbered single-precision floating-point registers for the o32 ABI, and -mno-odd-spreg stops it ๐Ÿงš It's the default for processors known to support those registers. When you're using the o32 FPXX ABI, -mno-odd-spreg is set by default ๐Ÿซถ
  • -mabs=2008, -mabs=legacy โ€” these control how special not-a-number (NaN) IEEE 754 floating-point data is handled by the "abs.fmt" and "neg.fmt" machine instructions ๐Ÿ”ฅ

By default, or when you use -mabs=legacy, you get the legacy treatment. In that case these instructions count as arithmetic, so they get avoided wherever correct operation matters and the input might be a NaN. GCC uses a longer sequence that manipulates the sign bit by hand instead, unless -ffinite-math-only is also specified ๐Ÿ˜ญ

-mabs=2008 selects the IEEE 754-2008 treatment. Here these instructions count as non-arithmetic, so they work correctly in every case, especially when the input is a NaN. They're always used for the matching operations ๐Ÿ’…

  • -mnan=2008, -mnan=legacy โ€” these control how special not-a-number (NaN) IEEE 754 floating-point data gets encoded ๐ŸŒˆ

-mnan=legacy picks the legacy encoding. Quiet NaNs (qNaNs) are marked by the first bit of their trailing significand field being 0, and signaling NaNs (sNaNs) by that bit being 1 ๐Ÿ’œ

-mnan=2008 picks the IEEE 754-2008 encoding. Now qNaNs have that first bit set to 1, and sNaNs have it set to 0 โœจ

The default is -mnan=legacy, unless GCC was configured with --with-nan=2008 ๐ŸŽ€

  • -mllsc, -mno-llsc โ€” -mllsc uses ll, sc, and sync instructions to implement atomic memory built-in functions, and -mno-llsc doesn't ๐Ÿฆ‹ If you specify neither, GCC uses those instructions as long as the target architecture supports them ๐ŸŒธ

Okay so the -mllsc vibe continues: -mllsc is clutch if the runtime can emulate the instructions, and -mno-llsc can be super useful when you're compiling for nonstandard ISAs ๐Ÿ’…. You can make either one the default by configuring GCC with --with-llsc or --without-llsc. Heads up, --with-llsc is already the default for some configurations, so peek at the installation docs for the tea โœจ

  • -mdsp, -mno-dsp โ€” Use (or don't use) revision 1 of the MIPS DSP ASE ๐ŸŽ€. Turning it on defines the preprocessor macro "__mips_dsp" and sets "__mips_dsp_rev" to 1.
  • -mdspr2, -mno-dspr2 โ€” Use (or skip) revision 2 of the MIPS DSP ASE ๐Ÿ’–. This one defines both "__mips_dsp" and "__mips_dspr2", and sets "__mips_dsp_rev" to 2.
  • -msmartmips, -mno-smartmips โ€” Use (or skip) the MIPS SmartMIPS ASE ๐Ÿฆ‹.
  • -mpaired-single, -mno-paired-single โ€” Use (or skip) paired-single floating-point instructions ๐ŸŒธ. This one needs hardware floating-point support to be enabled, so make sure that's on first.
  • -mdmx, -mno-mdmx โ€” Use (or skip) MIPS Digital Media Extension instructions ๐ŸŒˆ. These only work when you're generating 64-bit code, and they also need hardware floating-point support enabled.
  • -mips3d, -mno-mips3d โ€” Use (or skip) the MIPS-3D ASE โญ. Heads up, -mips3d implies -mpaired-single, so it comes along for the ride.
  • -mmicromips, -mno-micromips โ€” Generate (or don't generate) microMIPS code ๐Ÿงš.

You can also control microMIPS code generation on a per-function basis with the "micromips" and "nomicromips" attributes. Main character energy ๐Ÿ‘‘.

  • -mmt, -mno-mt โ€” Use (or skip) MT Multithreading instructions ๐Ÿงต.
  • -mmcu, -mno-mcu โ€” Use (or skip) the MIPS MCU ASE instructions ๐Ÿ’œ.
  • -meva, -mno-eva โ€” Use (or skip) the MIPS Enhanced Virtual Addressing instructions ๐Ÿ”ฅ.
  • -mvirt, -mno-virt โ€” Use (or skip) the MIPS Virtualization (VZ) instructions ๐ŸŒธ.
  • -mxpa, -mno-xpa โ€” Use (or skip) the MIPS eXtended Physical Address (XPA) instructions โœจ.
  • -mcrc, -mno-crc โ€” Use (or skip) the MIPS Cyclic Redundancy Check (CRC) instructions ๐Ÿ“.
  • -mginv, -mno-ginv โ€” Use (or skip) the MIPS Global INValidate (GINV) instructions ๐Ÿซถ.
  • -mloongson-mmi, -mno-loongson-mmi โ€” Use (or skip) the MIPS Loongson MultiMedia extensions Instructions (MMI) ๐ŸŽ€.
  • -mloongson-ext, -mno-loongson-ext โ€” Use (or skip) the MIPS Loongson EXTensions (EXT) instructions ๐Ÿฆ‹.
  • -mloongson-ext2, -mno-loongson-ext2 โ€” Use (or skip) the MIPS Loongson EXTensions r2 (EXT2) instructions ๐Ÿ’–.
  • -mlong64 โ€” Force "long" types to be 64 bits wide ๐ŸŒˆ. For the scoop on the default and how the pointer size gets decided, check out -mlong32 below.
  • -mlong32 โ€” Force "long", "int", and pointer types to be 32 bits wide ๐Ÿ“.

The default sizes of "int", "long", and pointers depend on the ABI ๐Ÿงš. All the supported ABIs use 32-bit "int"s. The n64 ABI uses 64-bit "long"s, and so does the 64-bit EABI; the others use 32-bit "long"s. Pointers are the same size as "long"s, or the same size as integer registers, whichever is smaller.

  • -msym32, -mno-sym32 โ€” Assume (or don't assume) that all symbols have 32-bit values, no matter which ABI you picked ๐Ÿ”ฅ. This is super handy alongside -mabi=64 and -mno-abicalls, because it lets GCC generate shorter and faster references to symbolic addresses.
  • -G num โ€” Put definitions of externally-visible data in a small data section if that data is no bigger than num bytes โœจ. GCC can then generate more efficient accesses to the data; see -mgpopt for the details.

The default -G option depends on the configuration ๐Ÿซถ.

  • -mlocal-sdata, -mno-local-sdata โ€” Extend (or don't extend) the -G behavior to local data too, like static variables in C ๐Ÿ’…. -mlocal-sdata is the default for all configurations.

If the linker complains that an application is using too much small data, try rebuilding the less performance-critical parts with -mno-local-sdata ๐Ÿ˜ญ. You might also want to build large libraries with -mno-local-sdata, so the libraries leave more room for the main program.

  • -mextern-sdata, -mno-extern-sdata โ€” Assume (or don't assume) that externally-defined data is in a small data section, as long as its size is within the -G limit ๐Ÿ‘‘. -mextern-sdata is the default for all configurations.

If you compile a module Mod with -mextern-sdata -G num -mgpopt, and Mod references a variable Var that is no bigger than num bytes, you must make sure that Var is placed in a small data section ๐Ÿ’€. If Var is defined by another module, you must either compile that module with a high-enough -G setting or attach a "section" attribute to Var's definition. If Var is common, you must link the application with a high-enough -G setting.

The easiest way to satisfy all of this is to compile and link every module with the same -G option ๐ŸŒˆ. However, if you want to build a library that supports several different small data limits, compile the library with the highest supported -G setting and also use -mno-extern-sdata to stop the library from making assumptions about externally-defined data ๐Ÿงš.

  • -mgpopt, -mno-gpopt โ€” Use (or don't use) GP-relative accesses for symbols that are known to be in a small data section; see -G, -mlocal-sdata and -mextern-sdata ๐Ÿฆ‹. -mgpopt is the default for all configurations.

-mno-gpopt is useful when the $gp register might not hold the value of "_gp" ๐Ÿ™ˆ. For example, if the code is part of a library that might be used in a boot monitor, programs that call boot monitor routines pass an unknown value in $gp. (In those situations, the boot monitor itself is usually compiled with -G0.)

-mno-gpopt implies -mno-local-sdata and -mno-extern-sdata.

  • -membedded-data, -mno-embedded-data โ€” Allocate variables to the read-only data section first if possible, then to the small data section if possible, and otherwise to data ๐ŸŒธ. This gives slightly slower code than the default, but it reduces the amount of RAM needed while executing, so it might be your bestie for some embedded systems.
  • -muninit-const-in-rodata, -mno-uninit-const-in-rodata โ€” Put uninitialized "const" variables in the read-only data section โœจ. This only means something when you're also using -membedded-data.
  • -mcode-readable=setting โ€” Tell GCC whether it's allowed to generate code that reads from executable sections ๐Ÿ’…. There are three possible settings, and they're all very different vibes:
    • -mcode-readable=yes โ€” Instructions can freely access executable sections ๐Ÿ’–. This is the default setting.
    • -mcode-readable=pcrel โ€” MIPS16 PC-relative load instructions can access executable sections, but other instructions must not do so ๐ŸŽ€. This is useful on 4KSc and 4KSd processors when the code TLBs have the Read Inhibit bit set. It's also useful on processors that can be configured to have a dual instruction/data SRAM interface and that, like the M4K, automatically redirect PC-relative loads to the instruction RAM.
    • -mcode-readable=no โ€” Instructions must not access executable sections ๐Ÿ™…. This can be useful on targets configured with a dual instruction/data SRAM interface that (unlike the M4K) do not automatically redirect PC-relative loads to the instruction RAM.
  • -msplit-addresses, -mno-split-addresses โ€” Enable (or disable) use of the %hi() and %lo() assembler relocation operators ๐Ÿงš. This option has been superseded by -mexplicit-relocs but is kept around for backwards compatibility.
  • -mexplicit-relocs=none, -mexplicit-relocs=base, -mexplicit-relocs=pcrel, -mexplicit-relocs, -mno-explicit-relocs โ€” These control whether explicit relocs (such as %gp_rel) get used ๐Ÿ”ฅ. The default depends on the version of GAS when GCC itself was built.

The "base" explicit-relocs support got introduced into GAS in 2001 ๐ŸŒˆ. The "pcrel" explicit-relocs support got introduced into GAS in 2014, and it supports %pcrel_hi and %pcrel_lo โœจ.

  • -mcheck-zero-division, -mno-check-zero-division โ€” Trap (or don't trap) on integer division by zero ๐Ÿ“. The default is -mcheck-zero-division.
  • -mdivide-traps, -mdivide-breaks โ€” MIPS systems check for division by zero by generating either a conditional trap or a break instruction ๐Ÿซถ. Traps give you smaller code, but they're only supported on MIPS II and later. Also, some versions of the Linux kernel have a bug that stops trap from generating the proper signal ("SIGFPE") ๐Ÿ˜ญ. Use -mdivide-traps to allow conditional traps on architectures that support them, and -mdivide-breaks to force the use of breaks.

The default is usually -mdivide-traps, but you can override it at configure time with --with-divide=breaks ๐Ÿ’…. Divide-by-zero checks can be completely disabled with -mno-check-zero-division.

  • -mload-store-pairs, -mno-load-store-pairs โ€” Enable (or disable) an optimization that pairs consecutive load or store instructions so they can do load/store bonding ๐Ÿ‘‘. This is enabled by default, but it only takes effect when the selected architecture is known to support bonding.
  • -mstrict-align, -mno-strict-align, -munaligned-access, -mno-unaligned-access โ€” Disable (or enable) direct unaligned access for MIPS Release 6 ๐Ÿ’€. MIPSr6 requires load/store unaligned-access support, either by hardware or by trap&emulate, so -mstrict-align may be needed by the kernel.
Heads up bestie: the options -munaligned-access and -mno-unaligned-access are obsoleted, and they're only kept for backward compatibility ๐Ÿ™ˆ.
  • -mmemcpy, -mno-memcpy โ€” Force (or don't force) the use of "memcpy" for non-trivial block moves ๐ŸŒˆ. The default is -mno-memcpy, which lets GCC inline most constant-sized copies.
  • -mlong-calls, -mno-long-calls โ€” Disable (or don't disable) use of the "jal" instruction ๐Ÿงš. Calling functions with "jal" is more efficient, but it requires the caller and callee to be in the same 256 megabyte segment.

This option has no effect on abicalls code ๐Ÿฆ‹. The default is -mno-long-calls.

  • -mmad, -mno-mad โ€” Enable (or disable) use of the "mad", "madu", and "mul" instructions, as provided by the R4650 ISA ๐Ÿ“.

Okay so bestie, we're still deep in the MIPS flags ๐Ÿฆ‹ and these ones are a whole mood ๐Ÿ’…

  • -mimadd, -mno-imadd โ€” Turns the madd and msub integer instructions on (or off) ๐Ÿ”ฅ Default is -mimadd on architectures that have madd and msub, EXCEPT the 74k, where it was found to generate slower code ๐Ÿ’€
  • -mfused-madd, -mno-fused-madd โ€” Same idea but for the floating-point multiply-accumulate instructions, when they're actually available โœจ Default is -mfused-madd.
๐Ÿ™ˆ On the R8000, when multiply-accumulate instructions get used, the intermediate product is calculated to infinite precision and is NOT subject to the FCSR Flush to Zero bit. This might be undesirable in some circumstances, so keep that in your back pocket ๐Ÿ’œ

On other processors, the result is numerically identical to doing the same computation with separate multiply, add, subtract and negate instructions ๐Ÿซถ

  • -nocpp โ€” Tells the MIPS assembler to NOT run its preprocessor over user assembler files (the ones with a .s suffix) when it assembles them ๐Ÿงš
  • -mfix-24k, -mno-fix-24k โ€” Works around the 24K E48 errata (lost data on stores during refill) ๐Ÿ˜ญ The workarounds are handled by the assembler, not by GCC itself.
  • -mfix-r4000, -mno-fix-r4000 โ€” Works around certain R4000 CPU errata ๐ŸŒธ
    • A double-word or a variable shift may give an incorrect result if it's executed immediately after starting an integer division.
    • A double-word or a variable shift may give an incorrect result if it's executed while an integer multiplication is in progress.
    • An integer division may give an incorrect result if it's started in a delay slot of a taken branch or a jump.
  • -mfix-r4400, -mno-fix-r4400 โ€” Works around certain R4400 CPU errata ๐Ÿ“
    • A double-word or a variable shift may give an incorrect result if it's executed immediately after starting an integer division.
  • -mfix-r10000, -mno-fix-r10000 โ€” Works around certain R10000 errata ๐Ÿ‘‘
    • ll/sc sequences may not behave atomically on revisions prior to 3.0. They may deadlock on revisions 2.6 and earlier.

This option can only be used if the target architecture supports branch-likely instructions ๐ŸŽ€ -mfix-r10000 is the default when -march=r10000 is used, and -mno-fix-r10000 is the default otherwise.

  • -mfix-r5900, -mno-fix-r5900 โ€” Basically says "don't try to schedule the preceding instruction into the delay slot of a branch" when that branch sits at the end of a short loop of six instructions or fewer ๐Ÿ™…โ€โ™€๏ธ Instead, it always schedules a nop instruction there. The short loop bug can make loops only run once or twice thanks to a hardware bug in the R5900 chip ๐Ÿ’€ The workaround is handled by the assembler, not GCC.
  • -mfix-rm7000, -mno-fix-rm7000 โ€” Works around the RM7000 dmult/dmultu errata โœจ The workarounds are implemented by the assembler rather than by GCC.
  • -mfix-vr4120, -mno-fix-vr4120 โ€” Works around certain VR4120 errata ๐Ÿฆ‹
    • dmultu does not always produce the correct result.
    • div and ddiv do not always produce the correct result if one of the operands is negative.

The workarounds for the division errata rely on special functions in libgcc.a ๐Ÿซ  At the moment, those functions are only provided by the mips64vr*-elf configurations.

Other VR4120 errata need a NOP inserted between certain pairs of instructions, and those get handled by the assembler, not by GCC itself ๐Ÿ’…

  • -mfix-vr4130 โ€” Works around the VR4130 mflo/mfhi errata ๐Ÿงš The workarounds are implemented by the assembler rather than by GCC, although GCC avoids using mflo and mfhi if the VR4130 macc, macchi, dmacc and dmacchi instructions are available instead.
  • -mfix-sb1, -mno-fix-sb1 โ€” Works around certain SB-1 CPU core errata ๐ŸŒˆ (This flag currently works around the SB-1 revision 2 "F1" and "F2" floating-point errata.)
  • -mr10k-cache-barrier=setting โ€” Lets you specify whether GCC should insert cache barriers to avoid the side effects of speculation on R10K processors ๐Ÿ”ฅ

Okay so the R10K is kinda chaotic ๐Ÿ˜ญ Like many processors, it tries to predict the outcome of a conditional branch and speculatively runs instructions from the "taken" branch. Then it aborts them if the prediction was wrong. BUT on the R10K, even aborted instructions can leave side effects behind, which is giving unreliable vibes ๐Ÿ’€

This problem only affects kernel stores and, depending on the system, kernel loads. For example, a speculatively-executed store might load the target memory into cache and mark the cache line as dirty, even if the store itself gets aborted later. If a DMA operation writes to the same area of memory before that "dirty" line is flushed, the cached data overwrites the DMA-ed data ๐Ÿ˜ฑ For a full description (plus other potential problems), check the R10K processor manual.

One workaround is to insert cache barrier instructions before every memory access that might be speculatively executed AND might have side effects even if aborted. -mr10k-cache-barrier=setting controls how GCC implements this workaround. It assumes that aborted accesses to any byte in the following regions do NOT have side effects:

  1. the memory occupied by the current function's stack frame;
  2. the memory occupied by an incoming stack argument;
  3. the memory occupied by an object with a link-time-constant address.

It's the kernel's job to make sure speculative accesses to these regions are actually safe ๐Ÿซถ

If the input program contains a function declaration like this:

void foo (void);

then the implementation of foo must allow j foo and jal foo to be executed speculatively. GCC honors this restriction for functions it compiles itself. It expects non-GCC functions (like hand-written assembly code) to do the same ๐Ÿ’–

The option comes in three forms:

  • -mr10k-cache-barrier=load-store โ€” Inserts a cache barrier before a load or store that might be speculatively executed AND might have side effects even if aborted.
  • -mr10k-cache-barrier=store โ€” Inserts a cache barrier before a store that might be speculatively executed AND might have side effects even if aborted.
  • -mr10k-cache-barrier=none โ€” Disables the insertion of cache barriers. This is the default setting โœจ

  • -mflush-func=func, -mno-flush-func โ€” Specifies the function to call to flush the I and D caches, or tells GCC to not call any such function ๐Ÿงš If it does get called, the function must take the same arguments as the common _flush_func: the address of the memory range being flushed, the size of that memory range, and the number 3 (to flush both caches). The default depends on the target GCC was configured for, but it's commonly either _flush_func or __cpu_flush ๐ŸŒธ
  • -mbranch-cost=num โ€” Sets the cost of branches to roughly num "simple" instructions ๐Ÿ’… This cost is only a heuristic, and it's NOT guaranteed to give consistent results across releases. A zero cost redundantly selects the default, which is based on the -mtune setting.
  • -mbranch-likely, -mno-branch-likely โ€” Enables or disables use of Branch Likely instructions, no matter the default for the selected architecture ๐Ÿฆ‹ By default, Branch Likely instructions may be generated if the selected architecture supports them. The exception is the MIPS32 and MIPS64 architectures (and processors that implement those architectures). For those, Branch Likely instructions are not generated by default, because those architectures specifically deprecate their use ๐Ÿ™ˆ
  • -mcompact-branches=never, -mcompact-branches=optimal, -mcompact-branches=always โ€” These control which form of branches gets generated ๐Ÿ”ฅ The default is -mcompact-branches=optimal.
    • -mcompact-branches=never makes sure compact branch instructions are NEVER generated.
    • -mcompact-branches=always makes sure a compact branch instruction gets generated if one is available, for MIPS Release 6 onwards. If a compact branch instruction isn't available (or it's pre-R6), a delay slot form of the branch gets used instead.
    • If it's used for MIPS16/microMIPS targets, it will just be ignored for now. The behaviour for MIPS16/microMIPS may change in the future, since they do have some compact branch instructions ๐Ÿ‘€
    • -mcompact-branches=optimal causes a delay slot branch to be used if one is available in the current ISA AND the delay slot is successfully filled. If the delay slot isn't filled, a compact branch gets chosen if one is available โœจ
  • -mfp-exceptions, -mno-fp-exceptions โ€” Specifies whether FP exceptions are enabled ๐ŸŒˆ This affects how FP instructions get scheduled for some processors. The default is that FP exceptions are enabled.

For instance, on the SB-1, if FP exceptions are disabled AND we're emitting 64-bit code, then we can use both FP pipes. Otherwise, we can only use one FP pipe ๐Ÿ’œ

  • -mvr4130-align, -mno-vr4130-align โ€” The VR4130 pipeline is two-way superscalar, but it can only issue two instructions together if the first one is 8-byte aligned ๐Ÿ“ When this option is enabled, GCC aligns pairs of instructions that it thinks should execute in parallel.

This option only has an effect when optimizing for the VR4130. It normally makes code faster, but at the expense of making it bigger. It's enabled by default at optimization level -O3 ๐Ÿ”ฅ

  • -msynci, -mno-synci โ€” Enables (or disables) generation of synci instructions on architectures that support it ๐Ÿงš The synci instructions (if enabled) get generated when __builtin___clear_cache is compiled.

This option defaults to -mno-synci, but the default can be overridden by configuring GCC with --with-synci.

When compiling code for single processor systems, it's generally safe to use synci. However, on many multi-core (SMP) systems, it does NOT invalidate the instruction caches on all cores and may lead to undefined behaviour ๐Ÿ˜ญ

  • -mrelax-pic-calls, -mno-relax-pic-calls โ€” Tries to turn PIC calls that are normally dispatched via register $25 into direct calls ๐Ÿ’– This is only possible if the linker can resolve the destination at link time AND the destination is within range for a direct call.

Okay so, the -mrelax-pic-calls thing is the default if GCC was set up with an assembler and linker that support the .reloc assembly directive, and -mexplicit-relocs is in effect. ๐Ÿงš With -mno-explicit-relocs, the assembler and linker can do this optimization all by themselves, no help from the compiler needed โœจ

  • -mmcount-ra-address, -mno-mcount-ra-address โ€” okay this one is lowkey wild ๐Ÿ˜ญ It tells GCC to emit (or not emit) code that lets _mcount change the return address of the calling function. When it's on, the usual _mcount interface gets a brand new ra-address parameter. It's of type intptr_t * and gets passed in register $12. So _mcount can rewrite the return address by doing both of these:
    • Returning the new address in register $31.
    • Storing the new address in *ra-address, but only if ra-address is nonnull.

The default is -mno-mcount-ra-address ๐ŸŽ€

  • -mframe-header-opt, -mno-frame-header-opt โ€” this turns frame header optimization on (or off) in the o32 ABI ๐Ÿ’… Here's the tea: when you're using o32, calling functions reserve 16 bytes on the stack so the called function has somewhere to write out its register arguments. With this on, GCC skips that frame header allocation if it can tell it's never used. Literally free bytes ๐Ÿซถ

This optimization is off by default at all optimization levels.

  • -mlxc1-sxc1, -mno-lxc1-sxc1 โ€” when it applies, this enables (or disables) the generation of lwxc1, swxc1, ldxc1 and sdxc1 instructions. It's on by default ๐ŸŒธ
  • -mmadd4, -mno-madd4 โ€” same energy here: when it applies, this enables (or disables) the 4-operand madd.s, madd.d and related instructions. Also on by default โœจ

๐ŸŒˆ MMIX Options ๐ŸŒˆ

Okay bestie, these options are defined for the MMIX ๐Ÿงš

  • -mlibfuncs, -mno-libfuncs โ€” specifies that intrinsic library functions are being compiled, so ALL values get passed in registers, no matter how big they are ๐Ÿ’œ
  • -mepsilon, -mno-epsilon โ€” generates floating-point comparison instructions that compare with respect to the rE epsilon register ๐Ÿ”ฅ
  • -mabi=mmixware, -mabi=gnu โ€” -mabi=mmixware generates code where function parameters and return values are seen as registers $0 and up (from the called function's point of view). -mabi=gnu is the GNU ABI instead, which uses global registers $231 and up ๐Ÿ‘‘
  • -mzero-extend, -mno-zero-extend โ€” when reading data from memory in sizes shorter than 64 bits, this decides whether you get zero-extending load instructions (with -mzero-extend) or sign-extending ones by default. -mno-zero-extend means you do not use zero-extending loads ๐Ÿ“
  • -mknuthdiv, -mno-knuthdiv โ€” this one's about division with a remainder. With -mknuthdiv, the result's remainder has the same sign as the divisor. With the default, -mno-knuthdiv, the sign of the remainder follows the sign of the dividend. Both methods are arithmetically valid, though the latter is almost exclusively used ๐Ÿ™ˆ
  • -mtoplevel-symbols, -mno-toplevel-symbols โ€” prepends (or doesn't prepend) a : to all global symbols, so the assembly code can be used with the PREFIX assembly directive ๐Ÿฆ‹
  • -melf โ€” generates an executable in the ELF format, instead of the default mmo format that the mmix simulator uses ๐Ÿ’–
  • -mbranch-predict, -mno-branch-predict โ€” uses (or doesn't use) the probable-branch instructions, when static branch prediction says a branch is probable โญ
  • -mbase-addresses, -mno-base-addresses โ€” generates (or doesn't generate) code that uses base addresses. Using a base address automatically makes a request (handled by the assembler and the linker) for a constant to be set up in a global register. That register gets used for one or more base address requests within the range 0 to 255 from the value held in the register. This generally leads to short and fast code, BUT the number of different data items you can address is limited. So if your program has lots of static data, you may need -mno-base-addresses ๐Ÿฅบ
  • -msingle-exit, -mno-single-exit โ€” forces (or doesn't force) generated code to have a single exit point in each function ๐Ÿ’…

๐Ÿฆ‹ MN10300 Options ๐Ÿฆ‹

These -m options are defined for Matsushita MN10300 architectures ๐ŸŒธ

  • -mmult-bug โ€” generates code to avoid bugs in the multiply instructions for the MN10300 processors. This is the default ๐ŸŽ€
  • -mno-mult-bug โ€” does NOT generate that multiply-bug workaround code for the MN10300 processors ๐Ÿ™…
  • -mam33 โ€” generates code using features specific to the AM33 processor โœจ
  • -mno-am33 โ€” does not generate code using AM33-specific features. This is the default ๐Ÿ’œ
  • -mam33-2 โ€” generates code using features specific to the AM33/2.0 processor ๐Ÿ”ฅ
  • -mam34 โ€” generates code using features specific to the AM34 processor ๐Ÿ‘‘
  • -mtune=cpu-type โ€” uses the timing characteristics of the CPU type you give when scheduling instructions. It does NOT change the targeted processor type, so don't get confused ๐Ÿซถ The CPU type has to be one of mn10300, am33, am33-2 or am34.
  • -mreturn-pointer-on-d0 โ€” when generating a function that returns a pointer, the pointer gets returned in both a0 and d0. Otherwise, it's only returned in a0, and trying to call those functions without a prototype gives you errors ๐Ÿ˜ญ Heads up: this option is on by default! Use -mno-return-pointer-on-d0 to turn it off.
  • -mno-crt0 โ€” does not link in the C run-time initialization object file ๐Ÿงš
  • -mrelax โ€” tells the linker to run a relaxation optimization pass that shortens branches, calls and absolute memory addresses. This only does anything when you use it on the command line for the final link step ๐Ÿ’–
This option makes symbolic debugging impossible. Be careful, bestie ๐Ÿ’€
  • -mliw โ€” lets the compiler generate Long Instruction Word instructions if the target is AM33 or later. This is the default. This option defines the preprocessor macro __LIW__ ๐ŸŒˆ
  • -mno-liw โ€” does not let the compiler generate Long Instruction Word instructions. This option defines the preprocessor macro __NO_LIW__ ๐Ÿ“
  • -msetlb โ€” lets the compiler generate the SETLB and Lcc instructions if the target is AM33 or later. This is the default. This option defines the preprocessor macro __SETLB__ โญ
  • -mno-setlb โ€” does not let the compiler generate SETLB or Lcc instructions. This option defines the preprocessor macro __NO_SETLB__ ๐Ÿ™ˆ

๐Ÿ‘ Moxie Options ๐Ÿ‘

  • -meb โ€” generates big-endian code. This is the default for moxie-*-* configurations ๐Ÿ’…
  • -mel โ€” generates little-endian code ๐ŸŒธ
  • -mmul.x โ€” generates mul.x and umul.x instructions. This is the default for moxiebox-*-* configurations โœจ
  • -mno-crt0 โ€” does not link in the C run-time initialization object file ๐ŸŽ€

๐Ÿ’œ MSP430 Options ๐Ÿ’œ

These options are defined for the MSP430 ๐Ÿ”ฅ

  • -masm-hex โ€” forces assembly output to ALWAYS use hex constants. Normally those constants are signed decimals, but this option exists for testsuite and/or aesthetic purposes ๐Ÿฆ‹
  • -mmcu= โ€” selects the MCU to target ๐Ÿงš This creates a C preprocessor symbol based on the MCU name, converted to upper case and pre- and post-fixed with __. That symbol is then used by the msp430.h header file to pick an MCU-specific supplementary header file, so it's giving organized ๐Ÿ’…

This option also sets the ISA to use. If the MCU name is one that's known to only support the 430 ISA, then that one gets selected. Otherwise the 430X ISA is selected. A generic MCU name of msp430 can also be used to select the 430 ISA, and the generic msp430x MCU name selects the 430X ISA โœจ

On top of that, an MCU-specific linker script gets added to the linker command line. The script's name is the MCU name with .ld appended. So specifying -mmcu=xxx on the gcc command line defines the C preprocessor symbol __XXX__ and makes the linker search for a script called xxx.ld ๐Ÿซถ

The ISA and hardware multiply support for the different MCUs is hard-coded into GCC. BUT an external devices.csv file can be used to extend device support beyond those hard-coded ones ๐ŸŒˆ

GCC searches for devices.csv using the methods below, in the given precedence order. The first method beats the second, and the second beats the third ๐Ÿ‘‘

  • Include path specified with -I and -L โ€” devices.csv is searched for in each of the directories specified by include paths and linker library search paths.
  • Path specified by the environment variable MSP430_GCC_INCLUDE_DIR โ€” define the value of the global environment variable MSP430_GCC_INCLUDE_DIR as the full path to the directory containing devices.csv, and GCC will search that directory for devices.csv. If devices.csv is found there, that directory is also registered as an include path and linker library path. Header files and linker scripts in this directory can therefore be used without manually specifying -I and -L on the command line ๐Ÿ’–
  • The msp430-elf{,bare}/include/devices directory โ€” finally, GCC examines msp430-elf{,bare}/include/devices from the toolchain root directory. This directory does NOT exist in a default installation, but if you've created it and copied devices.csv there, then the MCU data gets read. Just like above, this directory is also registered as an include path and linker library path ๐Ÿ“

If none of the above search methods find devices.csv, then the hard-coded MCU data is used ๐Ÿ™ˆ

  • -mwarn-mcu, -mno-warn-mcu โ€” enables (or disables) warnings about conflicts between the MCU name given by -mmcu= and the ISA set by -mcpu and/or the hardware multiply support set by -mhwmult. It also toggles warnings about unrecognized MCU names. This option is on by default โญ
  • -mcpu= โ€” specifies the ISA to use. Accepted values are msp430, msp430x and msp430xv2. This option is deprecated, so use the -mmcu= option to select the ISA instead ๐Ÿฅบ
  • -msim โ€” links to the simulator runtime libraries and linker script. This overrides any scripts that would be selected by the -mmcu= option ๐ŸŽ€
  • -mlarge โ€” uses large-model addressing (20-bit pointers, 20-bit size_t) ๐ŸŒธ
  • -msmall โ€” uses small-model addressing (16-bit pointers, 16-bit size_t) ๐Ÿฆ‹
  • -mrelax โ€” this one gets passed to the assembler and linker, and lets the linker do certain optimizations that can't happen until the final link โœจ

โœจ MSP430 Options, continued โœจ

  • -mhwmult= โ€” ๐Ÿ’– This picks which kind of hardware multiply your MSP430 chip has. Your choices are none (no hardware multiply at all), 16bit (the original 16-bit-only multiply from the early MCUs), 32bit (the 16/32-bit multiply on the later MCUs), and f5series (the 16/32-bit multiply on the F5-series MCUs). You can also say auto, which tells GCC to figure it out from the MCU name you gave -mmcu. If you skip -mmcu or the MCU name isn't recognised, GCC assumes no hardware multiply. auto is the default, so you don't have to touch it unless you want to ๐Ÿ”ฅ

Hardware multiplies usually happen through a library routine, which saves space in your code. But when you compile at -O3 or higher, the hardware multiplier gets invoked inline instead. That makes your code bigger but faster, so it's a trade-off, bestie ๐Ÿงš

The hardware multiply routines turn off interrupts while they run and then restore the previous interrupt state when they're done. So they're totally safe to use inside interrupt handlers as well as in regular code โœจ

  • -minrt โ€” ๐ŸŒธ Turns on a minimum runtime environment, so there are no static initializers or constructors. It's made for memory-constrained devices. The compiler slips special symbols into some objects so the linker and runtime know which code fragments they need ๐Ÿซถ
  • -mtiny-printf โ€” ๐ŸŽ€ Gives you slimmed-down printf and puts library functions, which are much smaller. Heads up though: these tiny versions are not reentrant, so be careful with them in multi-threaded apps ๐Ÿ˜ญ

Streams are gone with this option. The string you want printed is always sent to stdout through the write syscall, and it isn't buffered before it goes to write. This option needs Newlib Nano IO, so GCC has to be configured with --enable-newlib-nano-formatted-io ๐Ÿ’…

  • -mmax-inline-shift= โ€” ๐Ÿฆ‹ This takes an integer from 0 to 64 inclusive and sets how many inline shift instructions can be emitted for a shift by a constant amount. Once that limit would be exceeded, an mspabi helper function is used instead. The default is 4 โœจ

This only matters when a shift by several positions can't be done with a single instruction (for example, all shifts greater than 1 on the 430 ISA) ๐Ÿ“

Shifts of a 32-bit value are at least twice as expensive, so the number you pass here gets divided by 2, and that resulting value is what's actually used ๐Ÿ‘‘

  • -mcode-region= and -mdata-region= โ€” ๐Ÿ’œ These tell the compiler where to put functions and data that don't have a lower, upper, either or section attribute. The options are lower, upper, either or any. The first three act like the matching attribute. any is the default, and it leaves placement entirely up to the linker script and how it maps the standard sections (.text, .data, etc.) onto the memory regions ๐ŸŒˆ
  • -msilicon-errata= โ€” ๐Ÿ”ฅ Passes a request on to the assembler to turn on the fixes for the named silicon errata ๐Ÿ’€
  • -msilicon-errata-warn= โ€” โญ Passes a request on to the assembler to turn on warning messages for when a silicon erratum might need a fix applied ๐Ÿ™ˆ
  • -mwarn-devices-csv and -mno-warn-devices-csv โ€” ๐Ÿ“ Warns you if devices.csv can't be found or if there's a problem parsing it. This is on by default โœจ

๐ŸŒท NDS32 Options ๐ŸŒท

These options are defined for NDS32 implementations ๐Ÿฆ‹

  • -mbig-endian โ€” ๐Ÿ’– Generates code in big-endian mode ๐ŸŒธ
  • -mlittle-endian โ€” ๐ŸŒธ Generates code in little-endian mode ๐Ÿ’–
  • -mreduced-regs โ€” ๐ŸŽ€ Uses the reduced-set registers for register allocation ๐Ÿงš
  • -mfull-regs โ€” ๐Ÿ‘‘ Uses the full-set registers for register allocation โœจ
  • -mcmov โ€” ๐Ÿ”ฅ Generates conditional move instructions ๐Ÿ’…
  • -mno-cmov โ€” ๐Ÿ™ˆ Does not generate conditional move instructions ๐Ÿ˜ญ
  • -mext-perf โ€” โญ Generates performance extension instructions ๐Ÿ”ฅ
  • -mno-ext-perf โ€” ๐Ÿ’€ Does not generate performance extension instructions ๐Ÿซถ
  • -mext-perf2 โ€” ๐ŸŒˆ Generates performance extension 2 instructions ๐Ÿ’œ
  • -mno-ext-perf2 โ€” ๐Ÿ™ˆ Does not generate performance extension 2 instructions ๐Ÿ“
  • -mext-string โ€” ๐Ÿฆ‹ Generates string extension instructions ๐ŸŽ€
  • -mno-ext-string โ€” ๐Ÿ˜ญ Does not generate string extension instructions ๐ŸŒธ
  • -mv3push โ€” ๐Ÿงš Generates v3 push25 and pop25 instructions ๐Ÿ’…
  • -mno-v3push โ€” ๐Ÿ’€ Does not generate v3 push25 and pop25 instructions ๐Ÿ‘‘
  • -m16-bit โ€” โœจ Generates 16-bit instructions ๐ŸŒˆ
  • -mno-16-bit โ€” ๐Ÿ™ˆ Does not generate 16-bit instructions ๐Ÿ’–
  • -misr-vector-size=num โ€” ๐ŸŽ€ Sets the size of each interrupt vector, which must be 4 or 16 ๐Ÿ”ฅ
  • -mcache-block-size=num โ€” ๐Ÿ“ Sets the size of each cache block, which must be a power of 2 between 4 and 512 ๐Ÿฆ‹
  • -march=arch โ€” ๐Ÿ‘‘ Sets the name of the target architecture ๐Ÿ’œ
  • -mcmodel=code-model โ€” ๐ŸŒธ Sets the code model to one of the following ๐Ÿ’…
    • small โ€” ๐Ÿงš All the data and read-only data segments must be within 512KB of addressing space. The text segment must be within 16MB of addressing space ๐Ÿซถ
    • medium โ€” ๐ŸŒˆ The data segment must be within 512KB, while the read-only data segment can be within 4GB of addressing space. The text segment should still be within 16MB of addressing space ๐Ÿ’–
    • large โ€” ๐Ÿ‘‘ All the text and data segments can be within 4GB of addressing space โœจ
  • -mctor-dtor โ€” โญ Enables the constructor/destructor feature ๐Ÿ”ฅ
  • -mrelax โ€” ๐Ÿ’… Guides the linker to relax instructions ๐Ÿฆ‹

๐ŸŒˆ Nvidia PTX Options ๐ŸŒˆ

These options are defined for Nvidia PTX ๐Ÿ’œ

  • -m64 โ€” ๐ŸŽ€ Ignored, but kept around for backward compatibility. Only the 64-bit ABI is supported ๐Ÿซถ
  • -march=architecture-string โ€” ๐Ÿ”ฅ Generates code for the PTX ISA target architecture you specify. Valid architecture strings are sm_30, sm_35, sm_37, sm_52, sm_53, sm_61, sm_70, sm_75, sm_80, and sm_89. The default depends on how the compiler was configured, so check --with-arch ๐Ÿ’€

This option sets the value of the preprocessor macro __PTX_SM__. For example, for sm_35 it has the value 350 โœจ

  • -misa=architecture-string โ€” ๐ŸŒธ Just an alias of -march= ๐Ÿ’…
  • -march-map=architecture-string โ€” ๐Ÿงš Picks the closest available -march= value that isn't more capable than what you asked for. For example, -march-map=sm_50 gives you -march=sm_37, and -march-map=sm_53 gives you -march=sm_53 ๐Ÿ‘‘
  • -mptx=version-string โ€” ๐Ÿฆ‹ Generates code for the PTX ISA version you specify. Valid version strings are 3.1, 4.1, 4.2, 5.0, 6.0, 6.3, 7.0, 7.3, and 7.8 ๐Ÿ“

The default PTX ISA version is whichever one added support for your selected PTX ISA target architecture (see -march=), but it's at least 6.3, or 7.3 for -march=sm_52 and higher ๐ŸŒˆ

This option sets the values of the preprocessor macros __PTX_ISA_VERSION_MAJOR__ and __PTX_ISA_VERSION_MINOR__. For example, for 3.1 the macros have the values 3 and 1, respectively ๐Ÿ’œ

  • -mmainkernel โ€” ๐Ÿ”ฅ Links in code for a __main kernel. This is for stand-alone execution instead of offloading ๐Ÿ™ˆ
  • -moptimize โ€” ๐Ÿ’– Applies partitioned execution optimizations. This is the default whenever any level of optimization is selected โœจ
  • -msoft-stack and -mno-soft-stack โ€” ๐Ÿซถ With -mno-soft-stack (the default, unless -mgomp has been specified), GCC uses PTX "native" stacks. That means the code uses .local memory or PTX alloca directly for stack storage. Unless -mptx=7.3 or higher and -march=sm_52 or higher are active, variable-length arrays and dynamically allocating memory on the stack with alloca are not supported ๐Ÿ˜ญ

With -msoft-stack (implied by -mgomp), the code doesn't use .local memory or PTX alloca directly for stack storage. Instead, a per-warp stack pointer is maintained explicitly ๐Ÿงš That makes variable-length stack allocation possible (with variable-length arrays or alloca). When global memory is the underlying storage, it also lets you access automatic variables from other threads, or with atomic instructions ๐ŸŒˆ This code generation variant is used for OpenMP offloading, but the option is exposed on its own so you can test the compiler. To generate code suitable for linking into programs that use OpenMP offloading, use -mgomp instead ๐Ÿ’…

  • -muniform-simt โ€” ๐Ÿ‘‘ Switches to a code generation variant that lets all threads in each warp execute, while keeping memory state and side effects as if only one thread per warp were active outside of OpenMP SIMD regions ๐Ÿ’€ All atomic operations and calls to the runtime (malloc, free, vprintf) are conditionally executed (if and only if the current lane index equals the master lane index), and the register being assigned gets copied via a shuffle instruction from the master lane ๐Ÿฆ‹ Outside of SIMD regions, lane 0 is the master. Inside them, each thread sees itself as the master ๐ŸŽ€

The shared memory array int __nvptx_uni[] stores all-zeros or all-ones bitmasks for each warp, which show the current mode (0 outside of SIMD regions) ๐Ÿ”ฅ Each thread can bitwise-AND the bitmask at position tid.y with the current lane index to compute the master lane index โœจ

  • -mgomp โ€” ๐ŸŒธ Generates code for OpenMP offloading. It enables -msoft-stack and -muniform-simt, and selects the matching multilib variant ๐Ÿซถ

๐Ÿงก OpenRISC Options ๐Ÿงก

These options are defined for OpenRISC ๐Ÿ“

  • -mboard=name โ€” ๐ŸŒˆ Configures a board-specific runtime. This gets passed to the linker for newlib board library linking. The default is or1ksim ๐Ÿ’–
  • -mnewlib โ€” ๐Ÿ™ˆ This option is ignored, it's only there for compatibility. It used to select linker and preprocessor options for use with newlib ๐Ÿ’€
  • -msoft-div and -mhard-div โ€” ๐ŸŽ€ Selects software or hardware divide (l.div, l.divu) instructions. The default is hardware divide ๐Ÿงš
  • -msoft-mul and -mhard-mul โ€” ๐Ÿ’œ Selects software or hardware multiply (l.mul, l.muli) instructions. The default is hardware multiply ๐Ÿ‘‘
  • -msoft-float and -mhard-float โ€” ๐Ÿฆ‹ Selects software or hardware for floating-point operations. The default is software ๐ŸŒธ
  • -mdouble-float โ€” ๐Ÿ”ฅ When -mhard-float is selected, this enables generation of double-precision floating-point instructions. By default, functions from libgcc are used to perform double-precision floating-point operations ๐Ÿ’…
  • -munordered-float โ€” โœจ When -mhard-float is selected, this turns on generation of unordered floating point compare and set flag (lf.sfun*) instructions ๐Ÿ”ฅ. By default, functions from libgcc handle those unordered compare and set flag operations instead ๐Ÿ’….
  • -mcmov โ€” ๐ŸŽ€ Turns on generation of conditional move (l.cmov) instructions ๐Ÿฆ‹. By default, the same result is generated using set and branch ๐ŸŒธ.
  • -mror โ€” ๐ŸŒˆ Turns on generation of rotate right (l.ror) instructions ๐Ÿ”„. By default, functions from libgcc handle rotate right operations ๐Ÿซถ.
  • -mrori โ€” โœจ Turns on generation of rotate right with immediate (l.rori) instructions ๐Ÿ’œ. By default, functions from libgcc handle rotate right with immediate operations ๐Ÿงš.
  • -msext โ€” ๐Ÿ’– Turns on generation of sign extension (l.ext*) instructions ๐Ÿ“. By default, memory loads are used to perform sign extension ๐Ÿ’€.
  • -msfimm โ€” ๐ŸŒธ Turns on generation of compare and set flag with immediate (l.sf*i) instructions ๐Ÿ’…. By default, extra instructions get generated to store the immediate into a register first ๐Ÿ™ˆ.
  • -mshftimm โ€” โญ Turns on generation of shift with immediate (l.srai, l.srli, l.slli) instructions ๐Ÿ”ฅ. By default, extra instructions get generated to store the immediate into a register first ๐Ÿ˜ญ.
  • -mcmodel=small โ€” ๐Ÿฆ‹ Generates OpenRISC code for the small model, where the GOT is limited to 64k ๐ŸŽ€. This is the default model ๐Ÿ‘‘.
  • -mcmodel=large โ€” ๐ŸŒˆ Generates OpenRISC code for the large model, where the GOT may grow up to 4G in size ๐Ÿงš.

โœจ PDP-11 Options โœจ

okay so these options are defined for the PDP-11 ๐Ÿงš:

  • -mfpu โ€” ๐Ÿ’… Uses hardware FPP floating point ๐Ÿ”ฅ. This is the default ๐Ÿ‘‘. (FIS floating point on the PDP-11/40 is not supported ๐Ÿ’€.) Implies -m45 โœจ.
  • -msoft-float โ€” ๐Ÿ™ˆ Does not use hardware floating point ๐Ÿซถ.
  • -mac0 โ€” ๐Ÿ“ Returns floating-point results in ac0 (fr0 in Unix assembler syntax) ๐Ÿ’œ.
  • -mno-ac0 โ€” ๐ŸŒธ Returns floating-point results in memory ๐Ÿฆ‹. This is the default ๐Ÿ‘‘.
  • -m40 โ€” ๐Ÿ’– Generates code for a PDP-11/40 ๐ŸŒˆ. Implies -msoft-float -mno-split ๐Ÿ”ฅ.
  • -m45 โ€” โญ Generates code for a PDP-11/45 โœจ. This is the default ๐Ÿ‘‘.
  • -m10 โ€” ๐Ÿงš Generates code for a PDP-11/10 ๐ŸŽ€. Implies -msoft-float -mno-split ๐Ÿ’….
  • -mint16, -mno-int32 โ€” ๐Ÿ“ Uses 16-bit int ๐Ÿ’œ. This is the default ๐ŸŒธ.
  • -mint32, -mno-int16 โ€” ๐Ÿ”ฅ Uses 32-bit int โœจ.
  • -msplit โ€” ๐Ÿฆ‹ The target has split instruction and data space ๐ŸŒˆ. Implies -m45 ๐Ÿ’–.
  • -munix-asm โ€” ๐Ÿ’… Uses Unix assembler syntax ๐Ÿซถ.
  • -mdec-asm โ€” ๐ŸŽ€ Uses DEC assembler syntax ๐Ÿงš.
  • -mgnu-asm โ€” โœจ Uses GNU assembler syntax ๐Ÿ’œ. This is the default ๐Ÿ‘‘.
  • -mlra โ€” ๐ŸŒˆ Uses the new LRA register allocator ๐Ÿ”„. By default, the old reload allocator is used ๐Ÿ™ˆ.

โœจ PowerPC Options โœจ

these are listed under ๐ŸŒธ


โœจ PRU Options โœจ

these command-line options are defined for the PRU target ๐Ÿ’…:

  • -minrt โ€” ๐Ÿ”ฅ Links with a minimum runtime environment ๐Ÿ’€. This can significantly reduce the size of the final ELF binary, but some standard C runtime features get removed ๐Ÿ˜ญ.
๐Ÿšจ This option disables support for static initializers and constructors. Beware that the compiler could still generate code with static initializers and constructors. It is up to the programmer to make sure the source program will not use those features ๐Ÿ™ˆ.

The minimal startup code would not pass argc and argv arguments to main, so main must be declared as int main (void) ๐Ÿงš. This is already the norm for most firmware projects โœจ.

  • -mmcu=mcu โ€” ๐Ÿ’– Specifies the PRU hardware variant to use ๐ŸŒˆ. A correspondingly named spec file would be loaded, passing the memory region sizes to the linker and defining hardware-specific C macros ๐Ÿฆ‹.

Newlib provides only the sim spec, which is meant for running regression tests using a simulator ๐ŸŽ€. Specs for real hardware can be obtained by installing the GnuPruMcu (https://github.com/dinuxbg/gnuprumcu/) package ๐Ÿซถ.

  • -mno-relax โ€” ๐Ÿ“ Makes GCC pass the --no-relax command-line option to the linker instead of the --relax option ๐Ÿ”„.
  • -mloop โ€” ๐Ÿ’œ Allows (or does not allow) GCC to use the LOOP instruction ๐ŸŒธ.
  • -mabi=variant โ€” ๐Ÿ‘‘ Specifies the ABI variant to output code for ๐Ÿ’…. -mabi=ti selects the unmodified TI ABI, while -mabi=gnu selects a GNU variant that copes more naturally with certain GCC assumptions ๐Ÿซถ. These are the differences ๐Ÿ”ฅ:

โœจ Function Pointer Size โœจ

The TI ABI specifies that function (code) pointers are 16-bit ๐Ÿ’€, whereas GNU supports only 32-bit data and code pointers ๐Ÿ˜ญ.

โœจ Optional Return Value Pointer โœจ

Function return values larger than 64 bits are passed by using a hidden pointer as the first argument of the function ๐Ÿงš. The TI ABI, though, mandates that the pointer can be NULL in case the caller is not using the returned value ๐Ÿ™ˆ. GNU always passes and expects a valid return value pointer ๐Ÿ’–.

โš ๏ธ The current -mabi=ti implementation simply raises a compile error when any of the above code constructs is detected ๐Ÿ˜ญ. As a consequence, the standard C library cannot be built and it is omitted when linking with -mabi=ti ๐Ÿ’€.

Relaxation is a GNU feature, and for safety reasons it is disabled when using -mabi=ti ๐ŸŒˆ. The TI toolchain does not emit relocations for QBBx instructions, so the GNU linker cannot adjust them when shortening adjacent LDI32 pseudo instructions ๐Ÿฆ‹.


โœจ RISC-V Options โœจ

these command-line options are defined for RISC-V targets ๐ŸŒˆ:

  • -mbranch-cost=n โ€” ๐ŸŽ€ Sets the cost of branches to roughly n instructions ๐Ÿ’….
  • -mplt, -mno-plt โ€” ๐Ÿซถ When generating PIC code, does or doesn't allow the use of PLTs ๐Ÿ”ฅ. Ignored for non-PIC ๐Ÿ™ˆ. The default is -mplt โœจ.
  • -mabi=ABI-string โ€” ๐Ÿ’œ Specifies the integer and floating-point calling convention ๐Ÿงš. The ABI-string has two parts: the size of integer types and the registers used for floating-point types ๐Ÿ‘‘. For example, -march=rv64ifd -mabi=lp64d means that long and pointers are 64-bit (implicitly defining int to be 32-bit), and that floating-point values up to 64 bits wide are passed in F registers ๐Ÿฆ‹. Compare that with -march=rv64ifd -mabi=lp64f, which still lets the compiler generate code that uses the F and D extensions but only allows floating-point values up to 32 bits long to be passed in registers ๐ŸŒธ; or -march=rv64ifd -mabi=lp64, in which no floating-point arguments get passed in registers at all ๐Ÿ’€.

The default for this argument is system dependent, so users who want a specific calling convention should specify one explicitly ๐Ÿ˜ญ. The valid calling conventions are: ilp32, ilp32f, ilp32d, lp64, lp64f, and lp64d ๐Ÿ’–. Some calling conventions are impossible to implement on some ISAs ๐Ÿ™ˆ: for example, -march=rv32if -mabi=ilp32d is invalid, because the ABI requires 64-bit values to be passed in F registers, but F registers are only 32 bits wide ๐Ÿšซ. There is also the ilp32e ABI, which can only be used with the rv32e architecture, and the lp64e ABI, which can only be used with rv64e ๐Ÿงš. Those ABIs are not well specified at present and are subject to change โš ๏ธ.

  • -mfdiv, -mno-fdiv โ€” โœจ Does or doesn't use hardware floating-point divide and square root instructions ๐Ÿ”ฅ. This requires the F or D extensions for floating-point registers ๐Ÿซถ. The default is to use them if the specified architecture has these instructions ๐Ÿ‘‘.
  • -mfence-tso, -mno-fence-tso โ€” ๐ŸŒˆ Does or doesn't use the fence.tso instruction ๐Ÿฆ‹, which is unimplemented on some processors (including those from T-Head) ๐Ÿ’€. If the fence.tso instruction is not available, a stronger fence is used instead ๐ŸŽ€.
  • -mdiv, -mno-div โ€” ๐Ÿ’… Does or doesn't use hardware instructions for integer division ๐Ÿ’œ. This requires the M extension ๐Ÿ”ฅ. The default is to use them if the specified architecture has these instructions ๐ŸŒธ.
  • -misa-spec=ISA-spec-string โ€” ๐Ÿงš Specifies the version of the RISC-V Unprivileged (formerly User-Level) ISA specification to produce code conforming to ๐Ÿ“œ. The possibilities for ISA-spec-string are ๐Ÿ“:
    • 2.2 โ€” produces code conforming to version 2.2 โœจ.
    • 20190608 โ€” produces code conforming to version 20190608 ๐Ÿฆ‹.
    • 20191213 โ€” produces code conforming to version 20191213 ๐Ÿ’–.

The default is -misa-spec=20191213, unless GCC has been configured with --with-isa-spec= specifying a different default version ๐Ÿ‘‘.

  • -march=ISA-string โ€” ๐ŸŒˆ Generates code for the given RISC-V ISA (e.g. rv64im) ๐Ÿ’…. ISA strings must be lower-case ๐Ÿซถ. Examples include rv64i, rv32g, rv32e, and rv32imaf โœจ. Additionally, a special value, help (-march=help), is accepted to list all supported extensions ๐Ÿงš.

The syntax of the ISA string is defined as follows ๐Ÿ”ฅ:

  • The string must start with rv32 or rv64, followed by i, e, or g, referred to as the base ISA ๐Ÿ’œ.
  • The subsequent part of the string is a list of extension names ๐ŸŒธ. Extension names can be categorized as multi-letter (e.g. zba) and single-letter (e.g. v) ๐Ÿฆ‹. Single-letter extensions can appear consecutively, but multi-letter extensions must be separated by underscores ๐ŸŽ€.
  • An underscore can appear anywhere after the base ISA ๐Ÿซถ. It has no specific effect, but it is used to improve readability and can act as a separator โœจ.
  • Extension names may include an optional version number, following the syntax <major>p<minor> or <major> (e.g. m2p1 or m2) ๐Ÿ’–.

Supported extensions are listed below ๐Ÿ‘‘:

  • i โ€” version 2.0, 2.1 โ€” Base integer extension ๐Ÿ’….
  • e โ€” version 2.0 โ€” Reduced base integer extension ๐ŸŒธ.
  • g โ€” no version listed โ€” General-purpose computing base extension, where g will expand to i, m, a, f, d, zicsr and zifencei ๐ŸŒˆ.
  • m โ€” version 2.0 โ€” Integer multiplication and division extension ๐Ÿ”ฅ.
  • a โ€” version 2.0, 2.1 โ€” Atomic extension ๐Ÿฆ‹.
  • f โ€” version 2.0, 2.2 โ€” Single-precision floating-point extension ๐Ÿงš.
  • d โ€” version 2.0, 2.2 โ€” Double-precision floating-point extension ๐Ÿ’œ.
  • c โ€” version 2.0 โ€” Compressed extension ๐ŸŽ€.
  • h โ€” version 1.0 โ€” Hypervisor extension ๐Ÿ“.
  • v โ€” version 1.0 โ€” Vector extension โœจ.
  • zicsr โ€” version 2.0 โ€” Control and status register access extension ๐Ÿซถ.
  • zifencei โ€” version 2.0 โ€” Instruction-fetch fence extension ๐Ÿ’€.
  • zicond โ€” version 1.0 โ€” Integer conditional operations extension ๐ŸŒˆ.
  • za64rs โ€” version 1.0 โ€” Reservation set size of 64 bytes ๐Ÿฆ‹.
  • za128rs โ€” version 1.0 โ€” Reservation set size of 128 bytes ๐Ÿ’–.
  • zawrs โ€” version 1.0 โ€” Wait-on-reservation-set extension โญ.
  • zba โ€” version 1.0 โ€” Address calculation extension ๐Ÿ”ฅ.
  • zbb (1.0) โ€” Basic bit manipulation, bestie, the whole vibe ๐Ÿฆ‹
  • zbc (1.0) โ€” Carry-less multiplication, very math-coded ๐Ÿงฎ
  • zbs (1.0) โ€” Single-bit operations, tiny but mighty โœจ
  • zfinx (1.0) โ€” Single-precision floats living in integer registers ๐Ÿ’…
  • zdinx (1.0) โ€” Double-precision floats living in integer registers ๐Ÿ’–
  • zhinx (1.0) โ€” Half-precision floats living in integer registers ๐ŸŒธ
  • zhinxmin (1.0) โ€” The minimal half-precision-in-integer-registers version ๐Ÿซถ
  • zbkb (1.0) โ€” Cryptography bit manipulation ๐Ÿ”
  • zbkc (1.0) โ€” Cryptography carry-less multiply ๐Ÿ”
  • zbkx (1.0) โ€” Cryptography crossbar permutation ๐Ÿ”
  • zkne (1.0) โ€” AES encryption, secret agent stuff ๐Ÿ•ต๏ธ
  • zknd (1.0) โ€” AES decryption, undoing the secret agent stuff ๐Ÿ•ต๏ธ
  • zknh (1.0) โ€” Hash functions ๐Ÿง‚
  • zkr (1.0) โ€” Entropy source, the randomness queen ๐ŸŽฒ
  • zksed (1.0) โ€” SM4 block cipher ๐Ÿ€„
  • zksh (1.0) โ€” SM3 hash function ๐Ÿ€„
  • zkt (1.0) โ€” Data independent execution latency, no timing drama ๐Ÿ•ฐ๏ธ
  • zk (1.0) โ€” Standard scalar cryptography, the whole package ๐Ÿ“ฆ
  • zkn (1.0) โ€” NIST algorithm suite ๐Ÿ‡บ๐Ÿ‡ธ
  • zks (1.0) โ€” ShangMi algorithm suite ๐Ÿ‡จ๐Ÿ‡ณ
  • zihintntl (1.0) โ€” Non-temporal locality hints ๐Ÿ’œ
  • zihintpause (1.0) โ€” Pause hints, take a breath bestie ๐Ÿง˜
  • zicboz (1.0) โ€” Cache-block zero ๐ŸงŠ
  • zicbom (1.0) โ€” Cache-block management ๐ŸงŠ
  • zicbop (1.0) โ€” Cache-block prefetch ๐ŸงŠ
  • zic64b (1.0) โ€” Cache blocks are 64 bytes ๐Ÿ“
  • ziccamoa (1.0) โ€” Main memory supports all the atomics in A โš›๏ธ
  • ziccif (1.0) โ€” Main memory supports instruction fetch with the atomicity requirement ๐Ÿง 
  • zicclsm (1.0) โ€” Main memory supports misaligned loads and stores ๐Ÿ™ˆ
  • ziccrse (1.0) โ€” Main memory supports forward progress on LR/SC sequences ๐Ÿƒโ€โ™€๏ธ
  • zicntr (2.0) โ€” Standard base counters and timers โฐ
  • zihpm (2.0) โ€” Standard hardware performance counters ๐Ÿ“ˆ
  • ztso (1.0) โ€” Total store ordering, very orderly ๐Ÿ“š
  • zve32x (1.0) โ€” Vector extensions for embedded processors ๐ŸŒˆ
  • zve32f (1.0) โ€” Vector extensions for embedded processors ๐ŸŒˆ
  • zve64x (1.0) โ€” Vector extensions for embedded processors ๐ŸŒˆ
  • zve64f (1.0) โ€” Vector extensions for embedded processors ๐ŸŒˆ
  • zve64d (1.0) โ€” Vector extensions for embedded processors ๐ŸŒˆ
  • zvl32b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvl64b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvl128b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvl256b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvl512b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvl1024b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvl2048b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvl4096b (1.0) โ€” Minimum vector length standard extension ๐Ÿ“
  • zvbb (1.0) โ€” Vector basic bit manipulation ๐Ÿฆ‹
  • zvbc (1.0) โ€” Vector carry-less multiplication ๐Ÿงฎ
  • zvkb (1.0) โ€” Vector cryptography bit manipulation ๐Ÿ”
  • zvkg (1.0) โ€” Vector GCM/GMAC ๐Ÿ”
  • zvkned (1.0) โ€” Vector AES block cipher ๐Ÿ•ต๏ธ
  • zvknha (1.0) โ€” Vector SHA-2 secure hash ๐Ÿง‚
  • zvknhb (1.0) โ€” Vector SHA-2 secure hash ๐Ÿง‚
  • zvksed (1.0) โ€” Vector SM4 block cipher ๐Ÿ€„
  • zvksh (1.0) โ€” Vector SM3 secure hash ๐Ÿ€„
  • zvkn (1.0) โ€” Vector NIST algorithm suite, which expands to zvkned, zvknhb, zvkb and zvkt ๐Ÿ‘‘
  • zvknc (1.0) โ€” Vector NIST algorithm suite with carry-less multiply, which expands to zvkn and zvbc ๐Ÿ‘‘
  • zvkng (1.0) โ€” Vector NIST algorithm suite with GCM, which expands to zvkn and zvkg ๐Ÿ‘‘
  • zvks (1.0) โ€” Vector ShangMi algorithm suite, which expands to zvksed, zvksh, zvkb and zvkt ๐Ÿ‘‘
  • zvksc (1.0) โ€” Vector ShangMi algorithm suite with carry-less multiplication, which expands to zvks and zvbc ๐Ÿ‘‘
  • zvksg (1.0) โ€” Vector ShangMi algorithm suite with GCM, which expands to zvks and zvkg ๐Ÿ‘‘
  • zvkt (1.0) โ€” Vector data independent execution latency ๐Ÿ•ฐ๏ธ
  • zfh (1.0) โ€” Half-precision floating point ๐ŸŒธ
  • zfhmin (1.0) โ€” Minimal half-precision floating point ๐ŸŒธ
  • zvfh (1.0) โ€” Vector half-precision floating point ๐ŸŒธ
  • zvfhmin (1.0) โ€” Vector minimal half-precision floating point ๐ŸŒธ
  • zvfbfmin (1.0) โ€” Vector BF16 converts ๐Ÿ”„
  • zfa (1.0) โ€” Additional floating point, extra spicy ๐ŸŒถ๏ธ
  • zmmul (1.0) โ€” Integer multiplication โœ–๏ธ
  • zca (1.0) โ€” Integer compressed instructions ๐Ÿ—œ๏ธ
  • zcf (1.0) โ€” Compressed single-precision floating point loads and stores ๐Ÿ—œ๏ธ
  • zcd (1.0) โ€” Compressed double-precision floating point loads and stores ๐Ÿ—œ๏ธ
  • zcb (1.0) โ€” Simple compressed instructions ๐Ÿ—œ๏ธ
  • zce (1.0) โ€” Compressed instruction extensions for embedded processors ๐Ÿ—œ๏ธ
  • zcmp (1.0) โ€” Compressed push and pop ๐Ÿ—œ๏ธ
  • zcmt (1.0) โ€” Table jump instructions ๐Ÿ“‹
  • smaia (1.0) โ€” Advanced interrupt architecture ๐Ÿšจ
  • smepmp (1.0) โ€” PMP enhancements for memory access and execution prevention in Machine mode ๐Ÿ›ก๏ธ
  • smstateen (1.0) โ€” State enable ๐Ÿšช
  • ssaia (1.0) โ€” Advanced interrupt architecture for supervisor mode ๐Ÿšจ
  • sscofpmf (1.0) โ€” Count overflow and filtering ๐Ÿ”ข
  • ssstateen (1.0) โ€” State-enable for supervisor mode ๐Ÿšช
  • sstc (1.0) โ€” Supervisor-mode timer interrupts โฐ
  • svinval (1.0) โ€” Fine-grained address-translation cache invalidation ๐Ÿงน
  • svnapot (1.0) โ€” NAPOT translation contiguity ๐Ÿ—บ๏ธ
  • svpbmt (1.0) โ€” Page-based memory types ๐Ÿ—‚๏ธ
  • xcvmac (1.0) โ€” Core-V multiply-accumulate ๐Ÿงฉ
  • xcvalu (1.0) โ€” Core-V miscellaneous ALU ๐Ÿงฉ
  • xcvelw (1.0) โ€” Core-V event load word ๐Ÿงฉ
  • xtheadba (1.0) โ€” T-Head address calculation ๐Ÿ‰
  • xtheadbb (1.0) โ€” T-Head basic bit manipulation ๐Ÿ‰
  • xtheadbs (1.0) โ€” T-Head single-bit instructions ๐Ÿ‰
  • xtheadcmo (1.0) โ€” T-Head cache management operations ๐Ÿ‰
  • xtheadcondmov (1.0) โ€” T-Head conditional move ๐Ÿ‰
  • xtheadfmemidx (1.0) โ€” T-Head indexed memory operations for floating-point registers ๐Ÿ‰
  • xtheadfmv (1.0) โ€” T-Head double floating-point high-bit data transmission ๐Ÿ‰
  • xtheadint (1.0) โ€” T-Head acceleration interruption ๐Ÿ‰
  • xtheadmac (1.0) โ€” T-Head multiply-accumulate ๐Ÿ‰
  • xtheadmemidx (1.0) โ€” T-Head indexed memory operations ๐Ÿ‰
  • xtheadmempair (1.0) โ€” T-Head two-GPR memory operations ๐Ÿ‰
  • xtheadsync (1.0) โ€” T-Head multi-core synchronization ๐Ÿ‰
  • xventanacondops (1.0) โ€” Ventana integer conditional operations ๐ŸŒท

When -march= is not specified, the setting from -mcpu is used, bestie ๐Ÿ’–

If both -march and -mcpu= are left out, the default here depends on your system ๐Ÿ–ฅ๏ธ so if you want a specific architecture extension, you should totally name it yourself ๐Ÿ’…

When the RISC-V specs say one extension depends on others, GCC will automatically add those dependent extensions to the enabled set, as long as you didn't add them yourself โœจ


  • -mcpu=processor-string โ€” Use the architecture of, and optimize the output for, the given processor, identified by its particular CPU name ๐Ÿง  The permitted values are: sifive-e20, sifive-e21, sifive-e24, sifive-e31, sifive-e34, sifive-e76, sifive-s21, sifive-s51, sifive-s54, sifive-s76, sifive-u54, sifive-u74, sifive-x280, sifive-xp450, sifive-x670, thead-c906, tt-ascalon-d8, and xiangshan-nanhu ๐Ÿฃ

Note that -mcpu does not override -march or -mtune ๐Ÿ™…โ€โ™€๏ธ

  • -mtune=processor-string โ€” Optimize the output for the given processor, identified by its microarchitecture or particular CPU name ๐ŸŽฏ The permitted values are: generic-ooo, rocket, sifive-3-series, sifive-5-series, sifive-7-series, size, sifive-p400-series, sifive-p600-series, and all valid options for -mcpu= ๐Ÿ’œ

When -mtune= is not specified, the setting from -mcpu is used, and if neither is given, the default is rocket ๐Ÿš€

The size choice is not meant for end-users, lowkey an internal thing ๐Ÿ™ˆ It gets used when -Os is specified. It overrides the instruction cost info provided by -mtune=, but it does not override the pipeline info. This helps shrink the code size while still giving good performance ๐Ÿ“‰โœจ

  • -mpreferred-stack-boundary=num โ€” Try to keep the stack boundary aligned to a boundary of 2 raised to num bytes ๐Ÿ“ If -mpreferred-stack-boundary is not specified, the default is 4 (16 bytes, or 128 bits) ๐ŸŒŸ
โš ๏ธ Warning, bestie: if you use this switch, you must build all modules with the same value, including any libraries. That includes the system libraries and startup modules ๐Ÿ˜ญ

-msmall-data-limit=n is my favorite way to shrink things ๐Ÿงš. It puts global and static data smaller than n bytes into a special section, but only on some targets ๐ŸŒธ

  • -msave-restore, -mno-save-restore โ€” okay so these decide whether the prologue and epilogue code gets smaller but slower by calling library functions ๐Ÿ’…. The default is the fast inline prologues and epilogues โœจ
  • -mmovcc, -mno-movcc โ€” -mmovcc makes GCC produce branchless conditional-move sequences, even on targets without dedicated conditional instructions ๐Ÿฆ‹. When it's on, it builds sequences of ALU operations out of base integer ISA instructions wherever that's worth it ๐Ÿ”ฅ
  • -minline-atomics, -mno-inline-atomics โ€” -mno-inline-atomics is the smaller but slower option, using subword atomic emulation that calls libatomic functions ๐Ÿ™ˆ. The default is fast inline subword atomics that don't need libatomic at all ๐Ÿ’–
  • -minline-strlen, -mno-inline-strlen โ€” this one tries to inline strlen calls when it can ๐Ÿ“. It only happens if the string is properly aligned and the accelerated instructions are available. By default, strlen calls are not inlined ๐Ÿ’€
  • -minline-strcmp, -mno-inline-strcmp โ€” same vibe as strlen ๐Ÿ’…. GCC only inlines strcmp calls when the strings are properly aligned and the accelerated instructions exist. The default is to not inline strcmp calls โœจ

The --param riscv-strcmp-inline-limit=n parameter controls the maximum number of bytes that the inlined code compares ๐Ÿ”ฅ. Its default value is 64 ๐Ÿซถ

  • -minline-strncmp, -mno-inline-strncmp โ€” also the strcmp energy but for strncmp ๐ŸŒˆ. Inlining only happens when the strings are properly aligned and the accelerated instructions are around. The default is to not inline strncmp calls ๐Ÿ™…โ€โ™€๏ธ

Same deal here: --param riscv-strcmp-inline-limit=n sets the maximum number of bytes compared by the inlined code, and the default is 64 ๐Ÿ’œ

  • -mshorten-memrefs, -mno-shorten-memrefs โ€” this is a sneaky one ๐Ÿฆ‹. With it on, GCC tries to use more compressed load/store instructions by swapping a load/store of base register + large offset for a new load/store of new base + small offset. If that new base lands in a compressed register, the new load/store can be compressed too โœจ. Right now it only works on 32-bit integer load/stores ๐ŸŒธ
  • -mstrict-align, -mno-strict-align โ€” okay so this is about whether unaligned memory accesses are generated. The wording in the original is a little muddled ("Do not or do generate"), but the gist is that the default depends on whether the processor you're optimizing for handles fast unaligned access ๐Ÿ™ˆ
  • -mscalar-strict-align, -mno-scalar-strict-align โ€” same unaligned-access drama as above ๐Ÿ’…. The default also depends on the processor's fast unaligned access support. This one is just an alias for -mstrict-align ๐Ÿซถ
  • -mvector-strict-align, -mno-vector-strict-align โ€” this is the vector version of the story ๐Ÿ’–. Unaligned vector memory accesses are off by default, unless the processor you're targeting explicitly supports element-misaligned vector memory access โœจ
  • -mcmodel=medlow โ€” the medium-low code model ๐Ÿงš. The program and its statically defined symbols have to fit inside one 2 GiB address range, sitting between absolute addresses -2 GiB and +2 GiB ๐Ÿ“. Programs can be statically or dynamically linked. This is the default code model, unless GCC was configured with --with-cmodel= pointing at a different default ๐Ÿ‘‘
  • -mcmodel=medany โ€” the medium-any code model ๐ŸŒˆ. The program and its statically defined symbols just have to be within any single 2 GiB address range. Static or dynamic linking both work ๐Ÿ’œ
The code from medium-any is position-independent, but it is not guaranteed to work when linked into position-independent executables or libraries ๐Ÿ’€
  • -mcmodel=large โ€” the big boss code model ๐Ÿ‘‘. It has no restrictions on the size or placement of symbols ๐Ÿ”ฅ
  • -mexplicit-relocs, -mno-exlicit-relocs โ€” -mexplicit-relocs means the compiler uses assembler relocation operators when handling symbolic addresses ๐Ÿฆ‹. The alternative is assembler macros, which may limit optimization ๐Ÿ™ˆ. (The original spells the second one -mno-exlicit-relocs, so that's exactly what's in the code here ๐Ÿซถ)
  • -mrelax, -mno-relax โ€” this takes advantage of linker relaxations to cut down the number of instructions needed to materialize symbol addresses โœจ. The default is to take advantage of linker relaxations ๐Ÿ’…
  • -mriscv-attribute, -mno-riscv-attribute โ€” -mriscv-attribute emits a RISC-V attribute that records extra info into ELF objects, and -mno-riscv-attribute skips emitting it ๐ŸŒธ. Heads up: this feature needs at least binutils 2.32 ๐Ÿซถ
  • -mcsr-check, -mno-csr-check โ€” these enable or disable the CSR checking ๐Ÿงš
  • -malign-data=type โ€” controls how GCC aligns variables and constants of array, structure, or union types ๐Ÿ’–. The supported values for type are xlen, which uses the x register width as the alignment value, and natural, which uses natural alignment. xlen is the default ๐Ÿ‘‘
  • -mbig-endian โ€” generates big-endian code ๐ŸŒˆ. This is the default when GCC is configured for a riscv64be-*-* or riscv32be-*-* target ๐Ÿ’œ
  • -mlittle-endian โ€” generates little-endian code โœจ. This is the default when GCC is configured for a riscv64-*-* or riscv32-*-* target, but not a riscv64be-*-* or riscv32be-*-* target ๐Ÿซถ
  • -mstack-protector-guard=guard, -mstack-protector-guard-reg=reg, -mstack-protector-guard-offset=offset โ€” these generate stack protection code that uses a canary at guard ๐Ÿ›ก๏ธ. The supported locations are global for a global canary, or tls for a per-thread canary in the TLS block ๐Ÿงš

With the tls choice, -mstack-protector-guard-reg=reg and -mstack-protector-guard-offset=offset are the ones that get specific: they pick which register is the base for reading the canary, and what offset from that base to read at ๐Ÿ”ฅ. There's no default register or offset, because this is entirely for use within the Linux kernel ๐Ÿง

  • -mtls-dialect=desc โ€” uses TLS descriptors as the thread-local storage mechanism for dynamic accesses to TLS variables โœจ
  • -mtls-dialect=trad โ€” uses traditional TLS as the thread-local storage mechanism for dynamic accesses to TLS variables ๐ŸŒธ. This is the default ๐Ÿ’…

๐Ÿ’œ RL78 Options ๐Ÿ’œ

  • -msim โ€” links in extra target libraries so the program can run inside a simulator ๐Ÿงš
  • -mmul=none, -mmul=g10, -mmul=g13, -mmul=g14, -mmul=rl78 โ€” picks the type of hardware multiplication and division support to use ๐Ÿ”ฅ. The simplest is "none", which uses software for both multiplication and division, and it's the default ๐Ÿ™ˆ. "g13" is for the hardware multiply/divide peripheral on the RL78/G13 (S2 core) targets ๐Ÿฆ‹. "g14" turns on the multiplication and division instructions from the RL78/G14 (S3 core) parts ๐Ÿ‘‘. "rl78" is an alias for "g14", and "mg10" is an alias for "none" ๐Ÿ’–

A C preprocessor macro also gets defined based on this option. The possible values are "__RL78_MUL_NONE__", "__RL78_MUL_G13__", or "__RL78_MUL_G14__" โœจ

  • -mcpu=g10, -mcpu=g13, -mcpu=g14, -mcpu=rl78 โ€” specifies which RL78 core to target ๐Ÿ’…. The default is the G14 core, also known as an S3 core or just RL78 ๐Ÿ‘‘. The G13 (S2) core doesn't have multiply or divide instructions, and uses a hardware peripheral for those operations instead ๐Ÿฆ‹. The G10 (S1) core has no register banks, so it uses a different calling convention ๐Ÿ™ˆ

If this option is set, it also picks the hardware multiply support to use, unless you override it with an explicit -mmul=none on the command line ๐Ÿซถ. So -mcpu=g13 turns on the G13 hardware multiply peripheral, and -mcpu=g10 turns off hardware multiplication altogether ๐Ÿ’€

Heads up: even though the RL78/G14 core is the default target, passing -mcpu=g14 or -mcpu=rl78 on the command line does change how the toolchain behaves, because it also enables G14 hardware multiply support ๐Ÿ˜ญ. If you don't specify these on the command line, software multiplication routines get used even though the code targets the RL78 core. This is for backwards compatibility with older toolchains that didn't have hardware multiply and divide support ๐Ÿฅบ

A C preprocessor macro also gets defined based on this option. The possible values are "__RL78_G10__", "__RL78_G13__", or "__RL78_G14__" ๐ŸŒˆ

  • -mg10, -mg13, -mg14, -mrl78 โ€” these are aliases for the matching -mcpu= option ๐ŸŽ€. They're only here for backwards compatibility ๐Ÿงš
  • -mallregs โ€” lets the compiler use all of the available registers ๐Ÿ’ช. By default, registers r24..r31 are reserved for interrupt handlers. With this on, those registers can be used in ordinary functions too โœจ
  • -m64bit-doubles, -m32bit-doubles โ€” makes the "double" data type 64 bits (-m64bit-doubles) or 32 bits (-m32bit-doubles) ๐ŸŒธ. The default is -m32bit-doubles ๐Ÿ’–
  • -msave-mduc-in-interrupts, -mno-save-mduc-in-interrupts โ€” tells interrupt handler functions to preserve the MDUC registers ๐Ÿซถ. You only really need this if normal code might touch the MDUC registers, like if it does multiplication and division. By default, the MDUC registers get ignored, because that makes interrupt handlers faster ๐Ÿ”ฅ. The target option -mg13 has to be passed for this to work, since this feature only exists on the G13 target (S2 core) ๐Ÿฆ‹. The MDUC registers only get saved if the interrupt handler does a multiplication or division, or calls another function ๐Ÿ‘‘

๐ŸŒท IBM RS/6000 and PowerPC Options ๐ŸŒท

These -m options are defined for the IBM RS/6000 and PowerPC ๐Ÿงš

  • -mpowerpc-gpopt, -mno-powerpc-gpopt, -mpowerpc-gfxopt, -mno-powerpc-gfxopt, -mpowerpc64, -mno-powerpc64, -mmfcrf, -mno-mfcrf, -mpopcntb, -mno-popcntb, -mpopcntd, -mno-popcntd, -mfprnd, -mno-fprnd, -mcmpb, -mno-cmpb, -mhard-dfp, -mno-hard-dfp โ€” these are the options that say which instructions are available on the processor you're using ๐Ÿ’…. Their default value gets decided when GCC is configured ๐ŸŒธ. Specifying -mcpu=cpu_type overrides whatever these options say ๐Ÿ‘‘. We recommend using -mcpu=cpu_type instead of the options listed above ๐Ÿ’–

Okay so the PowerPC options are where things get really juicy ๐Ÿ’…โœจ

Turning on -mpowerpc-gpopt lets GCC use the optional PowerPC instructions from the General Purpose group, including floating-point square root ๐ŸŒธ And -mpowerpc-gfxopt unlocks the optional instructions from the Graphics group, like floating-point select ๐Ÿฆ‹

-mmfcrf lets GCC generate the move from condition register field instruction. It's on the POWER4 processor and other chips that speak PowerPC V2.01 ๐Ÿ”ฅ Then there's -mpopcntb, which gets you the popcount instruction and the double-precision FP reciprocal estimate instruction. That one's from POWER5 and other V2.02 chips ๐Ÿ’œ -mpopcntd gives you the popcount instruction from POWER7 and other V2.06 processors ๐ŸŽ€ -mfprnd lets GCC emit the FP round to integer instructions, found on POWER5+ and other V2.03 chips โญ -mcmpb is the compare bytes instruction from POWER6 and other V2.05 chips ๐Ÿซถ And -mhard-dfp turns on the decimal floating-point instructions that show up on some POWER processors ๐Ÿงš

-mpowerpc64 is the big one for 64-bit energy. It lets GCC generate the extra 64-bit instructions from the full PowerPC64 architecture and treat GPRs as 64-bit doubleword quantities ๐Ÿ‘‘ Heads up, GCC defaults to -mno-powerpc64, so you have to ask for it ๐Ÿ™ˆ


  • -mcpu=cpu_type โ€” this sets the architecture type, register usage, and instruction scheduling parameters for the machine type you name as cpu_type. The values you can pick are 401, 403, 405, 405fp, 440, 440fp, 464, 464fp, 476, 476fp, 505, 601, 602, 603, 603e, 604, 604e, 620, 630, 740, 7400, 7450, 750, 801, 821, 823, 860, 970, 8540, a2, e300c2, e300c3, e500mc, e500mc64, e5500, e6500, ec603e, G3, G4, G5, titan, power3, power4, power5, power5+, power6, power6x, power7, power8, power9, power10, power11, powerpc, powerpc64, powerpc64le, rs64, and native โœจ

Okay so -mcpu=powerpc, -mcpu=powerpc64, and -mcpu=powerpc64le are the generic vibes ๐ŸŒˆ They give you pure 32-bit PowerPC (either endian), 64-bit big endian PowerPC, and 64-bit little endian PowerPC. GCC assumes an appropriate generic processor model for scheduling, so nothing too specific ๐Ÿฆ‹

Then there's native, which is the main character move ๐Ÿ‘‘ It detects the host processor on the machine doing the compiling and picks the architecture option that matches it. Just know that -mcpu=native does nothing if GCC doesn't recognize the processor ๐Ÿ’€

Everything else names one specific processor. Code built for that chip runs its best there, and it might not run at all on other chips ๐Ÿ˜ญ

The -mcpu option quietly turns a bunch of other options on or off for you ๐Ÿซถ The list is -maltivec, -mfprnd, -mhard-float, -mmfcrf, -mmultiple, -mpopcntb, -mpopcntd, -mpowerpc64, -mpowerpc-gpopt, -mpowerpc-gfxopt, -mmulhw, -mdlmzb, -mmfpgpr, -mvsx, -mcrypto, -mhtm, -mpower8-fusion, -mquad-memory, -mquad-memory-atomic, -mfloat128, -mfloat128-hardware, -mprefixed, -mpcrel, -mmma, and -mrop-protect ๐ŸŒธ

The exact set of options for each CPU changes between compiler versions. GCC picks whatever seems to make the best code for that chip, so it doesn't always match what the hardware can actually do ๐Ÿ™ˆ

Want to override one? Just put it after -mcpu, like -mcpu=970 -mno-altivec. Order matters here bestie ๐Ÿ’…

On AIX, -maltivec and -mpowerpc64 don't get flipped by -mcpu right now, because AIX doesn't fully support them yet. You can still turn them on or off yourself if you're sure it'll work in your environment ๐ŸŽ€

  • -mtune=cpu_type โ€” this only sets the instruction scheduling parameters for cpu_type. It doesn't touch the architecture type or register usage like -mcpu=cpu_type does โœจ It takes the same values as -mcpu. If you use both, the code uses the architecture and registers from -mcpu, but the scheduling comes from -mtune ๐Ÿฆ‹
  • -mcmodel=small โ€” generates PowerPC64 code for the small model, where the TOC is capped at 64k ๐Ÿงš
  • -mcmodel=medium โ€” generates PowerPC64 code for the medium model, where the TOC and other static data can total up to 4G. This is the default for 64-bit Linux ๐Ÿ’œ
  • -mcmodel=large โ€” generates PowerPC64 code for the large model. The TOC can be up to 4G, and other data and code is only limited by the 64-bit address space ๐Ÿ‘‘
  • -maltivec, -mno-altivec โ€” -maltivec generates code that uses AltiVec instructions and also turns on the built-in functions that give you more direct access to the AltiVec instruction set ๐Ÿ”ฅ -mno-altivec does the opposite. You might also need -mabi=altivec to adjust the current ABI with AltiVec ABI enhancements ๐ŸŒˆ
When you use -maltivec, the element order for AltiVec intrinsics like "vec_splat", "vec_extract", and "vec_insert" matches the array element order for your target's endianness. So on a big-endian platform, element zero is the leftmost element in a vector register. On a little-endian platform, element zero is the rightmost one ๐Ÿ™ˆ
  • -mvrsave, -mno-vrsave โ€” -mvrsave generates VRSAVE instructions when it's making AltiVec code. -mno-vrsave skips them ๐Ÿฆ‹
  • -msecure-plt โ€” generates code that lets ld and ld.so build executables and shared libraries with non-executable ".plt" and ".got" sections. This one's a PowerPC 32-bit SYSV ABI option ๐Ÿซถ
  • -mbss-plt โ€” generates code that uses a BSS ".plt" section, which ld.so fills in. It needs ".plt" and ".got" sections that are both writable and executable ๐Ÿ˜ญ Also a PowerPC 32-bit SYSV ABI option ๐Ÿงš
  • -msplit-patch-nops โ€” when you add NOPs for a patchable area with -fpatchable-function-entry, this puts the "before" NOPs in front of the global entry point and the "after" NOPs after the local entry point. That means the NOPs aren't consecutive when a global entry point gets generated โœจ Without this option, the NOPs go right before and right after the local entry point. That keeps them consecutive, but it pushes the global and local entry points further apart ๐Ÿ’œ If only one entry point is generated, this option does nothing ๐Ÿ’€
  • -misel, -mno-isel โ€” turns the generation of ISEL instructions on or off ๐ŸŒธ
  • -mvsx, -mno-vsx โ€” -mvsx generates code that uses vector/scalar (VSX) instructions and turns on built-in functions for more direct VSX access ๐Ÿ”ฅ -mno-vsx generates code that doesn't use them ๐ŸŽ€
  • -mcrypto, -mno-crypto โ€” enables or disables the built-in functions that give direct access to the cryptographic instructions added in version 2.07 of the PowerPC ISA ๐Ÿ”๐ŸŒˆ
  • -mhtm, -mno-htm โ€” enables or disables the built-in functions for the Hardware Transactional Memory (HTM) instructions, which were also added in version 2.07 of the PowerPC ISA ๐Ÿฆ‹
  • -mpower8-fusion, -mno-power8-fusion โ€” -mpower8-fusion keeps some integer operations next to each other so they can get fused together on POWER8 and later processors ๐Ÿงš -mno-power8-fusion doesn't keep them adjacent like that ๐Ÿ™ˆ
  • -mquad-memory, -mno-quad-memory โ€” -mquad-memory generates code that uses the non-atomic quad word memory instructions. -mno-quad-memory skips them. The -mquad-memory option needs 64-bit mode ๐Ÿ‘‘
  • -mquad-memory-atomic, -mno-quad-memory-atomic โ€” -mquad-memory-atomic generates code that uses the atomic quad word memory instructions. -mno-quad-memory-atomic skips them. This one also needs 64-bit mode ๐Ÿ’…
  • -mfloat128, -mno-float128 โ€” these turn the __float128 keyword on or off for IEEE 128-bit floating point. You get either software emulation for IEEE 128-bit floating point or hardware instructions ๐ŸŒˆ
To use the IEEE 128-bit floating point support, the VSX instruction set (-mvsx) has to be enabled first. Also, IEEE 128-bit floating point is only supported on Linux ๐Ÿ˜ญ

-mfloat128 is on by default on PowerPC Linux systems that use the VSX instruction set. On other systems it's off ๐Ÿซถ

If you use the ISA 3.0 instruction set (-mcpu=power9) on a 64-bit system, the IEEE 128-bit floating point support also turns on the generation of ISA 3.0 IEEE 128-bit floating point instructions โญ Otherwise, if you don't ask for ISA 3.0 instructions, or you're targeting a 32-bit big endian system, IEEE 128-bit floating point gets done with software emulation ๐Ÿงš

  • -mfloat128-hardware, -mno-float128-hardware โ€” enables or disables using ISA 3.0 hardware instructions to support the __float128 data type ๐Ÿ”ฅ

-mfloat128-hardware is on by default on PowerPC Linux systems that use the ISA 3.0 instruction set. On other systems it's off ๐Ÿ’œ

  • -m32, -m64 โ€” generates code for the 32-bit or 64-bit environments of Darwin and SVR4 targets (including GNU/Linux) ๐ŸŒธ The 32-bit environment sets int, long, and pointer to 32 bits, and the code runs on any PowerPC variant ๐ŸŽ€ The 64-bit environment sets int to 32 bits and long and pointer to 64 bits. It generates code for PowerPC64, just like -mpowerpc64 โœจ
  • -mfull-toc, -mno-fp-in-toc, -mno-sum-in-toc, -mminimal-toc โ€” these all change how the TOC (Table Of Contents) gets generated, and every executable file gets one ๐Ÿงš -mfull-toc is the default. With it, GCC gives at least one TOC entry to each unique non-automatic variable reference in your program. GCC also puts floating-point constants in the TOC ๐Ÿ’œ The catch is that only 16,384 entries fit in the TOC ๐Ÿ˜ญ

If the linker yells that you've overflowed the available TOC space, you can shrink how much TOC space you use with -mno-fp-in-toc and -mno-sum-in-toc ๐Ÿซถ -mno-fp-in-toc stops GCC from putting floating-point constants in the TOC. -mno-sum-in-toc makes GCC generate code that calculates the sum of an address and a constant at run time, instead of stuffing that sum into the TOC ๐ŸŒˆ You can use one or both. Each one makes the code very slightly slower and larger, but that's the trade for saving TOC space ๐Ÿฆ‹

If you still run out of space in the TOC even when you specify both of these options, bestie, try -mminimal-toc instead ๐Ÿ˜ญ. It makes GCC create only one TOC entry for every file. The catch is that the code comes out slower and larger, but it uses extremely little TOC space ๐Ÿ’€. Honestly, save it for files with less frequently-executed code ๐Ÿซถ.

  • -maix64, -maix32 โ€” okay so -maix64 turns on the 64-bit AIX ABI and calling convention โœจ. You get 64-bit pointers, a 64-bit "long" type, and all the infrastructure needed to support them. It implies -mpowerpc64. -maix32 goes the other way, disabling the 64-bit ABI and implying -mno-powerpc64. GCC defaults to -maix32 ๐ŸŽ€.
  • -mxl-compat, -mno-xl-compat โ€” this one is giving IBM XL compiler vibes ๐Ÿ‘‘. It makes the code match XL semantics more closely when you're using the AIX-compatible ABI. Floating-point args to prototyped functions beyond the register save area (RSA) get passed on the stack too, on top of the argument FPRs. It also stops assuming the most significant double in a 128-bit long double value is properly rounded when comparing and converting to double, and it uses XL symbol names for long double support routines.

The AIX calling convention got extended without being documented at first, to handle an obscure K&R C case: calling a function that takes the address of its arguments with fewer arguments than it declared ๐Ÿ™ˆ. IBM XL compilers read floating-point arguments that don't fit in the RSA from the stack when a subroutine is compiled without optimization. Always storing floating-point arguments on the stack is inefficient and rarely needed, so this option isn't on by default. You only need it when calling subroutines compiled by IBM XL compilers without optimization.

  • -mpe โ€” this one's for the IBM RS/6000 SP Parallel Environment (PE) ๐Ÿงš. It links an app that uses message passing with special startup code so it can actually run. The system needs PE installed in the standard location (/usr/lpp/ppe.poe/). If it isn't there, you can override the specs file with -specs= and point it to the right directory.
The Parallel Environment doesn't support threads, so -mpe and -pthread are incompatible. Don't pair them up, bestie ๐Ÿ’€.
  • -malign-natural, -malign-power โ€” on AIX, 32-bit Darwin, and 64-bit PowerPC GNU/Linux, -malign-natural overrides the ABI-defined alignment of bigger types, like floating-point doubles, and puts them on their natural size-based boundary ๐Ÿ“. -malign-power tells GCC to follow the ABI-specified alignment rules instead. GCC defaults to the standard alignment defined in the ABI.

On 64-bit Darwin, natural alignment is already the default, and -malign-power isn't supported there ๐Ÿ™…โ€โ™€๏ธ.

  • -msoft-float, -mhard-float โ€” -msoft-float generates code that doesn't use the floating-point register set, so you don't touch those registers at all. Software floating-point emulation is provided, but you have to pass the option to GCC when linking too. -mhard-float is the opposite: it lets the code use the floating-point register set ๐Ÿ”ฅ.
  • -mmultiple, -mno-multiple โ€” these generate (or don't generate) the load multiple word and store multiple word instructions ๐Ÿ’œ. They're generated by default on POWER systems and aren't generated on PowerPC systems. Don't use -mmultiple on little-endian PowerPC systems, because those instructions don't work in little-endian mode. The exceptions are PPC740 and PPC750, which let these instructions work in little-endian mode.
  • -mupdate, -mno-update โ€” these control whether the load or store instructions that update the base register to the address of the calculated memory location get used. They're generated by default. If you use -mno-update, there's a small window between the time the stack pointer gets updated and the time the address of the previous frame gets stored. That means code that walks the stack frame across interrupts or signals might end up with corrupted data ๐Ÿ˜ญ.
  • -mavoid-indexed-addresses, -mno-avoid-indexed-addresses โ€” these make the code try to avoid (or not avoid) indexed load or store instructions ๐Ÿฆ‹. Those can cause a performance penalty on Power6 processors in certain situations, like stepping through large arrays that cross a 16M boundary. It's enabled by default when targeting Power6 and disabled otherwise.
  • -mfused-madd, -mno-fused-madd โ€” these generate (or don't generate) the floating-point multiply and accumulate instructions โœจ. They're generated by default if hardware floating point is used. The machine-dependent -mfused-madd is now mapped to the machine-independent -ffp-contract=fast, and -mno-fused-madd is mapped to -ffp-contract=off.
  • -mmulhw, -mno-mulhw โ€” these generate (or don't generate) the half-word multiply and multiply-accumulate instructions on the IBM 405, 440, 464 and 476 processors ๐ŸŒธ. They're generated by default when you target those processors.
  • -mdlmzb, -mno-dlmzb โ€” these generate (or don't generate) the string-search dlmzb instruction on the IBM 405, 440, 464 and 476 processors ๐Ÿ”. It's generated by default when targeting those processors.
  • -mno-bit-align, -mbit-align โ€” on System V.4 and embedded PowerPC systems, -mno-bit-align stops structures and unions that contain bit-fields from being forced to align to the base type of the bit-field. -mbit-align does force that alignment.

Here's the tea ๐Ÿ“: by default, a structure with nothing but 8 "unsigned" bit-fields of length 1 gets aligned to a 4-byte boundary and ends up 4 bytes in size. With -mno-bit-align, that same structure gets aligned to a 1-byte boundary and is only 1 byte in size.

  • -mno-strict-align, -mstrict-align โ€” on System V.4 and embedded PowerPC systems, these decide whether GCC assumes unaligned memory references get handled by the system. -mno-strict-align assumes they do, and -mstrict-align doesn't ๐Ÿ’….
  • -mrelocatable, -mno-relocatable โ€” these generate code that allows (or doesn't allow) a static executable to be relocated to a different address at run time ๐Ÿฆ‹. A simple embedded PowerPC system loader should relocate the entire contents of .got2 and the 4-byte locations listed in the .fixup section, which is a table of 32-bit addresses this option generates. For this to work, every object linked together must be compiled with -mrelocatable or -mrelocatable-lib. -mrelocatable code aligns the stack to an 8-byte boundary.
  • -mrelocatable-lib, -mno-relocatable-lib โ€” like -mrelocatable, -mrelocatable-lib generates a .fixup section so static executables can be relocated at run time. The difference is that -mrelocatable-lib doesn't use the smaller stack alignment of -mrelocatable. Objects compiled with -mrelocatable-lib can be linked with objects compiled with any combination of the -mrelocatable options ๐Ÿค.
  • -mno-toc, -mtoc โ€” on System V.4 and embedded PowerPC systems, these decide whether GCC assumes register 2 contains a pointer to a global area with the addresses the program uses ๐Ÿง .
  • -mlittle, -mlittle-endian โ€” on System V.4 and embedded PowerPC systems, this compiles code for the processor in little-endian mode ๐Ÿ’œ. -mlittle-endian is the same as -mlittle.
  • -mbig, -mbig-endian โ€” on System V.4 and embedded PowerPC systems, this compiles code for the processor in big-endian mode ๐ŸŒˆ. -mbig-endian is the same as -mbig.
  • -mdynamic-no-pic โ€” on Darwin / macOS systems, this compiles code so it isn't relocatable itself, but its external references are relocatable ๐ŸŽ. The result is good for applications, but not for shared libraries.
  • -msingle-pic-base โ€” this treats the register used for PIC addressing as read-only, instead of loading it in the prologue for each function โœจ. The runtime system is responsible for initializing that register with an appropriate value before execution begins.
  • -mprioritize-restricted-insns=priority โ€” this controls the priority given to dispatch-slot restricted instructions during the second scheduling pass ๐Ÿ“‹. The argument priority takes the value 0, 1, or 2, which assigns no, highest, or second-highest priority (respectively) to dispatch-slot restricted instructions.
  • -msched-costly-dep=dependence_type โ€” this controls which dependences the target considers costly during instruction scheduling ๐ŸŽ€. The argument dependence_type takes one of these values:
    • no โ€” No dependence is costly.
    • all โ€” All dependences are costly.
    • true_store_to_load โ€” A true dependence from store to load is costly.
    • store_to_load โ€” Any dependence from store to load is costly.
    • number โ€” Any dependence for which the latency is greater than or equal to number is costly.
  • -minsert-sched-nops=scheme โ€” this controls which NOP insertion scheme gets used during the second scheduling pass ๐Ÿซง. The argument scheme takes one of these values:
    • no โ€” Don't insert NOPs.
    • pad โ€” Pad with NOPs any dispatch group that has vacant issue slots, according to the scheduler's grouping.
    • regroup_exact โ€” Insert NOPs to force costly dependent insns into separate groups. Insert exactly as many NOPs as needed to force an insn to a new group, according to the estimated processor grouping.
    • number โ€” Insert NOPs to force costly dependent insns into separate groups. Insert number NOPs to force an insn to a new group.
  • -mcall-sysv โ€” on System V.4 and embedded PowerPC systems, this compiles code using calling conventions that follow the March 1995 draft of the System V Application Binary Interface, PowerPC processor supplement ๐Ÿ“œ. This is the default unless you configured GCC using powerpc-*-eabiaix.
  • -mcall-sysv-eabi, -mcall-eabi โ€” these specify both -mcall-sysv and -meabi ๐Ÿ’….
  • -mcall-sysv-noeabi โ€” this specifies both -mcall-sysv and -mno-eabi ๐ŸŒธ.
  • -mcall-aixdesc โ€” on System V.4 and embedded PowerPC systems, this compiles code for the AIX operating system ๐Ÿฆ….
  • -mcall-linux โ€” on System V.4 and embedded PowerPC systems, this compiles code for the Linux-based GNU system ๐Ÿง.
  • -mcall-freebsd โ€” on System V.4 and embedded PowerPC systems, this compiles code for the FreeBSD operating system ๐Ÿ˜ˆ.
  • -mcall-netbsd โ€” on System V.4 and embedded PowerPC systems, this compiles code for the NetBSD operating system ๐Ÿก.
  • -mcall-openbsd โ€” okay so this one is for System V.4 and embedded PowerPC systems, and it makes the compiler build code for the OpenBSD operating system ๐Ÿงš
  • -mtraceback=traceback_type โ€” pick which kind of traceback table you want ๐Ÿ” The valid values for traceback_type are full, part, and no.
  • -maix-struct-return โ€” every structure gets returned in memory, as the AIX ABI says ๐Ÿ“ฆ
  • -msvr4-struct-return โ€” structures smaller than 8 bytes come back in registers instead, which is what the SVR4 ABI wants ๐Ÿ’œ
  • -mabi=abi-type โ€” this lets you extend the current ABI with a particular extension, or remove one ๐ŸŽ€ The valid values are altivec, no-altivec, ibmlongdouble, ieeelongdouble, elfv1, and elfv2. And for AIX, you also get vec-extabi and vec-default โœจ
  • -mabi=ibmlongdouble โ€” switches the current ABI over to IBM extended-precision long double ๐Ÿซถ Heads up bestie, this is not likely to work if your system already defaults to IEEE extended-precision long double. If you change the long double type away from IEEE extended-precision, the compiler gives you a warning unless you use the -Wno-psabi option. You also need -mlong-double-128 turned on for this one.
  • -mabi=ieeelongdouble โ€” the opposite move: it changes the current ABI to IEEE extended-precision long double ๐ŸŒˆ Same deal here, this is not likely to work if your system defaults to IBM extended-precision long double. Switching away from IBM extended-precision gets you a warning unless you use -Wno-psabi, and -mlong-double-128 has to be enabled too.
  • -mabi=elfv1 โ€” changes the current ABI to the ELFv1 ABI ๐Ÿ“ This is the default for big-endian PowerPC 64-bit Linux. Overriding the default needs special system support, and honestly it is likely to fail in spectacular ways ๐Ÿ’€
Overriding the default ABI with -mabi=elfv1 requires special system support and is likely to fail in spectacular ways.
  • -mabi=elfv2 โ€” changes the current ABI to the ELFv2 ABI ๐ŸŒธ This is the default for little-endian PowerPC 64-bit Linux. Overriding the default also requires special system support and is likely to fail in spectacular ways.
Overriding the default ABI with -mabi=elfv2 requires special system support and is likely to fail in spectacular ways.
  • -mgnu-attribute, -mno-gnu-attribute โ€” this makes the compiler emit .gnu_attribute assembly directives ๐Ÿฆ‹ Those set tag/value pairs in a .gnu.attributes section that describe the ABI variations used in function parameters or return values.
  • -mprototype, -mno-prototype โ€” on System V.4 and embedded PowerPC systems, -mprototype tells the compiler to assume that all calls to variable argument functions are properly prototyped โœจ Without it, the compiler has to insert an instruction before every non-prototyped call to set or clear bit 6 of the condition code register ("CR"), so the function knows whether floating-point values are passed in the floating-point registers, in case the function takes variable arguments. With -mprototype, only calls to prototyped variable argument functions set or clear that bit.
  • -msim โ€” on embedded PowerPC systems, this assumes the startup module is called sim-crt0.o and the standard C libraries are libsim.a and libc.a ๐Ÿงธ This is the default for powerpc-*-eabisim configurations.
  • -mmvme โ€” on embedded PowerPC systems, this assumes the startup module is called crt0.o and the standard C libraries are libmvme.a and libc.a ๐Ÿญ
  • -mads โ€” on embedded PowerPC systems, this assumes the startup module is called crt0.o and the standard C libraries are libads.a and libc.a ๐ŸŒท
  • -myellowknife โ€” on embedded PowerPC systems, this assumes the startup module is called crt0.o and the standard C libraries are libyk.a and libc.a ๐Ÿ’›
  • -mvxworks โ€” on System V.4 and embedded PowerPC systems, this tells the compiler you are compiling for a VxWorks system ๐Ÿ›ธ
  • -memb โ€” on embedded PowerPC systems, this sets the "PPC_EMB" bit in the ELF flags header, which indicates that eabi extended relocations are used ๐Ÿช„
  • -meabi, -mno-eabi โ€” on System V.4 and embedded PowerPC systems, -meabi means you adhere to the Embedded Applications Binary Interface (EABI), which is a set of modifications to the System V.4 specifications ๐ŸŽ€ -mno-eabi means you do not. Choosing -meabi aligns the stack to an 8-byte boundary, calls a function "__eabi" from "main" to set up the EABI environment, and lets the -msdata option use both "r2" and "r13" to point to two separate small data areas. Choosing -mno-eabi aligns the stack to a 16-byte boundary, does not call any EABI initialization function from "main", and makes the -msdata option use only "r13" to point to a single small data area. The -meabi option is on by default if you configured GCC using one of the powerpc*-*-eabi* options ๐Ÿ’…
  • -msdata=eabi โ€” on System V.4 and embedded PowerPC systems, small initialized "const" global and static data goes in the ".sdata2" section, which is pointed to by register "r2" ๐ŸŒˆ Small initialized non-"const" global and static data goes in the ".sdata" section, pointed to by register "r13". Small uninitialized global and static data goes in the ".sbss" section, which sits right next to ".sdata". Heads up, -msdata=eabi is incompatible with the -mrelocatable option, and it also turns on the -memb option.
  • -msdata=sysv โ€” on System V.4 and embedded PowerPC systems, small global and static data goes in the ".sdata" section, pointed to by register "r13" ๐Ÿ“ Small uninitialized global and static data goes in the ".sbss" section, next to ".sdata". This one is incompatible with -mrelocatable too.
  • -msdata=default, -msdata โ€” on System V.4 and embedded PowerPC systems, if -meabi is used, the code gets compiled the same as -msdata=eabi. Otherwise it gets compiled the same as -msdata=sysv ๐Ÿซง
  • -msdata=data โ€” on System V.4 and embedded PowerPC systems, small global data goes in the ".sdata" section, and small uninitialized global data goes in the ".sbss" section ๐ŸŒธ The twist is that register "r13" is not used to address small data. This is the default behavior unless other -msdata options are used.
  • -msdata=none, -mno-sdata โ€” on embedded PowerPC systems, all initialized global and static data goes in the ".data" section, and all uninitialized data goes in the ".bss" section ๐Ÿง
  • -mreadonly-in-sdata โ€” read-only objects get put in the ".sdata" section as well ๐Ÿ’– This is the default.
  • -mblock-move-inline-limit=num โ€” inlines all block moves (like calls to "memcpy" or structure copies) that are less than or equal to num bytes โœจ The minimum value for num is 32 bytes on 32-bit targets and 64 bytes on 64-bit targets. The default value is target-specific.
  • -mblock-compare-inline-limit=num โ€” generates non-looping inline code for all block compares (like calls to "memcmp" or structure compares) that are less than or equal to num bytes ๐Ÿ”ฅ If num is 0, all inline expansion of block compare, both non-loop and loop, is disabled. The default value is target-specific.
  • -mblock-compare-inline-loop-limit=num โ€” generates an inline expansion using loop code for all block compares that are less than or equal to num bytes, but greater than the limit for non-loop inline block compare expansion ๐Ÿ” If the block length is not constant, at most num bytes get compared before "memcmp" is called to compare the rest of the block. The default value is target-specific.
  • -mstring-compare-inline-limit=num โ€” compares at most num string bytes with inline code ๐Ÿงต If the difference or end of string is not found by the end of the inline compare, a call to "strcmp" or "strncmp" takes care of the rest. The default is 64 bytes.
  • -G num โ€” on embedded PowerPC systems, global and static items less than or equal to num bytes go into the small data or BSS sections instead of the normal data or BSS section ๐Ÿฃ By default, num is 8. The -G num switch also gets passed to the linker, and all modules should be compiled with the same -G num value.
  • -mregnames, -mno-regnames โ€” on System V.4 and embedded PowerPC systems, -mregnames makes the compiler emit register names in the assembly output using symbolic forms, and -mno-regnames means it does not ๐ŸŒŸ
  • -mlongcall, -mno-longcall โ€” by default, the compiler assumes all calls are far away, so it uses a longer and more expensive calling sequence ๐Ÿข That's required for calls farther than 32 megabytes (33,554,432 bytes) from the current location. A short call only gets generated if the compiler knows the call cannot be that far away. You can override this setting with the "shortcall" function attribute, or with #pragma longcall(0).

Some linkers can detect out-of-range calls and generate glue code on the fly. On those systems, long calls are unnecessary and generate slower code ๐Ÿ™„ As of this writing, the AIX linker can do this, and so can the GNU linker for PowerPC/64. It is planned to add this feature to the GNU linker for 32-bit PowerPC systems as well.

On PowerPC64 ELFv2 and 32-bit PowerPC systems with newer GNU linkers, GCC can generate long calls using an inline PLT call sequence (see -mpltseq). PowerPC with -mbss-plt and PowerPC64 ELFv1 (big-endian) do not support inline PLT calls.

On Darwin/PPC systems, #pragma longcall generates "jbsr callee, L42", plus a branch island (glue code) ๐Ÿ๏ธ The two target addresses represent the callee and the branch island. The Darwin/PPC linker prefers the first address, and it generates a "bl callee" if the PPC "bl" instruction reaches the callee directly. Otherwise, the linker generates "bl L42" to call the branch island. The branch island gets appended to the body of the calling function, and it computes the full 32-bit address of the callee and jumps to it.

On Mach-O (Darwin) systems, this option tells the compiler to emit the glue for every direct call, and the Darwin linker decides whether to use it or discard it ๐Ÿฆฉ

In the future, GCC may ignore all longcall specifications when the linker is known to generate glue ๐Ÿ‘€

  • -mpltseq, -mno-pltseq โ€” -mpltseq implements -fno-plt and long calls using an inline PLT call sequence, while -mno-pltseq does not ๐ŸŒ™ That sequence supports lazy linking and long calls to functions in dlopen'd shared libraries. Inline PLT calls are only supported on PowerPC64 ELFv2 and 32-bit PowerPC systems with newer GNU linkers. They're enabled by default if the support is detected when configuring GCC, and, in the case of 32-bit PowerPC, if GCC is configured with --enable-secureplt. Also be careful bestie: -mpltseq code and -mbss-plt 32-bit PowerPC relocatable objects may not be linked together ๐Ÿšซ

Okay bestie, we're still deep in the powerpc flags, so let's keep going ๐Ÿฆ‹โœจ

  • -mtls-markers, -mno-tls-markers โ€” okay so -mtls-markers tags calls to __tls_get_addr with a relocation that spells out the function argument ๐ŸŽ€ -mno-tls-markers skips the tagging. The tag lets the linker reliably match each call with its argument setup instructions for TLS optimization, and that means GCC can schedule the whole sequence better ๐Ÿ’…
  • -mrecip, -mno-recip โ€” this one turns on the reciprocal estimate and reciprocal square root estimate instructions, plus extra Newton-Raphson steps to get back the precision. Instead of doing a divide (or a square root and then a divide) for floating-point arguments, the chip just does the quick estimate-and-refine thing ๐Ÿงš You should pair -mrecip with -ffast-math, or at least with -funsafe-math-optimizations, -ffinite-math-only, -freciprocal-math and -fno-trapping-math ๐Ÿซถ The throughput is usually higher than the non-reciprocal instruction, but the precision can drop by up to 2 ulp (so the inverse of 1.0 can come out as 0.99999994), especially for reciprocal square roots ๐Ÿ˜ญ
  • -mrecip=opt โ€” this one controls exactly which reciprocal estimate instructions are allowed. opt is a comma-separated list, and any item can get a ! in front to flip it ๐ŸŒˆ
    • all โ€” enables every estimate instruction ๐Ÿ”ฅ
    • default โ€” enables the default instructions, same as -mrecip โœจ
    • none โ€” disables every estimate instruction, same as -mno-recip ๐Ÿ™ˆ
    • div โ€” enables the reciprocal approximation instructions for both single and double precision ๐Ÿ’œ
    • divf โ€” enables just the single-precision reciprocal approximation instructions ๐ŸŒธ
    • divd โ€” enables just the double-precision reciprocal approximation instructions ๐Ÿ“
    • rsqrt โ€” enables the reciprocal square root approximation instructions for both single and double precision ๐Ÿ‘‘
    • rsqrtf โ€” enables just the single-precision reciprocal square root approximation instructions โญ
    • rsqrtd โ€” enables just the double-precision reciprocal square root approximation instructions ๐Ÿฆ‹

So, for example, -mrecip=all,!rsqrtd turns on every estimate instruction except FRSQRTE, XSRSQRTEDP, and XVRSQRTEDP, which are the ones handling double-precision reciprocal square roots ๐Ÿ’…

  • -mrecip-precision, -mno-recip-precision โ€” -mrecip-precision assumes the reciprocal estimate instructions are more precise than the PowerPC ABI requires, and -mno-recip-precision refuses to assume that ๐Ÿงš Picking -mcpu=power6, -mcpu=power7 or -mcpu=power8 turns -mrecip-precision on automatically โœจ The double-precision square root estimate instructions aren't generated by default on low-precision machines, because they don't converge after three steps ๐Ÿ™ˆ
  • -mveclibabi=type โ€” picks the ABI type used when vectorizing intrinsics with an external library. The only type supported right now is mass, which tells GCC to use IBM's Mathematical Acceleration Subsystem (MASS) libraries ๐ŸŽ€ When generating code for power7, GCC currently emits calls to acosd2, acosf4, acoshd2, acoshf4, asind2, asinf4, asinhd2, asinhf4, atan2d2, atan2f4, atand2, atanf4, atanhd2, atanhf4, cbrtd2, cbrtf4, cosd2, cosf4, coshd2, coshf4, erfcd2, erfcf4, erfd2, erff4, exp2d2, exp2f4, expd2, expf4, expm1d2, expm1f4, hypotd2, hypotf4, lgammad2, lgammaf4, log10d2, log10f4, log1pd2, log1pf4, log2d2, log2f4, logd2, logf4, powd2, powf4, sind2, sinf4, sinhd2, sinhf4, sqrtd2, sqrtf4, tand2, tanf4, tanhd2, and tanhf4 ๐Ÿ˜ญ Both -ftree-vectorize and -funsafe-math-optimizations must also be enabled, and the MASS libraries have to be specified at link time ๐Ÿ”ฅ
  • -mfriz, -mno-friz โ€” -mfriz generates the friz instruction when -funsafe-math-optimizations is used to optimize rounding floating-point values to 64-bit integer and back to floating point. -mno-friz doesn't generate it ๐Ÿ’… Heads up though: friz doesn't give the same value if the floating-point number is too large to fit in an integer ๐Ÿ’€
  • -mpointers-to-nested-functions, -mno-pointers-to-nested-functions โ€” -mpointers-to-nested-functions generates code that loads the static chain register (r11) when calling through a pointer on AIX and 64-bit Linux systems, where a function pointer points to a 3-word descriptor holding the function address, the TOC value to load into r2, and the static chain value to load into r11 ๐Ÿงš This one is on by default. If you use -mno-pointers-to-nested-functions, you can't call through pointers to nested functions, or through pointers to functions compiled in other languages that use the static chain ๐Ÿ™ˆ
  • -msave-toc-indirect, -mno-save-toc-indirect โ€” -msave-toc-indirect saves the TOC value in the reserved stack location in the function prologue when the function calls through a pointer on AIX and 64-bit Linux systems. If it isn't saved in the prologue, it gets saved right before the call through the pointer โœจ -mno-save-toc-indirect is the default ๐ŸŒธ
  • -mcompat-align-parm, -mno-compat-align-parm โ€” -mcompat-align-parm generates code that passes structure parameters with a maximum alignment of 64 bits, so it matches older versions of GCC ๐ŸŽ€

Older versions of GCC (prior to 4.9.0) incorrectly didn't align a structure parameter on a 128-bit boundary when that structure had a member needing 128-bit alignment. Newer versions fixed that. This option lets you make code that works with functions compiled by the older GCC ๐Ÿซถ

-mno-compat-align-parm is the default ๐Ÿ’œ

  • -mstack-protector-guard=guard, -mstack-protector-guard-reg=reg, -mstack-protector-guard-offset=offset โ€” these generate stack protection code using a canary at guard. The supported locations are global for a global canary, or tls for a per-thread canary in the TLS block (the default with GNU libc version 2.4 or later) ๐Ÿ”ฅ With tls, -mstack-protector-guard-reg=reg and -mstack-protector-guard-offset=offset also decide which register is the base register for reading the canary, and what offset from that base it's read at ๐Ÿ‘‘ Both default to whatever the relevant ABI specifies โœจ
  • -mpcrel, -mno-pcrel โ€” -mpcrel generates pc-relative addressing, and -mno-pcrel doesn't ๐Ÿฆ‹ Using -mpcrel requires that the medium code model (-mcmodel=medium) and prefixed addressing (-mprefixed) are both enabled ๐ŸŒˆ
  • -mprefixed, -mno-prefixed โ€” -mprefixed generates addressing modes that use prefixed load and store instructions, and -mno-prefixed doesn't ๐Ÿงš -mprefixed needs -mcpu=power10 (or later) enabled ๐Ÿ’…
  • -mmma, -mno-mma โ€” -mmma generates the MMA instructions, and -mno-mma doesn't ๐Ÿ’œ -mmma needs -mcpu=power10 (or later) enabled ๐Ÿ“
  • -mrop-protect, -mno-rop-protect โ€” -mrop-protect generates ROP protection instructions when the target processor supports them, and -mno-rop-protect doesn't ๐Ÿ›ก๏ธ Right now this option disables the shrink-wrap optimization (-fshrink-wrap) ๐Ÿ˜ญ
  • -mprivileged, -mno-privileged โ€” -mprivileged generates code that runs in privileged state, and -mno-privileged doesn't ๐Ÿ‘‘
  • -mblock-ops-unaligned-vsx, -mno-block-ops-unaligned-vsx โ€” -mblock-ops-unaligned-vsx generates unaligned vsx loads and stores for inline expansion of memcpy and memmove, and -mno-block-ops-unaligned-vsx doesn't ๐ŸŒธ
  • --param rs6000-vect-unroll-limit= โ€” the vectorizer checks with target information to figure out whether unrolling the main vectorized loop would be worth it, and by how much. This parameter sets the upper bound on how much the vectorizer will unroll that main loop. The default value is four โœจ

๐ŸŒˆ RX Options ๐ŸŒˆ

These command-line options are defined for RX targets ๐Ÿงš

  • -m64bit-doubles, -m32bit-doubles โ€” makes the double data type 64 bits (-m64bit-doubles) or 32 bits (-m32bit-doubles) in size. The default is -m32bit-doubles ๐Ÿ’– Note that RX floating-point hardware only works on 32-bit values, which is exactly why the default is -m32bit-doubles ๐Ÿ™ˆ
  • -fpu, -nofpu โ€” -fpu enables the use of RX floating-point hardware, and -nofpu disables it ๐ŸŽ€ The default is enabled for the RX600 series and disabled for the RX200 series ๐Ÿ’…

Floating-point instructions are only generated for 32-bit floating-point values, though. So the FPU hardware isn't used for doubles if -m64bit-doubles is used ๐Ÿ˜ญ

Note: if -fpu is enabled, then -funsafe-math-optimizations is also enabled automatically. That's because the RX FPU instructions are themselves unsafe ๐Ÿ’€
  • -mcpu=name โ€” selects the type of RX CPU to target ๐Ÿ”ฅ Right now three types are supported: the generic RX600 and RX200 series hardware, and the specific RX610 CPU. The default is RX600 ๐Ÿ‘‘

The only difference between RX600 and RX610 is that the RX610 doesn't support the MVTIPL instruction ๐Ÿฆ‹

The RX200 series doesn't have a hardware floating-point unit, so -nofpu is enabled by default when this type is selected ๐ŸŒธ

  • -mbig-endian-data, -mlittle-endian-data โ€” stores data (but not code) in the big-endian format with -mbig-endian-data. The default is -mlittle-endian-data, which stores data in the little-endian format ๐Ÿงš
  • -msmall-data-limit=N โ€” specifies the maximum size in bytes of global and static variables that can be placed into the small data area. Using the small data area can lead to smaller and faster code, but the area is limited, and it's up to the programmer to make sure it doesn't overflow ๐Ÿ˜ญ Also, when the small data area is used, one of the RX's registers (usually r13) is reserved to point at this area, so the compiler can't use it anymore. That can make code slower and/or larger if variables get pushed onto the stack instead of being held in that register ๐Ÿ™ˆ
Note: common variables (variables that haven't been initialized) and constants aren't placed into the small data area, because they get assigned to other sections in the output executable ๐Ÿ’œ

So the default value is zero, which means this feature is switched off ๐Ÿ™ˆ Heads up: it's not turned on by default at higher optimization levels (-O2 and friends) because reserving a register can be kinda detrimental. It's on you to experiment and figure out whether it actually helps your program ๐Ÿงช For how the register holding the small data area pointer gets picked, check out the -mpid option โœจ

  • -msim, -mno-sim โ€” Uses the simulator runtime ๐Ÿ–ฅ๏ธ The default is the libgloss board-specific runtime.
  • -mas100-syntax, -mno-as100-syntax โ€” When GCC spits out assembler, this makes it use a syntax that plays nicely with Renesas's AS100 assembler ๐Ÿ’… The GAS assembler can handle this syntax too, but it comes with some restrictions, so it isn't generated by default.
  • -mmax-constant-size=N โ€” Sets the biggest size, in bytes, that a constant can be and still be used as an operand in an RX instruction ๐Ÿ“ The RX instruction set can handle constants up to 4 bytes, but bigger values make the instruction longer, so sometimes it's smart to limit this. Constants that are too big get tucked into a constant pool and reached through register indirection ๐Ÿงš

The value N can be anywhere from 0 to 4. A value of 0 (the default) or 4 means constants of any size are allowed ๐Ÿ’–

  • -mrelax โ€” Turns on linker relaxation ๐Ÿฆ‹ That's when the linker tries to shrink your program by finding shorter versions of various instructions. It's disabled by default.
  • -mint-register=N โ€” Reserves a number of registers for fast interrupt handler functions ๐Ÿ‘‘ N can be between 0 and 4. A value of 1 reserves register "r13" for the exclusive use of fast interrupt handlers. A value of 2 reserves "r13" and "r12". A value of 3 reserves "r13", "r12" and "r11", and a value of 4 reserves "r13" through "r10". The default, 0, doesn't reserve anything at all ๐ŸŒธ
  • -msave-acc-in-interrupts โ€” Tells interrupt handler functions to preserve the accumulator register ๐Ÿซถ You only need this if your normal code might touch the accumulator, like if it does 64-bit multiplications. By default the accumulator gets ignored, which keeps the interrupt handlers faster โšก
  • -mpid, -mno-pid โ€” Turns on the generation of position independent data ๐ŸŒˆ With it enabled, any access to constant data goes through an offset from a base address held in a register. This lets the location of constant data get figured out at run time without the executable needing to be relocated, which is a big win for embedded apps with tight memory constraints ๐Ÿ’œ Data that can be modified isn't affected by this option.

Heads up though, this reserves a register (usually "r13") for the constant data base address, which can make code slower and/or bigger, especially in complicated functions ๐Ÿ˜ญ

Which register actually gets chosen depends on whether -msmall-data-limit and/or -mint-register are enabled. Starting at "r13" and working downwards, registers are allocated first to satisfy -mint-register, then -mpid, and finally -msmall-data-limit. So the small data area register can end up being "r8" if both -mint-register=4 and -mpid are on the command line ๐ŸŽ€

By default this feature is not enabled. You can restore that default with the -mno-pid option โœจ

  • -mno-warn-multiple-fast-interrupts, -mwarn-multiple-fast-interrupts โ€” Stops GCC from yelling with a warning if it finds more than one fast interrupt handler while compiling a file ๐Ÿ™… By default it warns about each extra one, because the RX only supports one such interrupt.
  • -mallow-string-insns, -mno-allow-string-insns โ€” Turns the string manipulation instructions on or off ๐Ÿงต That's "SMOVF", "SCMPU", "SMOVB", "SMOVU", "SUNTIL" "SWHILE", and also the "RMPA" instruction. These instructions may prefetch data, which is not safe if you're accessing an I/O register ๐Ÿ’€ (Check out section 12.2.7 of the RX62N Group User's Manual for more info ๐Ÿ“–)

These are allowed by default, but GCC can't reliably detect every case where a string instruction might touch an I/O register, so it can't turn them off automatically. It's up to you to use -mno-allow-string-insns if your program accesses I/O space ๐Ÿซฃ

When the instructions are enabled, GCC defines the C preprocessor symbol __RX_ALLOW_STRING_INSNS__. Otherwise it defines __RX_DISALLOW_STRING_INSNS__ ๐Ÿ“

  • -mjsr, -mno-jsr โ€” Uses only (or not only) "JSR" instructions to access functions ๐Ÿš€ This can help when your code size goes past the range of "BSR" instructions. Fun fact: -mno-jsr doesn't mean "don't use JSR" ๐Ÿ™ˆ It just means any type of branch may be used.
Note: the generic GCC command-line option -ffixed-reg has special meaning for the RX port when it's paired with the "interrupt" function attribute. That attribute marks a function meant to process fast interrupts. GCC makes sure it only uses "r10", "r11", "r12" and/or "r13", and only if the normal use of those registers has been restricted through -ffixed-reg or -mint-register ๐Ÿซถ

โœจ S/390 and zSeries Options โœจ

These are the -m options defined for the S/390 and zSeries architecture ๐Ÿงš

  • -mhard-float, -msoft-float โ€” Lets you use (or not use) the hardware floating-point instructions and registers for floating-point operations ๐Ÿ’– With -msoft-float, functions in libgcc.a do the floating-point work. With -mhard-float, GCC generates IEEE floating-point instructions. This is the default ๐ŸŒˆ
  • -mhard-dfp, -mno-hard-dfp โ€” Lets you use (or not use) the hardware decimal-floating-point instructions for decimal-floating-point operations ๐Ÿ”ข With -mno-hard-dfp, functions in libgcc.a handle the decimal-floating-point stuff. With -mhard-dfp, GCC generates the decimal-floating-point hardware instructions. This is the default for -march=z9-ec or higher ๐Ÿ‘‘
  • -mlong-double-64, -mlong-double-128 โ€” These switches control the size of the "long double" type ๐Ÿ“ A size of 64 bits makes "long double" equivalent to "double". This is the default ๐Ÿ’…
  • -mbackchain, -mno-backchain โ€” Stores (or doesn't store) the address of the caller's frame as a backchain pointer in the callee's stack frame ๐Ÿ” A backchain may be needed for debugging with tools that don't understand DWARF call frame information. When -mno-packed-stack is in effect, the backchain pointer is stored at the bottom of the stack frame. When -mpacked-stack is in effect, the backchain goes into the topmost word of the 96/160 byte register save area ๐Ÿงต

Generally, code compiled with -mbackchain is call-compatible with code compiled with -mno-backchain. But using the backchain for debugging usually means the whole binary needs to be built with -mbackchain ๐Ÿ™ˆ Also, the combo of -mbackchain, -mpacked-stack and -mhard-float is not supported ๐Ÿ’€ If you want to build a linux kernel, use -msoft-float instead.

The default is to not maintain the backchain.

  • -mpacked-stack, -mno-packed-stack โ€” Uses (or doesn't use) the packed stack layout ๐Ÿ“ฆ With -mno-packed-stack, the compiler uses all the fields of the 96/160 byte register save area only for their default purpose, so unused fields still take up stack space. With -mpacked-stack, register save slots are densely packed at the top of the register save area, and unused space gets reused for other purposes, which is much more efficient with the stack space ๐ŸŒธ However, when -mbackchain is also in effect, the topmost word of the save area is always used to store the backchain, and the return address register is always saved two words below the backchain.

As long as the stack frame backchain isn't used, code generated with -mpacked-stack is call-compatible with code generated with -mno-packed-stack. Heads up: some non-FSF releases of GCC 2.95 for S/390 or zSeries generated code that uses the stack frame backchain at run time, not just for debugging. That code is not call-compatible with code compiled with -mpacked-stack ๐Ÿ˜ญ Also, the combo of -mbackchain, -mpacked-stack and -mhard-float is not supported. To build a linux kernel, use -msoft-float.

The default is to not use the packed stack layout.

  • -msmall-exec, -mno-small-exec โ€” Generates (or doesn't generate) code that uses the "bras" instruction for subroutine calls ๐Ÿ“ž This only works reliably if the total executable size doesn't go over 64k. The default is to use the "basr" instruction instead, which doesn't have that limitation ๐ŸŽ€
  • -m64, -m31 โ€” With -m31, GCC generates code compliant with the GNU/Linux for S/390 ABI. With -m64, it generates code compliant with the GNU/Linux for zSeries ABI ๐Ÿฆ‹ This lets GCC generate 64-bit instructions. For the s390 targets the default is -m31, while the s390x targets default to -m64 ๐Ÿ’œ
  • -mzarch, -mesa โ€” With -mzarch, GCC generates code using the instructions available on z/Architecture โœจ With -mesa, it uses the instructions available on ESA/390. Note that -mesa is not possible with -m64 ๐Ÿ™… When generating code compliant with the GNU/Linux for S/390 ABI, the default is -mesa. For the GNU/Linux for zSeries ABI, the default is -mzarch ๐ŸŒˆ
  • -mhtm, -mno-htm โ€” The -mhtm option turns on a set of builtins that use instructions from the transactional execution facility, which was introduced with the IBM zEnterprise EC12 machine generation. These are the S/390 System z Built-in Functions ๐Ÿง  -mhtm is enabled by default when using -march=zEC12 ๐Ÿ”ฅ
  • -mvx, -mno-vx โ€” okay so -mvx tells GCC to generate code using the vector extension facility instructions that came with the IBM z13 machine generation ๐Ÿฆ‹. It also changes the ABI for some vector type values when it comes to alignment and calling conventions. If vector types are used somewhere the ABI cares about, GCC adds a GAS .gnu_attribute command so the finished binary is marked with the ABI it used. And -mvx is turned on by default when you use -march=z13 โœจ
  • -mzvector, -mno-zvector โ€” bestie, -mzvector unlocks vector language extensions and builtins, using those same z13 vector instructions ๐Ÿ’… It lets you use vector as a keyword to define vector type variables and arguments. But vector only works when GNU extensions are on, so it won't expand if you ask for strict standard compliance, like with -std=c99. On top of the GCC low-level builtins, -mzvector also adds a set of builtins made for compatibility with AltiVec-style implementations like Power and Cell ๐ŸŒˆ To use them, you need to include the header file vecintrin.h. -mzvector is disabled by default, so you have to ask for it ๐Ÿ™ˆ
  • -mmvcle, -mno-mvcle โ€” -mmvcle makes GCC generate code that uses the "mvcle" instruction for block moves, and -mno-mvcle swaps that out for a "mvc" loop instead ๐Ÿ“ The default is the "mvcle" version, unless you're optimizing for size.
  • -mdebug, -mno-debug โ€” -mdebug makes the compiler print extra debug information while it's compiling, and -mno-debug keeps it quiet. By default, no debug info gets printed ๐Ÿงš
  • -march=cpu-type โ€” this tells GCC to generate code that runs on cpu-type, which is just the name of a system representing a certain processor type ๐Ÿ‘‘ The possible values are z900/arch5, z990/arch6, z9-109, z9-ec/arch7, z10/arch8, z196/arch9, zEC12, z13/arch11, z14/arch12, z15/arch13, z16/arch14, z17/arch15, and native.

The default is -march=z900.

Picking native as your cpu type lets GCC choose the best architecture option for the host processor ๐Ÿ’– Heads up, though: -march=native does nothing if GCC doesn't recognize the processor.

  • -mtune=cpu-type โ€” this tunes everything applicable about the generated code to cpu-type, except for the ABI and the set of available instructions โœจ The list of cpu-type values is the same as for -march. The default is whatever value you used for -march.
  • -mtpf-trace, -mno-tpf-trace โ€” -mtpf-trace makes the code add TPF OS specific branches to trace routines in the operating system, and -mno-tpf-trace means it doesn't add them ๐Ÿฆ‹ This one is off by default, even when you're compiling for the TPF OS.
  • -mtpf-trace-skip, -mno-tpf-trace-skip โ€” with -mtpf-trace-skip, the generated code changes the default branch targets enabled by -mtpf-trace so they point to specialized trace routines. That gives you the power to selectively skip function trace entries for the TPF OS ๐ŸŽ€ With -mno-tpf-trace-skip, those targets stay the same. This is off by default, even when you're compiling for the TPF OS and specifying -mtpf-trace.
  • -mfused-madd, -mno-fused-madd โ€” -mfused-madd lets the code use the floating-point multiply and accumulate instructions, and -mno-fused-madd tells it not to ๐Ÿ’œ These instructions get generated by default when hardware floating point is used.
  • -mwarn-framesize=framesize โ€” this makes GCC emit a warning if the current function goes over the given frame size ๐Ÿ”ฅ It's a compile-time check, so it doesn't mean your program is actually broken when it runs. It's meant to spot functions that will most likely cause a stack overflow, so it's super useful in environments with a limited stack size, like the Linux kernel.
  • -mwarn-dynamicstack โ€” emits a warning if the function calls alloca or uses dynamically-sized arrays ๐Ÿ’€ That's generally a bad idea when the stack size is limited, so this one's giving caution.
  • -mstack-guard=stack-guard, -mstack-size=stack-size โ€” okay this one is lowkey intense ๐Ÿ˜ญ If you give these options, the S/390 back end adds extra instructions to the function prologue that trigger a trap when the stack size goes stack-guard bytes above the stack-size (remember, the stack on S/390 grows downward). If you leave out -mstack-guard, GCC picks the smallest power of 2 that's bigger than the frame size of the compiled function. These are meant for debugging stack overflow problems ๐Ÿงš The extra code only adds a little overhead, so it's fine for production-like systems without a big performance hit. The values have to be exact powers of 2, and stack-size has to be greater than stack-guard without going over 64k. To be efficient, the extra code assumes the stack starts at an address aligned to the value given by stack-size. And -mstack-guard can only be used together with -mstack-size ๐Ÿ’…
  • -mhotpatch=pre-halfwords,post-halfwords โ€” if the hotpatch option is enabled, GCC generates a "hot-patching" function prologue for every function in the compilation unit ๐ŸŒธ The function label gets prepended with the given number of two-byte NOP instructions (pre-halfwords, maximum 1000000). After the label, 2 * post-halfwords bytes get appended, using the largest NOP-like instructions the architecture allows (maximum 1000000).

If both arguments are zero, hotpatching is disabled.

You can override this for individual functions with the "hotpatch" attribute โœจ


โœจ SH Options โœจ

These -m options are defined for the SH implementations ๐ŸŒˆ

  • -m1 โ€” generates code for the SH1 ๐Ÿ’–
  • -m2 โ€” generates code for the SH2 ๐Ÿ’–
  • -m2e โ€” generates code for the SH2e ๐Ÿ’–
  • -m2a-nofpu โ€” generates code for the SH2a without an FPU, or for a SH2a-FPU in a way that the floating-point unit isn't used ๐Ÿฆ‹
  • -m2a-single-only โ€” generates code for the SH2a-FPU so that no double-precision floating-point operations are used ๐Ÿฆ‹
  • -m2a-single โ€” generates code for the SH2a-FPU, assuming the floating-point unit is in single-precision mode by default ๐Ÿฆ‹
  • -m2a โ€” generates code for the SH2a-FPU, assuming the floating-point unit is in double-precision mode by default ๐Ÿฆ‹
  • -m3 โ€” generates code for the SH3 ๐Ÿ’–
  • -m3e โ€” generates code for the SH3e ๐Ÿ’–
  • -m4-nofpu โ€” generates code for the SH4 without a floating-point unit ๐ŸŒธ
  • -m4-single-only โ€” generates code for the SH4 with a floating-point unit that only supports single-precision arithmetic ๐ŸŒธ
  • -m4-single โ€” generates code for the SH4, assuming the floating-point unit is in single-precision mode by default ๐ŸŒธ
  • -m4 โ€” generates code for the SH4 ๐ŸŒธ
  • -m4-100 โ€” generates code for SH4-100 ๐Ÿ“
  • -m4-100-nofpu โ€” generates code for SH4-100 in a way that the floating-point unit isn't used ๐Ÿ“
  • -m4-100-single โ€” generates code for SH4-100, assuming the floating-point unit is in single-precision mode by default ๐Ÿ“
  • -m4-100-single-only โ€” generates code for SH4-100 so that no double-precision floating-point operations are used ๐Ÿ“
  • -m4-200 โ€” generates code for SH4-200 ๐Ÿซถ
  • -m4-200-nofpu โ€” generates code for SH4-200 without, like, in a way that the floating-point unit isn't used ๐Ÿซถ
  • -m4-200-single โ€” generates code for SH4-200, assuming the floating-point unit is in single-precision mode by default ๐Ÿซถ
  • -m4-200-single-only โ€” generates code for SH4-200 so that no double-precision floating-point operations are used ๐Ÿซถ
  • -m4-300 โ€” generates code for SH4-300 ๐Ÿงš
  • -m4-300-nofpu โ€” generates code for SH4-300 without, again, the floating-point unit being used ๐Ÿงš
  • -m4-300-single โ€” generates code for SH4-300 so that no double-precision floating-point operations are used ๐Ÿงš
  • -m4-300-single-only โ€” generates code for SH4-300 so that no double-precision floating-point operations are used ๐Ÿงš
  • -m4-340 โ€” generates code for SH4-340 (no MMU, no FPU) ๐Ÿ’œ
  • -m4-500 โ€” generates code for SH4-500 (no FPU). It also passes -isa=sh4-nofpu to the assembler ๐Ÿ’œ
  • -m4a-nofpu โ€” generates code for the SH4al-dsp, or for a SH4a in a way that the floating-point unit isn't used ๐ŸŒˆ
  • -m4a-single-only โ€” generates code for the SH4a so that no double-precision floating-point operations are used ๐ŸŒˆ
  • -m4a-single โ€” generates code for the SH4a, assuming the floating-point unit is in single-precision mode by default ๐ŸŒˆ
  • -m4a โ€” generates code for the SH4a โญ
  • -m4al โ€” same as -m4a-nofpu, except it implicitly passes -dsp to the assembler. GCC doesn't generate any DSP instructions at the moment, so don't expect them ๐Ÿ™ˆ
  • -mb โ€” compiles code for the processor in big-endian mode ๐ŸŽ€
  • -ml โ€” compiles code for the processor in little-endian mode ๐ŸŽ€
  • -mdalign โ€” aligns doubles at 64-bit boundaries ๐Ÿซถ
Heads up, bestie: this changes the calling conventions, so some functions from the standard C library won't work unless you recompile them first with -mdalign ๐Ÿ˜ญ
  • -mrelax โ€” shortens some address references at link time, when it's possible. It uses the linker option -relax โœจ
  • -mbigtable โ€” uses 32-bit offsets in "switch" tables. The default is 16-bit offsets ๐Ÿ“
  • -mbitops โ€” enables the use of bit manipulation instructions on SH2A ๐Ÿ’…
  • -mfmovd โ€” enables the use of the instruction "fmovd". Check -mdalign for the alignment constraints ๐Ÿ’…
  • -mrenesas โ€” makes the code comply with the calling conventions defined by Renesas ๐Ÿ‘‘
  • -mno-renesas โ€” makes the code comply with the calling conventions GCC used before the Renesas conventions were available. This is the default for all targets of the SH toolchain ๐Ÿ‘‘
  • -mnomacsave โ€” marks the "MAC" register as call-clobbered, even if -mrenesas is given ๐Ÿฆ‹
  • -mieee, -mno-ieee โ€” these control the IEEE compliance of floating-point comparisons, which affects how cases where a comparison result is unordered get handled ๐Ÿ”ฅ By default, -mieee is implicitly enabled. If -ffinite-math-only is enabled, -mno-ieee is implicitly set, which makes floating-point greater-equal and less-equal comparisons faster โญ The implicit settings can be overridden by specifying either -mieee or -mno-ieee ๐Ÿ’–

โœจ MACHINE-DEPENDENT OPTIONS โœจ

  • -minline-ic_invalidate ๐Ÿงš โ€” okay so this one inlines code that invalidates instruction cache entries right after the compiler sets up nested function trampolines. It does nothing if -musermode is on and your chosen code generation option (like -m4) doesn't allow the icbi instruction. If your code generation option doesn't allow icbi and -musermode isn't on, the inlined code pokes the instruction cache address array directly with an associative write. That needs privileged mode at run time, and it breaks if the cache line was mapped through the TLB and got unmapped. ๐Ÿ’€
  • -misize ๐Ÿ“ โ€” dumps the instruction size and location into the assembly code. Very useful if you're a size-obsessed girlie ๐Ÿ“
  • -mpadstruct ๐Ÿ™ˆ โ€” this one is deprecated, so please don't start new projects with it ๐Ÿ˜ญ. It pads structures to a multiple of 4 bytes, which is incompatible with the SH ABI.
  • -matomic-model=model ๐Ÿ’œ โ€” sets the model of atomic operations, plus extra parameters, as a comma separated list. For the full tea on atomic built-in functions, check out __atomic Builtins. These are the models and parameters you can pick from:
    • none ๐Ÿซฅ โ€” disables compiler generated atomic sequences and emits library calls for atomic operations instead. This is the default if the target is not sh*-*-linux*.
    • soft-gusa โœจ โ€” generates GNU/Linux compatible gUSA software atomic sequences for the atomic built-in functions. These sequences need extra help from the interrupt/exception handling code of the system, and they're only suitable for SH3* and SH4* single-core systems. This option is on by default when the target is sh*-*-linux* and SH3* or SH4*. When the target is SH4A, it also partially uses the hardware atomic instructions movli.l and movco.l to make the code more efficient, unless strict is specified.
    • soft-tcb ๐ŸŽ€ โ€” generates software atomic sequences that use a variable in the thread control block. It's a variation of the gUSA sequences that can also run on SH1* and SH2* targets. These sequences also need extra support from the interrupt/exception handling code, and they're only suitable for single-core systems. When you use this model, you have to specify the gbr-offset= parameter too.
    • soft-imask ๐Ÿ”’ โ€” generates software atomic sequences that temporarily disable interrupts by setting SR.IMASK = 1111. This model only works when the program runs in privileged mode and is only suitable for single-core systems. It doesn't need extra support from the interrupt/exception handling code. It's on by default when the target is sh*-*-linux* and SH1* or SH2*.
    • hard-llcs ๐Ÿ”ฅ โ€” generates hardware atomic sequences using only the movli.l and movco.l instructions. This is only available on SH4A and is suitable for multi-core systems. The hardware instructions only support 32 bit atomic variables, so 8 or 16 bit variables get emulated with 32 bit accesses. Code compiled with this is also compatible with other software atomic model interrupt/exception handling systems if it runs on an SH4A system. This model doesn't need any extra support from the interrupt/exception handling code.
    • gbr-offset= ๐Ÿฆ‹ โ€” specifies the offset in bytes of the variable in the thread control block structure that the generated atomic sequences should use when the soft-tcb model is selected. For other models this parameter is ignored. The value must be an integer multiple of four and in the range 0-1020.
    • strict ๐Ÿ‘‘ โ€” prevents mixing multiple atomic models, even if they're compatible, and makes the compiler generate atomic sequences of the specified model only.
  • -mtas ๐Ÿ’… โ€” generates the tas.b opcode for __atomic_test_and_set. Heads up: depending on your hardware and software setup, this can tank overall performance because of the operand cache line flushes that the tas.b instruction implies. On multi-core SH4A processors, tas.b has to be used with caution, since it can corrupt data for certain cache configurations. ๐Ÿ˜ฌ
  • -mprefergot ๐ŸŒธ โ€” when generating position-independent code, emits function calls through the Global Offset Table instead of the Procedure Linkage Table.
  • -musermode, -mno-usermode ๐Ÿ™…โ€โ™€๏ธ โ€” -mno-usermode means the compiler is not allowed to generate privileged mode code, and -musermode means it is not allowed... wait, babe, let me be precise: -musermode says don't allow the compiler to generate privileged mode code, and -mno-usermode allows it. -musermode also implies -mno-inline-ic_invalidate if the inlined code wouldn't work in user mode. -musermode is the default when the target is sh*-*-linux*. If the target is SH1* or SH2*, -musermode does nothing, since there is no user mode there.
  • -multcost=number โœ–๏ธ โ€” sets the cost the compiler assumes for a multiply instruction.
  • -mdiv=strategy โž— โ€” sets the division strategy for integer division operations. strategy can be one of these:
    • call-div1 ๐Ÿ“ž โ€” calls a library function that uses the single-step division instruction div1 to do the operation. Dividing by zero gives an unspecified result and doesn't trap. This is the default except for SH4, SH2A and SHcompact.
    • call-fp ๐Ÿงฎ โ€” calls a library function that does the operation in double precision floating point. Dividing by zero causes a floating-point exception. This is the default for SHcompact with FPU. If you pick this for targets without a double precision FPU, it falls back to call-div1.
    • call-table ๐Ÿ—‚๏ธ โ€” calls a library function that uses a lookup table for small divisors, and the div1 instruction with case distinction for bigger divisors. Dividing by zero gives an unspecified result and doesn't trap. This is the default for SH4. If you pick this for targets without dynamic shift instructions, it falls back to call-div1.

When you don't specify a division strategy, the compiler picks a default based on the current target. For SH2A, the default is to use the divs and divu instructions instead of library function calls.

  • -maccumulate-outgoing-args ๐Ÿ“ฆ โ€” reserves space once for outgoing arguments in the function prologue, instead of around each call. This is usually great for performance and size. It's also needed for unwinding, so the stack frame doesn't change around conditional code.
  • -mdivsi3_libfunc=name ๐Ÿท๏ธ โ€” sets the name of the library function used for 32-bit signed division to name. This only changes the name used in the call division strategies. The compiler still expects the same sets of input/output/clobbered registers as if this option weren't there.
  • -mfixed-range=register-range ๐Ÿงฑ โ€” makes the compiler treat the given register range as fixed registers. A fixed register is one the register allocator isn't allowed to use. This is handy when you're compiling kernel code. A register range is two registers separated by a dash, and you can list multiple ranges separated by a comma.
  • -mbranch-cost=num ๐ŸŒฟ โ€” assumes num is the cost of a branch instruction. Higher numbers make the compiler try harder to generate branch-free code when it can. If you don't specify it, the value is picked based on the processor type you're compiling for.
  • -mzdcbranch, -mno-zdcbranch โšก โ€” -mzdcbranch assumes zero displacement conditional branch instructions bt and bf are fast, so the compiler prefers zero displacement branch code sequences. -mno-zdcbranch says that assumption doesn't hold. This is on by default for SH4 and SH4A, and you can turn it off explicitly with -mno-zdcbranch.
  • -mcbranch-force-delay-slot ๐Ÿข โ€” forces the use of delay slots for conditional branches. If no suitable instruction is found, the delay slot gets stuffed with a nop. This option is disabled by default. Turn it on to work around hardware bugs like the ones found in the original SH7055.
  • -mfused-madd, -mno-fused-madd ๐Ÿง โ€” -mfused-madd generates code that uses the floating-point multiply and accumulate instructions, and -mno-fused-madd generates code that doesn't. These instructions are generated by default if hardware floating point is used. The machine-dependent -mfused-madd option is now mapped to the machine-independent -ffp-contract=fast option, and -mno-fused-madd is mapped to -ffp-contract=off.
  • -mfsca, -mno-fsca ๐Ÿ“ โ€” allows or disallows the compiler from emitting the fsca instruction for sine and cosine approximations. -mfsca has to be used together with -funsafe-math-optimizations. It's enabled by default when generating code for SH4A. -mno-fsca disables sine and cosine approximations even if -funsafe-math-optimizations is in effect.
  • -mfsrra, -mno-fsrra ๐Ÿ”ข โ€” allows or disallows the compiler from emitting the fsrra instruction for reciprocal square root approximations. -mfsrra has to be used together with -funsafe-math-optimizations and -ffinite-math-only. It's enabled by default when generating code for SH4A. -mno-fsrra disables reciprocal square root approximations even if -funsafe-math-optimizations and -ffinite-math-only are in effect.
  • -mpretend-cmove ๐ŸŽญ โ€” prefers zero-displacement conditional branches for conditional move instruction patterns. This can make code run faster on the SH4 processor.
  • -mfdpic ๐ŸŒˆ โ€” generates code using the FDPIC ABI.

๐Ÿ’œ Solaris 2 Options ๐Ÿ’œ

These -m options are supported on Solaris 2 ๐ŸŒž

  • -mclear-hwcap ๐Ÿงน โ€” tells the compiler to remove the hardware capabilities that the Solaris assembler generated. You only need this when object files use ISA extensions the current machine doesn't support, but they check at runtime whether or not to use them.
  • -mimpure-text ๐Ÿฆ„ โ€” used together with -shared, this tells the compiler not to pass -z text to the linker when linking a shared object. With this option you can link position-dependent code into a shared object.

Okay so, -mimpure-text is the one that silences the "relocations remain against allocatable but non-writable sections" linker error ๐Ÿ™ˆ but bestie, it's a trap. The relocations trigger copy-on-write, so the shared object isn't actually shared across processes anymore ๐Ÿ’€ The move is to compile all your source with -fpic or -fPIC instead โœจ

These switches also work on Solaris 2, on top of everything above:

  • -pthreads โ€” literally just a synonym for -pthread ๐Ÿงš

โœจ SPARC Options โœจ

These -m options are supported on the SPARC ๐Ÿ’œ

  • -mno-app-regs, -mapp-regs โ€” -mapp-regs is the default and it's giving reserved vibes ๐Ÿซถ It makes the compiler use global registers 2 through 4, which the SPARC SVR4 ABI sets aside for applications. Just like global register 1, each of 2 through 4 is treated as an allocable register that function calls are allowed to clobber. If you want to be fully SVR4 ABI-compliant and you don't mind a bit of performance loss, go with -mno-app-regs. Libraries and system software should be compiled with this one ๐ŸŽ€
  • -mflat, -mno-flat โ€” With -mflat, the compiler skips the save/restore instructions and uses a "flat" single register window model ๐Ÿฆ‹ That's compatible with the regular register window model. The local registers and the input registers (0--5) are still treated as "call-saved" and get saved on the stack when needed. -mno-flat is the default, and it generates save/restore instructions everywhere except leaf functions. That's the normal operating mode ๐Ÿ’…
  • -mfpu, -mhard-float โ€” generates output with floating-point instructions. This is the default ๐Ÿ”ฅ
  • -mno-fpu, -msoft-float โ€” generates output that calls library functions for floating point instead ๐Ÿ˜ญ
Heads up: the required libraries aren't available for every SPARC target. Normally GCC borrows the facilities of the machine's usual C compiler, but that can't be done directly when cross-compiling, so you need to sort out your own suitable library functions for cross-compilation. The embedded targets sparc-*-aout and sparclite-*-* do come with software floating-point support.

-msoft-float changes the calling convention in the output file, so it only makes sense if you compile the whole program with it. Specifically, libgcc.a, the library that ships with GCC, has to be built with -msoft-float for this to work ๐Ÿ™ˆ

  • -mhard-quad-float โ€” generates output with quad-word (long double) floating-point instructions ๐ŸŒˆ
  • -msoft-quad-float โ€” generates output that calls library functions for quad-word (long double) floating-point instructions. Those functions are the ones defined in the SPARC ABI, and this is the default ๐Ÿ’–

As of this writing, no SPARC implementations have hardware support for the quad-word floating-point instructions ๐Ÿ’€ Every one of them hits a trap handler for these instructions, and then the trap handler emulates what the instruction would have done. Because of that trap handler overhead it's way slower than calling the ABI library routines, which is exactly why -msoft-quad-float is the default โœจ

  • -mno-unaligned-doubles, -munaligned-doubles โ€” -mno-unaligned-doubles is the default, and it assumes doubles have 8-byte alignment ๐Ÿ“

With -munaligned-doubles, GCC only assumes 8-byte alignment for doubles if they're contained in another type or have an absolute address. Otherwise it assumes 4-byte alignment. Using this option helps dodge some rare compatibility problems with code from other compilers, but it's not the default because it costs performance, especially in floating-point code ๐Ÿฅบ

  • -muser-mode, -mno-user-mode โ€” -mno-user-mode is the default, and it means no code that can only run in supervisor mode gets generated ๐Ÿ‘‘ This only matters for the "casa" instruction emitted for the LEON3 processor.
  • -mfaster-structs, -mno-faster-structs โ€” With -mfaster-structs, the compiler assumes structures should have 8-byte alignment. That unlocks pairs of "ldd" and "std" instructions for copies in structure assignment, instead of twice as many "ld" and "st" pairs โšก BUT the changed alignment directly violates the SPARC ABI, so it's only meant for targets where the developer knows and accepts that the code doesn't follow the ABI rules ๐Ÿ™ˆ
  • -mstd-struct-return, -mno-std-struct-return โ€” With -mstd-struct-return, the compiler generates checking code in functions that return structures or unions, so it can catch size mismatches between the two sides of a function call, as the 32-bit ABI requires ๐Ÿ” The default is -mno-std-struct-return, and this option does nothing in 64-bit mode.
  • -mcpu=cpu_type โ€” sets the instruction set, register set, and instruction scheduling parameters for the machine type cpu_type โœจ The supported values are v7, cypress, v8, supersparc, hypersparc, leon, leon3, leon3v7, leon5, sparclite, f930, f934, sparclite86x, sparclet, tsc701, v9, ultrasparc, ultrasparc3, niagara, niagara2, niagara3, niagara4, niagara7 and m8.

Native Solaris and GNU/Linux toolchains also accept native, which picks the best architecture option for your host processor ๐Ÿงš If GCC doesn't recognise the processor, -mcpu=native does nothing.

For values that name an architecture rather than a specific implementation (v7, v8, sparclite, sparclet, v9), GCC uses the default instruction scheduling parameters ๐Ÿ’…

Here's the full breakdown of each architecture and the implementations it covers:

  • v7 โ€” cypress, leon3v7
  • v8 โ€” supersparc, hypersparc, leon, leon3, leon5
  • sparclite โ€” f930, f934, sparclite86x
  • sparclet โ€” tsc701
  • v9 โ€” ultrasparc, ultrasparc3, niagara, niagara2, niagara3, niagara4, niagara7, m8

By default (unless it's been configured otherwise), GCC generates V7 code ๐ŸŒธ With -mcpu=cypress, it also optimises for the Cypress CY7C602 chip, the one in the SPARCStation/SPARCServer 3xx series. That's also a good fit for the older SPARCStation 1, 2, IPX and friends.

With -mcpu=v8, GCC generates V8 code. The only real difference from V7 is that it emits the integer multiply and integer divide instructions that exist in SPARC-V8 but not SPARC-V7 โšก With -mcpu=supersparc, it also optimises for the SuperSPARC chip, found in the SPARCStation 10, 1000 and 2000 series.

With -mcpu=sparclite, GCC generates code for the SPARClite variant. That adds the integer multiply, integer divide step and scan ("ffs") instructions, which SPARClite has but SPARC-V7 doesn't ๐Ÿฆ‹ With -mcpu=f930, it also optimises for the Fujitsu MB86930, the original SPARClite, which has no FPU. With -mcpu=f934, it optimises for the Fujitsu MB86934, the newer SPARClite that does have an FPU.

With -mcpu=sparclet, GCC generates code for the SPARClet variant. That adds integer multiply, multiply/accumulate, integer divide step and scan ("ffs") instructions, all things SPARClet has and SPARC-V7 doesn't ๐ŸŽ€ With -mcpu=tsc701, it also optimises for the TEMIC SPARClet chip.

With -mcpu=v9, GCC generates V9 code, which adds 64-bit integer and floating-point move instructions, 3 extra floating-point condition code registers and conditional move instructions ๐ŸŒˆ With -mcpu=ultrasparc, it also optimises for the Sun UltraSPARC I/II/IIi chips. -mcpu=ultrasparc3 targets the Sun UltraSPARC III/III+/IIIi/IIIi+/IV/IV+ chips. -mcpu=niagara targets Sun UltraSPARC T1 chips, -mcpu=niagara2 targets Sun UltraSPARC T2 chips, -mcpu=niagara3 targets Sun UltraSPARC T3 chips, and -mcpu=niagara4 targets Sun UltraSPARC T4 chips. -mcpu=niagara7 optimises for Oracle SPARC M7 chips, and -mcpu=m8 optimises for Oracle M8 chips ๐Ÿ‘‘

  • -mtune=cpu_type โ€” sets the instruction scheduling parameters for the machine type cpu_type, but leaves alone the instruction set and register set that -mcpu=cpu_type would set ๐Ÿ’–

The same values work for -mtune=cpu_type as for -mcpu=cpu_type, but only the ones that pick a specific CPU implementation are actually useful: cypress, supersparc, hypersparc, leon, leon3, leon3v7, leon5, f930, f934, sparclite86x, tsc701, ultrasparc, ultrasparc3, niagara, niagara2, niagara3, niagara4, niagara7 and m8. On native Solaris and GNU/Linux toolchains you can use native here too ๐Ÿงš

  • -mv8plus, -mno-v8plus โ€” -mv8plus makes GCC generate code for the SPARC-V8+ ABI ๐Ÿ”ฅ The difference from the V8 ABI is that the global and out registers are treated as 64 bits wide. This is on by default on Solaris in 32-bit mode for all SPARC-V9 processors.
  • -mvis, -mno-vis โ€” -mvis makes GCC generate code that takes advantage of the UltraSPARC Visual Instruction Set extensions โœจ The default is -mno-vis.
  • -mvis2, -mno-vis2 โ€” -mvis2 makes GCC use version 2.0 of the UltraSPARC Visual Instruction Set extensions ๐Ÿฆ‹ The default is -mvis2 when you're targeting a CPU that supports those instructions, like UltraSPARC-III and later. Setting -mvis2 also sets -mvis.
  • -mvis3, -mno-vis3 โ€” -mvis3 makes GCC use version 3.0 of the UltraSPARC Visual Instruction Set extensions ๐Ÿ’œ The default is -mvis3 when you're targeting a CPU that supports those, like niagara-3 and later. Setting -mvis3 also sets -mvis2 and -mvis.
  • -mvis3b, -mno-vis3b โ€” okay bestie, -mvis3b tells GCC to write code that uses version 3.0 of the UltraSPARC Visual Instruction Set extensions, plus the extra VIS instructions from the Oracle SPARC Architecture 2011 ๐Ÿฆ‹. It's on by default when you target a cpu that supports them, like niagara-7 and later. Turning it on also turns on -mvis3, -mvis2 and -mvis โœจ.
  • -mvis4, -mno-vis4 โ€” -mvis4 gets GCC generating code for version 4.0 of the UltraSPARC Visual Instruction Set extensions ๐Ÿ’…. It's the default when targeting a cpu that supports them, such as niagara-7 and later. Setting -mvis4 also sets -mvis3b, -mvis3, -mvis2 and -mvis ๐ŸŒธ.
  • -mvis4b, -mno-vis4b โ€” -mvis4b is giving version 4.0 of the UltraSPARC Visual Instruction Set extensions, plus the additional VIS instructions from the Oracle SPARC Architecture 2017 ๐Ÿ”ฅ. It's the default when targeting a cpu that supports them, such as m8 and later. Setting -mvis4b also sets -mvis4, -mvis3b, -mvis3, -mvis2 and -mvis ๐Ÿ‘‘.
  • -mcbcond, -mno-cbcond โ€” -mcbcond makes GCC use the UltraSPARC Compare-and-Branch-on-Condition instructions ๐Ÿงš. It's on by default when targeting a CPU that has them, like Niagara-4 and later.
  • -mfmaf, -mno-fmaf โ€” -mfmaf lets GCC use the UltraSPARC Fused Multiply-Add Floating-point instructions ๐Ÿ’œ. Default when targeting a CPU that supports them, such as Niagara-3 and later.
  • -mfsmuld, -mno-fsmuld โ€” -mfsmuld has GCC take advantage of the Floating-point Multiply Single to Double (FsMULd) instruction ๐Ÿ“. It's the default when targeting a CPU supporting the architecture versions V8 or V9 with FPU, except -mcpu=leon.
  • -mpopc, -mno-popc โ€” -mpopc has GCC using the UltraSPARC Population Count instruction ๐ŸŒˆ. Default when targeting a CPU that has one, such as Niagara-2 and later.
  • -msubxc, -mno-subxc โ€” -msubxc opts into the UltraSPARC Subtract-Extended-with-Carry instruction ๐ŸŽ€. It's the default when targeting a CPU that supports it, such as Niagara-7 and later.
  • -mfix-at697f โ€” turns on the documented workaround for the single erratum of the Atmel AT697F processor, which is erratum #13 of the AT697E processor ๐Ÿ™ˆ.
  • -mfix-ut699 โ€” turns on the documented workarounds for the floating-point errata and the data cache nullify errata of the UT699 processor โœจ.
  • -mfix-ut700 โ€” turns on the documented workaround for the back-to-back store errata of the UT699E/UT700 processor ๐Ÿซถ.
  • -mfix-gr712rc โ€” turns on the documented workaround for the back-to-back store errata of the GR712RC processor ๐Ÿ’–.

These -m options are supported in addition to the above on SPARC-V9 processors in 64-bit environments ๐Ÿฆ‹:

  • -m32, -m64 โ€” generates code for a 32-bit or 64-bit environment ๐ŸŒธ. The 32-bit one sets int, long and pointer to 32 bits. The 64-bit one sets int to 32 bits and long and pointer to 64 bits.
  • -mcmodel=which โ€” sets the code model to one of these:
    • medlow โ€” The Medium/Low code model: 64-bit addresses, and programs must be linked in the low 32 bits of memory ๐Ÿ’…. Programs can be statically or dynamically linked.
    • medmid โ€” The Medium/Middle code model: 64-bit addresses, and programs must be linked in the low 44 bits of memory ๐Ÿงš. The text and data segments must be less than 2GB in size, and the data segment must be located within 2GB of the text segment.
    • medany โ€” The Medium/Anywhere code model: 64-bit addresses, and programs may be linked anywhere in memory ๐ŸŒˆ. The text and data segments must be less than 2GB in size, and the data segment must be located within 2GB of the text segment.
    • embmedany โ€” The Medium/Anywhere code model for embedded systems: 64-bit addresses ๐Ÿ”ฅ. The text and data segments must be less than 2GB in size, both starting anywhere in memory (determined at link time). The global register %g4 points to the base of the data segment. Programs are statically linked and PIC is not supported.
  • -mmemory-model=mem-model โ€” sets the memory model on the processor to one of these:
    • default โ€” the default memory model for the processor and operating system ๐Ÿ“.
    • rmo โ€” Relaxed Memory Order
    • pso โ€” Partial Store Order
    • tso โ€” Total Store Order
    • sc โ€” Sequential Consistency

These memory models are formally defined in Appendix D of the SPARC-V9 architecture manual, and they're set in the processor's "PSTATE.MM" field ๐Ÿซถ.

  • -mstack-bias, -mno-stack-bias โ€” -mstack-bias tells GCC to assume the stack pointer, and the frame pointer if there is one, are offset by -2047, which has to be added back when making stack frame references ๐Ÿ’œ. This is the default in 64-bit mode. Otherwise, GCC assumes no such offset is present.

โœจ Options for System V โœจ

These additional options are available on System V Release 4 for compatibility with other compilers on those systems ๐Ÿ’–:

  • -G โ€” creates a shared object ๐ŸŽ€. It's recommended to use -symbolic or -shared instead.
  • -Qy โ€” identifies the versions of each tool used by the compiler, in a ".ident" assembler directive in the output ๐Ÿงš.
  • -Qn โ€” refrains from adding ".ident" directives to the output file, which is the default ๐ŸŒธ.
  • -YP,dirs โ€” searches the directories dirs, and no others, for libraries specified with -l ๐Ÿฆ‹.
  • -Ym,dir โ€” looks in the directory dir to find the M4 preprocessor ๐Ÿ”ฅ. The assembler uses this option.

๐Ÿ‘‘ V850 Options ๐Ÿ‘‘

These -m options are defined for V850 implementations ๐Ÿ’…:

  • -mlong-calls, -mno-long-calls โ€” treats all calls as being far away (near) ๐ŸŒˆ. If calls are assumed to be far away, the compiler always loads the function's address into a register, and calls indirect through the pointer.
  • -mno-ep, -mep โ€” -mno-ep means do not optimize basic blocks that use the same index pointer 4 or more times to copy the pointer into the "ep" register, and use the shorter "sld" and "sst" instructions ๐Ÿ“. -mep means do optimize them. The -mep option is on by default if you optimize โœจ.
  • -mno-prolog-function, -mprolog-function โ€” -mno-prolog-function means do not use external functions to save and restore registers at the prologue and epilogue of a function ๐Ÿ™ˆ. -mprolog-function means do use them. The external functions are slower, but they use less code space if more than one function saves the same number of registers. The -mprolog-function option is on by default if you optimize ๐Ÿ’œ.
  • -mspace โ€” tries to make the code as small as possible ๐ŸŽ€. At present, this just turns on the -mep and -mprolog-function options.
  • -mtda=n โ€” puts static or global variables whose size is n bytes or less into the tiny data area that register "ep" points to ๐Ÿงš. The tiny data area can hold up to 256 bytes in total (128 bytes for byte references).
  • -msda=n โ€” puts static or global variables whose size is n bytes or less into the small data area that register "gp" points to ๐ŸŒธ. The small data area can hold up to 64 kilobytes.
  • -mzda=n โ€” puts static or global variables whose size is n bytes or less into the first 32 kilobytes of memory ๐Ÿฆ‹.
  • -mv850 โ€” specifies that the target processor is the V850 ๐Ÿ”ฅ.
  • -mv850e3v5 โ€” specifies that the target processor is the V850E3V5 ๐Ÿ‘‘. The preprocessor constant "__v850e3v5__" is defined if this option is used.
  • -mv850e2v4 โ€” specifies that the target processor is the V850E3V5 ๐Ÿ’…. This is an alias for the -mv850e3v5 option.
  • -mv850e2v3 โ€” specifies that the target processor is the V850E2V3 ๐ŸŒˆ. The preprocessor constant "__v850e2v3__" is defined if this option is used.
  • -mv850e2 โ€” specifies that the target processor is the V850E2 ๐Ÿ“. The preprocessor constant "__v850e2__" is defined if this option is used.
  • -mv850e1 โ€” specifies that the target processor is the V850E1 โœจ. The preprocessor constants "__v850e1__" and "__v850e__" are defined if this option is used.
  • -mv850es โ€” specifies that the target processor is the V850ES ๐Ÿซถ. This is an alias for the -mv850e1 option.
  • -mv850e โ€” specifies that the target processor is the V850E ๐Ÿ’–. The preprocessor constant "__v850e__" is defined if this option is used.

If neither -mv850 nor -mv850e nor -mv850e1 nor -mv850e2 nor -mv850e2v3 nor -mv850e3v5 are defined, then a default target processor is chosen and the relevant __v850*__ preprocessor constant is defined ๐Ÿงš.

The preprocessor constants "__v850" and "__v851__" are always defined, regardless of which processor variant is the target ๐ŸŽ€.

  • -mdisable-callt, -mno-disable-callt โ€” -mdisable-callt suppresses generation of the "CALLT" instruction for the v850e, v850e1, v850e2, v850e2v3 and v850e3v5 flavors of the v850 architecture ๐ŸŒธ.

This option is enabled by default when the RH850 ABI is in use (see -mrh850-abi), and disabled by default when the GCC ABI is in use ๐Ÿฆ‹. If "CALLT" instructions are being generated, then the C preprocessor symbol "__V850_CALLT__" is defined.

  • -mrelax, -mno-relax โ€” passes on (or does not pass on) the -mrelax command-line option to the assembler ๐Ÿ”ฅ.
  • -mlong-jumps, -mno-long-jumps โ€” -mlong-jumps disables the generation of PC-relative jump instructions, and -mno-long-jumps re-enables them ๐Ÿ‘‘.
  • -msoft-float, -mhard-float โ€” -msoft-float disables the generation of hardware floating point instructions, and -mhard-float re-enables them ๐Ÿ’…. This option is only significant when the target architecture is V850E2V3 or higher. If hardware floating point instructions are being generated, then the C preprocessor symbol "__FPU_OK__" is defined; otherwise the symbol "__NO_FPU__" is defined ๐ŸŒˆ.
  • -mloop โ€” enables the use of the e3v5 LOOP instruction ๐Ÿ“. The use of this instruction is not enabled by default when the e3v5 architecture is selected, because its use is still experimental โš ๏ธ.
  • -mrh850-abi, -mghs โ€” enables support for the RH850 version of the V850 ABI โœจ. This is the default. With this version of the ABI, the following rules apply:
    • Integer sized structures and unions are returned via a memory pointer rather than a register ๐Ÿซถ.
  • Large structures and unions (more than 8 bytes in size) are passed by value. ๐ŸŽ€
  • Functions are aligned to 16-bit boundaries. โœจ
  • The -m8byte-align command-line option is supported. ๐Ÿ’–
  • The -mdisable-callt command-line option is enabled by default. The -mno-disable-callt command-line option is not supported. ๐Ÿซถ

When this version of the ABI is enabled, the C preprocessor symbol "__V850_RH850_ABI__" gets defined. ๐ŸŒˆ

  • -mgcc-abi โ€” okay so this one turns on the old GCC version of the V850 ABI ๐Ÿ•ฐ๏ธ ๐Ÿ’… With this version, these rules apply:
    • Integer sized structures and unions are returned in register "r10". ๐Ÿ’œ
    • Large structures and unions (more than 8 bytes in size) are passed by reference. ๐Ÿฆ‹
    • Functions are aligned to 32-bit boundaries, unless you're optimizing for size. ๐ŸŒธ
    • The -m8byte-align command-line option is not supported. ๐Ÿ™ˆ
    • The -mdisable-callt command-line option is supported but not enabled by default. โญ

When this version of the ABI is enabled, the C preprocessor symbol "__V850_GCC_ABI__" gets defined. ๐Ÿงš

  • -m8byte-align, -mno-8byte-align โ€” enables "double" and "long long" types to be aligned on 8-byte boundaries, bestie ๐Ÿ“ The default is to keep the alignment of all objects to at most 4-bytes. When -m8byte-align is in effect, the C preprocessor symbol "__V850_8BYTE_ALIGN__" gets defined. โœจ
  • -mbig-switch โ€” generates code that's suitable for big switch tables. Only use this one if the assembler/linker complains about out of range branches within a switch table. ๐Ÿ˜ญ
  • -mapp-regs โ€” makes r2 and r5 get used in the code the compiler generates. This is the default, so you're already set ๐Ÿ’…
  • -mno-app-regs โ€” makes r2 and r5 get treated as fixed registers. ๐Ÿ”’

๐ŸŒท VAX Options ๐ŸŒท

These -m options are defined for the VAX. ๐Ÿฆ‹

  • -munix โ€” don't output certain jump instructions ("aobleq" and so on) that the Unix assembler for the VAX can't handle across long ranges. ๐Ÿ™…โ€โ™€๏ธ
  • -mgnu โ€” do output those jump instructions, on the assumption that the GNU assembler is being used. ๐Ÿง 
  • -md, -md-float โ€” uses the D_floating data format for double-precision floating-point numbers instead of G_floating. ๐Ÿ’ซ
  • -mg, -mg-float โ€” uses the G_floating data format for double-precision floating-point numbers instead of D_floating. ๐Ÿ’–
  • -mlra, -mno-lra โ€” enables Local Register Allocation. This is still experimental for the VAX, so by default the compiler uses standard reload. ๐Ÿงช

๐Ÿ›ธ Visium Options ๐Ÿ›ธ

  • -mdebug โ€” a program that does file I/O and is destined to run on an MCM target should be linked with this option. It causes the libraries libc.a and libdebug.a to be linked. The program should be run on the target under the control of the GDB remote debugging stub. ๐Ÿ”ง
  • -msim โ€” a program that does file I/O and is destined to run on the simulator should be linked with option. This causes the libraries libc.a and libsim.a to be linked. ๐ŸŽฎ
  • -mfpu, -mhard-float โ€” generates code containing floating-point instructions. This is the default. ๐Ÿ”ฅ
  • -mno-fpu, -msoft-float โ€” generates code containing library calls for floating-point. ๐Ÿงฎ
โš ๏ธ bestie, -msoft-float changes the calling convention in the output file, so it's only useful if you compile all of a program with this option. In particular, you need to compile libgcc.a, the library that comes with GCC, with -msoft-float for this to work. ๐Ÿ’€
  • -mcpu=cpu_type โ€” sets the instruction set, register set, and instruction scheduling parameters for machine type cpu_type. Supported values for cpu_type are mcm, gr5 and gr6. ๐Ÿ‘‘
    • mcm is a synonym of gr5, kept around for backward compatibility. ๐Ÿซถ
    • By default (unless configured otherwise), GCC generates code for the GR5 variant of the Visium architecture. ๐ŸŒธ
    • With -mcpu=gr6, GCC generates code for the GR6 variant of the Visium architecture. The only difference from GR5 code is that the compiler will generate block move instructions. โœจ
  • -mtune=cpu_type โ€” sets the instruction scheduling parameters for machine type cpu_type, but does not set the instruction set or register set that -mcpu=cpu_type would. ๐ŸŽ€
  • -msv-mode โ€” generates code for supervisor mode, where there are no restrictions on access to general registers. This is the default. ๐Ÿ‘ธ
  • -muser-mode โ€” generates code for user mode, where access to some general registers is forbidden: on the GR5, registers r24 to r31 cannot be accessed in this mode; on the GR6, only registers r29 to r31 are affected. ๐Ÿ™ˆ

๐Ÿ’œ VMS Options ๐Ÿ’œ

These -m options are defined for the VMS implementations. ๐ŸŒŒ

  • -mvms-return-codes โ€” returns VMS condition codes from "main". The default is to return POSIX-style condition (e.g. error) codes. ๐Ÿงพ
  • -mdebug-main=prefix โ€” flags the first routine whose name starts with prefix as the main routine for the debugger. ๐Ÿ”
  • -mmalloc64 โ€” defaults to 64-bit memory allocation routines. ๐Ÿง 
  • -mpointer-size=size โ€” sets the default size of pointers. Possible options for size are 32 or short for 32 bit pointers, 64 or long for 64 bit pointers, and no for supporting only 32 bit pointers. The latter option disables "pragma pointer_size". ๐Ÿ“

๐ŸŒˆ VxWorks Options ๐ŸŒˆ

The options in this section are defined for all VxWorks targets. Options specific to the target hardware are listed with the other options for that target. ๐Ÿ› ๏ธ

  • -mrtp โ€” GCC can generate code for both VxWorks kernels and real time processes (RTPs). This option switches from the former to the latter. It also defines the preprocessor macro "__RTP__". ๐Ÿฆ‹
  • -msmp โ€” selects SMP runtimes for linking. Not available on architectures other than PowerPC, nor on VxWorks version 7 or later, where the selection is part of the VxWorks build configuration and the library paths are the same for either choice. ๐Ÿ™…โ€โ™€๏ธ
  • -non-static โ€” links an RTP executable against shared libraries rather than static libraries. The options -static and -shared can also be used for RTPs; -static is the default. ๐Ÿ”—
  • -Bstatic, -Bdynamic โ€” these options are passed down to the linker. They're defined for compatibility with Diab. ๐Ÿค
  • -Xbind-lazy โ€” enables lazy binding of function calls. This option is equivalent to -Wl,-z,now and is defined for compatibility with Diab. ๐Ÿข
  • -Xbind-now โ€” disables lazy binding of function calls. This option is the default and is defined for compatibility with Diab. โšก

๐Ÿฆ„ x86 Options ๐Ÿฆ„

These -m options are defined for the x86 family of computers. ๐Ÿ’ป

  • -march=cpu-type โ€” generates instructions for the machine type cpu-type. Unlike -mtune=cpu-type, which merely tunes the generated code for the specified cpu-type, -march=cpu-type allows GCC to generate code that may not run at all on processors other than the one indicated. Specifying -march=cpu-type implies -mtune=cpu-type, except where noted otherwise. ๐ŸŽ๏ธ

The choices for cpu-type are:

  • native โ€” selects the CPU to generate code for at compilation time by figuring out the processor type of the compiling machine. Using -march=native enables all instruction subsets supported by the local machine (so the result might not run on different machines). Using -mtune=native produces code optimized for the local machine under the constraints of the selected instruction set. ๐Ÿ 
  • x86-64 โ€” a generic CPU with 64-bit extensions, MMX, SSE, SSE2, and FXSR instruction set support. ๐ŸŒŸ
  • x86-64-v2, x86-64-v3, x86-64-v4 โ€” these choices select the corresponding micro-architecture level from the x86-64 psABI. On ABIs other than the x86-64 psABI, they select the same CPU features as the x86-64 psABI documents for the particular micro-architecture level.

Since these cpu-type values don't have a corresponding -mtune setting, using -march with these values enables generic tuning. Specific tuning can be enabled using the -mtune=other-cpu-type option with an appropriate other-cpu-type value. ๐ŸŽฏ

  • i386 โ€” the original Intel i386 CPU. ๐Ÿฆ•
  • i486 โ€” the Intel i486 CPU. (No scheduling is implemented for this chip.) ๐Ÿ™ƒ
  • i586, pentium โ€” the Intel Pentium CPU with no MMX support. ๐Ÿ’ฟ
  • lakemont โ€” the Intel Lakemont MCU, based on the Intel Pentium CPU. ๐Ÿ”ฌ
  • pentium-mmx โ€” the Intel Pentium MMX CPU, based on the Pentium core with MMX instruction set support. ๐ŸŽต
  • pentiumpro โ€” the Intel Pentium Pro CPU with no MMX support. ๐Ÿš€
  • i686 โ€” with -march, the Pentium Pro instruction set is used, so the code runs on all i686 family chips. With -mtune, it has the same meaning as generic. ๐ŸŒ
  • pentium2 โ€” the Intel Pentium II CPU, based on the Pentium Pro core with MMX and FXSR instruction set support. ๐Ÿ’
  • pentium3, pentium3m โ€” the Intel Pentium III CPU, based on the Pentium Pro core with MMX, FXSR and SSE instruction set support. ๐ŸŒป
  • pentium-m โ€” the Intel Pentium M, a low-power version of the Intel Pentium III CPU with MMX, SSE, SSE2 and FXSR instruction set support. Used by Centrino notebooks. ๐Ÿ’ผ
  • pentium4, pentium4m โ€” the Intel Pentium 4 CPU with MMX, SSE, SSE2 and FXSR instruction set support. ๐Ÿฉท
  • prescott โ€” an improved version of the Intel Pentium 4 CPU with MMX, SSE, SSE2, SSE3 and FXSR instruction set support. ๐ŸŒท
  • nocona โ€” an improved version of the Intel Pentium 4 CPU with 64-bit extensions, MMX, SSE, SSE2, SSE3 and FXSR instruction set support. ๐ŸŒบ
  • core2 โ€” the Intel Core 2 CPU with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, CX16, SAHF and FXSR instruction set support. โ˜•
  • nehalem, corei7 โ€” the Intel Nehalem CPU with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF and FXSR instruction set support. ๐ŸŒ™
  • westmere โ€” the Intel Westmere CPU with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR and PCLMUL instruction set support. ๐Ÿซง
  • sandybridge, corei7-avx โ€” the Intel Sandy Bridge CPU with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE and PCLMUL instruction set support. ๐ŸŒ…
  • ivybridge, core-avx-i ๐Ÿฆ‹ Intel Ivy Bridge CPU, bestie! It comes with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND and F16C instruction set support. Vintage but still iconic โœจ
  • haswell, core-avx2 ๐ŸŒธ Intel Haswell CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE and HLE instruction set support. The AVX2 glow-up is so real ๐Ÿ’…
  • broadwell ๐ŸŽ€ Intel Broadwell CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX and PREFETCHW instruction set support. Lowkey underrated ๐Ÿซถ
  • skylake ๐Ÿ”ฅ Intel Skylake CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES and SGX instruction set support. It's giving a whole upgrade ๐ŸŒˆ
  • skylake-avx512 ๐Ÿ‘‘ Intel Skylake Server CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, CLWB, AVX512VL, AVX512BW, AVX512DQ and AVX512CD instruction set support. Server girlies, this one's for you ๐Ÿซถ
  • cascadelake ๐Ÿ’œ Intel Cascade Lake CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, CLWB, AVX512VL, AVX512BW, AVX512DQ, AVX512CD and AVX512VNNI instruction set support. Serving VNNI realness โœจ
  • cannonlake ๐Ÿงš Intel Cannon Lake Server CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, PKU, AVX512VBMI, AVX512IFMA and SHA instruction set support. Okay, the list is giving main character ๐ŸŒŸ
  • cooperlake ๐Ÿ“ Intel Cooper Lake CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, CLWB, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, AVX512VNNI and AVX512BF16 instruction set support. BF16 is so cute, no cap ๐Ÿฅน
  • icelake-client ๐ŸŒŠ Intel Ice Lake Client CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID and AVX512VPOPCNTDQ instruction set support. The feature drop is literally unreal ๐Ÿ˜ญ
  • icelake-server ๐Ÿฐ Intel Ice Lake Server CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID, AVX512VPOPCNTDQ, PCONFIG, WBNOINVD and CLWB instruction set support. Server edition with all the extras ๐Ÿ’…
  • tigerlake ๐Ÿฏ Intel Tiger Lake CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID, AVX512VPOPCNTDQ, MOVDIRI, MOVDIR64B, CLWB, AVX512VP2INTERSECT and KEYLOCKER instruction set support. Tiger Lake is a whole roar ๐Ÿ”ฅ
  • rocketlake ๐Ÿš€ Intel Rocket Lake CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID and AVX512VPOPCNTDQ instruction set support. Blast off, periodt ๐ŸŒŒ
  • alderlake, raptorlake, meteorlake, gracemont โญ Intel Alder Lake/Raptor Lake/Meteor Lake/Gracemont CPU, with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PREFETCHW, PCLMUL, RDRND, XSAVE, XSAVEC, XSAVES, XSAVEOPT, FSGSBASE, PTWRITE, RDPID, SGX, GFNI-SSE, CLWB, MOVDIRI, MOVDIR64B, WAITPKG, ADCX, AVX, AVX2, BMI, BMI2, F16C, FMA, LZCNT, PCONFIG, PKU, VAES, VPCLMULQDQ, SERIALIZE, HRESET, KL, WIDEKL and AVX-VNNI instruction set support. Hybrid-core era, and it's so serious ๐Ÿง 
  • arrowlake ๐Ÿน Intel Arrow Lake CPU, with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PREFETCHW, PCLMUL, RDRND, XSAVE, XSAVEC, XSAVES, XSAVEOPT, FSGSBASE, PTWRITE, RDPID, SGX, GFNI-SSE, CLWB, MOVDIRI, MOVDIR64B, WAITPKG, ADCX, AVX, AVX2, BMI, BMI2, F16C, FMA, LZCNT, PCONFIG, PKU, VAES, VPCLMULQDQ, SERIALIZE, HRESET, KL, WIDEKL, AVX-VNNI, UINTR, AVXIFMA, AVXVNNIINT8, AVXNECONVERT and CMPCCXADD instruction set support. Arrow sharp, bestie ๐Ÿ’ซ
  • arrowlake-s, lunarlake ๐ŸŒ™ Intel Arrow Lake S/Lunar Lake CPU, with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PREFETCHW, PCLMUL, RDRND, XSAVE, XSAVEC, XSAVES, XSAVEOPT, FSGSBASE, PTWRITE, RDPID, SGX, GFNI-SSE, CLWB, MOVDIRI, MOVDIR64B, WAITPKG, ADCX, AVX, AVX2, BMI, BMI2, F16C, FMA, LZCNT, PCONFIG, PKU, VAES, VPCLMULQDQ, SERIALIZE, HRESET, KL, WIDEKL, AVX-VNNI, UINTR, AVXIFMA, AVXVNNIINT8, AVXNECONVERT, CMPCCXADD, AVXVNNIINT16, SHA512, SM3 and SM4 instruction set support. Two names, one slay ๐Ÿฆ‹
  • pantherlake ๐ŸŒ  Intel Panther Lake CPU, with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PREFETCHW, PCLMUL, RDRND, XSAVE, XSAVEC, XSAVES, XSAVEOPT, FSGSBASE, PTWRITE, RDPID, SGX, GFNI-SSE, CLWB, MOVDIRI, MOVDIR64B, WAITPKG, ADCX, AVX, AVX2, BMI, BMI2, F16C, FMA, LZCNT, PCONFIG, PKU, VAES, VPCLMULQDQ, SERIALIZE, HRESET, AVX-VNNI, UINTR, AVXIFMA, AVXVNNIINT8, AVXNECONVERT, CMPCCXADD, AVXVNNIINT16, SHA512, SM3, SM4 and PREFETCHI instruction set support. The ext list is literally endless โœจ
  • sapphirerapids, emeraldrapids ๐Ÿ’Ž Intel Sapphire Rapids/Emerald Rapids CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID, AVX512VPOPCNTDQ, PCONFIG, WBNOINVD, CLWB, MOVDIRI, MOVDIR64B, ENQCMD, CLDEMOTE, PTWRITE, WAITPKG, SERIALIZE, TSXLDTRK, UINTR, AMX-BF16, AMX-TILE, AMX-INT8, AVX-VNNI, AVX512-FP16 and AVX512BF16 instruction set support. Sapphire and emerald, both gorgeous ๐Ÿฅฐ
  • graniterapids ๐Ÿชจ Intel Granite Rapids CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID, AVX512VPOPCNTDQ, PCONFIG, WBNOINVD, CLWB, MOVDIRI, MOVDIR64B, ENQCMD, CLDEMOTE, PTWRITE, WAITPKG, SERIALIZE, TSXLDTRK, UINTR, AMX-BF16, AMX-TILE, AMX-INT8, AVX-VNNI, AVX512-FP16, AVX512BF16, AMX-FP16 and PREFETCHI instruction set support. Granite? More like granite-level strong ๐Ÿ’ช
  • graniterapids-d ๐ŸŒ‹ Intel Granite Rapids D CPU, with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, MOVBE, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID, AVX512VPOPCNTDQ, PCONFIG, WBNOINVD, CLWB, MOVDIRI, MOVDIR64B, ENQCMD, CLDEMOTE, PTWRITE, WAITPKG, SERIALIZE, TSXLDTRK, UINTR, AMX-BF16, AMX-TILE, AMX-INT8, AVX-VNNI, AVX512FP16, AVX512BF16, AMX-FP16, PREFETCHI and AMX-COMPLEX instruction set support. The D variant is the final boss ๐Ÿ‘‘๐Ÿงš
  • diamondrapids โ€” Intel Diamond Rapids ๐Ÿ‘‘ CPU, the full glam package: 64-bit extensions plus MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, AVX, XSAVE, PCLMUL, FSGSBASE, RDRND, F16C, AVX2, BMI, BMI2, LZCNT, FMA, HLE, RDSEED, ADCX, PREFETCHW, AES, CLFLUSHOPT, XSAVEC, XSAVES, SGX, AVX512F, AVX512VL, AVX512BW, AVX512DQ, AVX512CD, PKU, AVX512VBMI, AVX512IFMA, SHA, AVX512VNNI, GFNI, VAES, AVX512VBMI2, VPCLMULQDQ, AVX512BITALG, RDPID, AVX512VPOPCNTDQ, PCONFIG, WBNOINVD, CLWB, MOVDIRI, MOVDIR64B, ENQCMD, CLDEMOTE, PTWRITE, WAITPKG, SERIALIZE, TSXLDTRK, UINTR, AMX-BF16, AMX-TILE, AMX-INT8, AVX-VNNI, AVX512FP16, AVX512BF16, AMX-FP16, PREFETCHI, AMX-COMPLEX, AVX10.1-512, AVX-IFMA, AVX-NE-CONVERT, AVX-VNNI-INT16, AVX-VNNI-INT8, CMPccXADD, SHA512, SM3, SM4, AVX10.2-512, APX_F, AMX-AVX512, AMX-FP8, AMX-TF32, AMX-TRANSPOSE, MOVRS, AMX-MOVRS and USER_MSR instruction set support. โœจ
  • bonnell, atom โ€” Intel Bonnell ๐ŸŒธ CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3 and SSSE3 instruction set support.
  • silvermont, slm โ€” Intel Silvermont ๐Ÿฆ‹ CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, PCLMUL, PREFETCHW and RDRND instruction set support.
  • goldmont โ€” Intel Goldmont ๐Ÿ“ CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, PCLMUL, PREFETCHW, RDRND, AES, SHA, RDSEED, XSAVE, XSAVEC, XSAVES, XSAVEOPT, CLFLUSHOPT and FSGSBASE instruction set support.
  • goldmont-plus โ€” Intel Goldmont Plus ๐Ÿงš CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, PCLMUL, PREFETCHW, RDRND, AES, SHA, RDSEED, XSAVE, XSAVEC, XSAVES, XSAVEOPT, CLFLUSHOPT, FSGSBASE, PTWRITE, RDPID and SGX instruction set support.
  • tremont โ€” Intel Tremont ๐Ÿ’œ CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, CX16, SAHF, FXSR, PCLMUL, PREFETCHW, RDRND, AES, SHA, RDSEED, XSAVE, XSAVEC, XSAVES, XSAVEOPT, CLFLUSHOPT, FSGSBASE, PTWRITE, RDPID, SGX, CLWB, GFNI-SSE, MOVDIRI, MOVDIR64B, CLDEMOTE and WAITPKG instruction set support.
  • sierraforest โ€” Intel Sierra Forest ๐ŸŒˆ CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PREFETCHW, PCLMUL, RDRND, XSAVE, XSAVEC, XSAVES, XSAVEOPT, FSGSBASE, PTWRITE, RDPID, SGX, GFNI-SSE, CLWB, MOVDIRI, MOVDIR64B, CLDEMOTE, WAITPKG, ADCX, AVX, AVX2, BMI, BMI2, F16C, FMA, LZCNT, PCONFIG, PKU, VAES, VPCLMULQDQ, SERIALIZE, HRESET, KL, WIDEKL, AVX-VNNI, AVXIFMA, AVXVNNIINT8, AVXNECONVERT, CMPCCXADD, ENQCMD and UINTR instruction set support.
  • grandridge โ€” Intel Grand Ridge โญ CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PREFETCHW, PCLMUL, RDRND, XSAVE, XSAVEC, XSAVES, XSAVEOPT, FSGSBASE, PTWRITE, RDPID, SGX, GFNI-SSE, CLWB, MOVDIRI, MOVDIR64B, CLDEMOTE, WAITPKG, ADCX, AVX, AVX2, BMI, BMI2, F16C, FMA, LZCNT, PCONFIG, PKU, VAES, VPCLMULQDQ, SERIALIZE, HRESET, KL, WIDEKL, AVX-VNNI, AVXIFMA, AVXVNNIINT8, AVXNECONVERT, CMPCCXADD, ENQCMD and UINTR instruction set support.
  • clearwaterforest โ€” Intel Clearwater Forest ๐Ÿ’… CPU with 64-bit extensions, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PREFETCHW, PCLMUL, RDRND, XSAVE, XSAVEC, XSAVES, XSAVEOPT, FSGSBASE, PTWRITE, RDPID, SGX, GFNI-SSE, CLWB, MOVDIRI, MOVDIR64B, CLDEMOTE, WAITPKG, ADCX, AVX, AVX2, BMI, BMI2, F16C, FMA, LZCNT, PCONFIG, PKU, VAES, VPCLMULQDQ, SERIALIZE, HRESET, AVX-VNNI, ENQCMD, UINTR, AVXIFMA, AVXVNNIINT8, AVXNECONVERT, CMPCCXADD, AVXVNNIINT16, SHA512, SM3, SM4, USER_MSR and PREFETCHI instruction set support.
  • k6 โ€” AMD K6 ๐ŸŽ€ CPU, with MMX instruction set support.
  • k6-2, k6-3 โ€” Upgraded versions of the AMD K6 CPU, with MMX and 3DNow! instruction set support. ๐Ÿ‘
  • athlon, athlon-tbird โ€” AMD Athlon CPU with MMX, 3dNOW!, enhanced 3DNow! and SSE prefetch instructions support. ๐Ÿ”ฅ
  • athlon-4, athlon-xp, athlon-mp โ€” The glow-up of the AMD Athlon CPU, with MMX, 3DNow!, enhanced 3DNow! and full SSE instruction set support. โœจ
  • k8, opteron, athlon64, athlon-fx โ€” Processors built on the AMD K8 core with x86-64 instruction set support, including the AMD Opteron, Athlon 64 and Athlon 64 FX processors. (This supersets MMX, SSE, SSE2, 3DNow!, enhanced 3DNow! and 64-bit instruction set extensions.) ๐Ÿ’–
  • k8-sse3, opteron-sse3, athlon64-sse3 โ€” Upgraded versions of the AMD K8 cores, with SSE3 instruction set support. ๐ŸŒธ
  • amdfam10, barcelona โ€” CPUs built on AMD Family 10h cores, with x86-64 instruction set support. (This supersets MMX, SSE, SSE2, SSE3, SSE4A, 3DNow!, enhanced 3DNow!, ABM and 64-bit instruction set extensions.) ๐Ÿฆ‹
  • bdver1 โ€” CPUs built on AMD Family 15h cores, with x86-64 instruction set support. (This supersets FMA4, AVX, XOP, LWP, AES, PCLMUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM and 64-bit instruction set extensions.) ๐Ÿ”ฅ
  • bdver2 โ€” AMD Family 15h core based CPUs, with x86-64 instruction set support. (This supersets BMI, TBM, F16C, FMA, FMA4, AVX, XOP, LWP, AES, PCLMUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM and 64-bit instruction set extensions.) ๐Ÿ’…
  • bdver3 โ€” AMD Family 15h core based CPUs, with x86-64 instruction set support. (This supersets BMI, TBM, F16C, FMA, FMA4, FSGSBASE, AVX, XOP, LWP, AES, PCLMUL, CX16, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM and 64-bit instruction set extensions.) ๐ŸŒˆ
  • bdver4 โ€” AMD Family 15h core based CPUs, with x86-64 instruction set support. (This supersets BMI, BMI2, TBM, F16C, FMA, FMA4, FSGSBASE, AVX, AVX2, XOP, LWP, AES, PCLMUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM and 64-bit instruction set extensions.) ๐Ÿ‘‘
  • znver1 โ€” AMD Family 17h core based CPUs, with x86-64 instruction set support. (This supersets BMI, BMI2, F16C, FMA, FSGSBASE, AVX, AVX2, ADCX, RDSEED, MWAITX, SHA, CLZERO, AES, PCLMUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM, XSAVEC, XSAVES, CLFLUSHOPT, POPCNT, and 64-bit instruction set extensions.) ๐Ÿซถ
  • znver2 โ€” AMD Family 17h core based CPUs, with x86-64 instruction set support. (This supersets BMI, BMI2, CLWB, F16C, FMA, FSGSBASE, AVX, AVX2, ADCX, RDSEED, MWAITX, SHA, CLZERO, AES, PCLMUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM, XSAVEC, XSAVES, CLFLUSHOPT, POPCNT, RDPID, WBNOINVD, and 64-bit instruction set extensions.) ๐Ÿ’œ
  • znver3 โ€” AMD Family 19h core based CPUs, with x86-64 instruction set support. (This supersets BMI, BMI2, CLWB, F16C, FMA, FSGSBASE, AVX, AVX2, ADCX, RDSEED, MWAITX, SHA, CLZERO, AES, PCLMUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM, XSAVEC, XSAVES, CLFLUSHOPT, POPCNT, RDPID, WBNOINVD, PKU, VPCLMULQDQ, VAES, and 64-bit instruction set extensions.) ๐Ÿ”ฅ
  • znver4 โ€” AMD Family 19h core based CPUs, with x86-64 instruction set support. (This supersets BMI, BMI2, CLWB, F16C, FMA, FSGSBASE, AVX, AVX2, ADCX, RDSEED, MWAITX, SHA, CLZERO, AES, PCLMUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM, XSAVEC, XSAVES, CLFLUSHOPT, POPCNT, RDPID, WBNOINVD, PKU, VPCLMULQDQ, VAES, AVX512F, AVX512DQ, AVX512IFMA, AVX512CD, AVX512BW, AVX512VL, AVX512BF16, AVX512VBMI, AVX512VBMI2, AVX512VNNI, AVX512BITALG, AVX512VPOPCNTDQ, GFNI and 64-bit instruction set extensions.) ๐ŸŒธ
  • znver5 โ€” AMD Family 1ah core based CPUs, with x86-64 instruction set support. (This supersets BMI, BMI2, CLWB, F16C, FMA, FSGSBASE, AVX, AVX2, ADCX, RDSEED, MWAITX, SHA, CLZERO, AES, PCLMUL, CX16, MOVBE, MMX, SSE, SSE2, SSE3, SSE4A, SSSE3, SSE4.1, SSE4.2, ABM, XSAVEC, XSAVES, CLFLUSHOPT, POPCNT, RDPID, WBNOINVD, PKU, VPCLMULQDQ, VAES, AVX512F, AVX512DQ, AVX512IFMA, AVX512CD, AVX512BW, AVX512VL, AVX512BF16, AVX512VBMI, AVX512VBMI2, AVX512VNNI, AVX512BITALG, AVX512VPOPCNTDQ, GFNI, AVXVNNI, MOVDIRI, MOVDIR64B, AVX512VP2INTERSECT, PREFETCHI and 64-bit instruction set extensions.) ๐Ÿงš
  • btver1 โ€” CPUs built on AMD Family 14h cores, with x86-64 instruction set support. (This supersets MMX, SSE, SSE2, SSE3, SSSE3, SSE4A, CX16, ABM and 64-bit instruction set extensions.) ๐ŸŒท
  • btver2 โ€” CPUs built on AMD Family 16h cores, with x86-64 instruction set support. This includes MOVBE, F16C, BMI, AVX, PCLMUL, AES, SSE4.2, SSE4.1, CX16, ABM, SSE4A, SSSE3, SSE3, SSE2, SSE, MMX and 64-bit instruction set extensions. ๐Ÿ“
  • winchip-c6 โ€” IDT WinChip C6 CPU ๐Ÿงธ, handled the same way as i486, with additional MMX instruction set support.
  • winchip2 โ€” IDT WinChip 2 CPU ๐ŸŒผ, handled the same way as i486, with additional MMX and 3DNow! instruction set support.
  • c3 โ€” VIA C3 CPU with MMX and 3DNow! instruction set support. (No scheduling is implemented for this chip.) ๐Ÿ™ˆ
  • c3-2 โ€” VIA C3-2 (Nehemiah/C5XL) CPU with MMX and SSE instruction set support. (No scheduling is implemented for this chip.) ๐Ÿซง
  • c7 โ€” VIA C7 (Esther) CPU with MMX, SSE, SSE2 and SSE3 instruction set support. (No scheduling is implemented for this chip.) ๐ŸŽ€
  • samuel-2 โ€” VIA Eden Samuel 2 CPU with MMX and 3DNow! instruction set support. (No scheduling is implemented for this chip.) ๐Ÿฆ„
  • nehemiah โ€” VIA Eden Nehemiah CPU with MMX and SSE instruction set support. (No scheduling is implemented for this chip.) ๐ŸŒ™
  • esther โ€” VIA Eden Esther CPU with MMX, SSE, SSE2 and SSE3 instruction set support. (No scheduling is implemented for this chip.) ๐Ÿ’ซ
  • eden-x2 โ€” ๐Ÿ’– VIA Eden X2 CPU with x86-64, MMX, SSE, SSE2 and SSE3 instruction set support. (No scheduling is implemented for this chip.)
  • eden-x4 โ€” ๐ŸŒธ VIA Eden X4 CPU with x86-64, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX and AVX2 instruction set support. (No scheduling is implemented for this chip.)
  • nano โ€” โœจ Generic VIA Nano CPU with x86-64, MMX, SSE, SSE2, SSE3 and SSSE3 instruction set support. (No scheduling is implemented for this chip.)
  • nano-1000 โ€” ๐Ÿฆ‹ VIA Nano 1xxx CPU with x86-64, MMX, SSE, SSE2, SSE3 and SSSE3 instruction set support. (No scheduling is implemented for this chip.)
  • nano-2000 โ€” ๐ŸŽ€ VIA Nano 2xxx CPU with x86-64, MMX, SSE, SSE2, SSE3 and SSSE3 instruction set support. (No scheduling is implemented for this chip.)
  • nano-3000 โ€” ๐Ÿงš VIA Nano 3xxx CPU with x86-64, MMX, SSE, SSE2, SSE3, SSSE3 and SSE4.1 instruction set support. (No scheduling is implemented for this chip.)
  • nano-x2 โ€” ๐ŸŒˆ VIA Nano Dual Core CPU with x86-64, MMX, SSE, SSE2, SSE3, SSSE3 and SSE4.1 instruction set support. (No scheduling is implemented for this chip.)
  • nano-x4 โ€” ๐Ÿ‘‘ VIA Nano Quad Core CPU with x86-64, MMX, SSE, SSE2, SSE3, SSSE3 and SSE4.1 instruction set support. (No scheduling is implemented for this chip.)
  • lujiazui โ€” โญ ZHAOXIN lujiazui CPU with x86-64, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, POPCNT, AES, PCLMUL, RDRND, XSAVE, XSAVEOPT, FSGSBASE, CX16, ABM, BMI, BMI2, FXSR and RDSEED instruction set support. The chips do support AVX and F16C, but -march=lujiazui doesn't turn those on, for performance reasons. ๐Ÿซถ
  • yongfeng โ€” ๐Ÿ”ฅ ZHAOXIN yongfeng CPU with x86-64, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, POPCNT, AES, PCLMUL, RDRND, XSAVE, XSAVEOPT, FSGSBASE, CX16, ABM, BMI, BMI2, F16C, FXSR, RDSEED, AVX2, FMA, SHA and LZCNT instruction set support. ๐Ÿ’…
  • shijidadao โ€” ๐Ÿ’œ ZHAOXIN shijidadao CPU with x86-64, MOVBE, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, POPCNT, AES, PCLMUL, RDRND, XSAVE, XSAVEOPT, FSGSBASE, CX16, ABM, BMI, BMI2, F16C, FXSR, RDSEED, AVX2, FMA, SHA and LZCNT instruction set support. ๐Ÿ“
  • geode โ€” ๐ŸŒธ AMD Geode embedded processor with MMX and 3DNow! instruction set support.

  • -mtune=cpu-type โ€” ๐ŸŽ€ Tunes everything that can be tuned in the generated code to cpu-type, except for the ABI and the set of available instructions. Picking a specific cpu-type schedules things for that exact chip, but the compiler won't generate anything that can't run on the default machine type, unless you also use a -march=cpu-type option. For example, if GCC is configured for i686-pc-linux-gnu, then -mtune=pentium4 makes code tuned for Pentium 4 that still runs on i686 machines. ๐Ÿซถ
    • The choices for cpu-type are the same as for -march. On top of that, -mtune has 2 extra choices:
    • generic โ€” Produces code optimized for the most common IA32/AMD64/EM64T processors. If you know exactly which CPU your code will run on, use the matching -mtune or -march option instead of -mtune=generic. But if you have no clue what CPU your users will have, this is the one to pick. โœจ
      • As new processors hit the market, what this option does will change. So if you upgrade to a newer GCC, the code generated under this option will shift to match whatever processors are most common when that GCC version is released. ๐Ÿฆ‹
      • There's no -march=generic, because -march is about the instruction set the compiler can use, and there's no generic instruction set that works on every processor. -mtune, on the other hand, is about which processor (or, in this case, group of processors) the code is optimized for. ๐Ÿ’…
    • intel โ€” Produces code optimized for the most current Intel processors, which for this version of GCC are Haswell and Silvermont. If you know the CPU your code will run on, use the matching -mtune or -march option instead of -mtune=intel. But if you want your application to perform well on both Haswell and Silvermont, this is your option. ๐Ÿ’–
      • As new Intel processors come out, this option's behaviour will change. So if you upgrade to a newer GCC, the code generated will shift to reflect the most current Intel processors at the time that GCC version is released. ๐ŸŒˆ
      • There's no -march=intel, because -march is about the instruction set, and there's no common instruction set shared by all Intel processors. -mtune is about the processor (or group of processors) the code is optimized for. ๐Ÿงš
  • -mcpu=cpu-type โ€” ๐Ÿฅ€ A deprecated synonym for -mtune. Honestly, just use -mtune instead. ๐Ÿ’€
  • -mfpmath=unit โ€” ๐Ÿ”ฅ Generates floating-point arithmetic using the selected unit. The choices for unit are:
    • 387 โ€” Uses the standard 387 floating-point coprocessor found on most chips, and emulates it where there isn't one. Code built with this runs almost everywhere. Temporary results are computed in 80-bit precision instead of the precision the type specifies, so the results can differ slightly from most other chips. Check -ffloat-store for a more detailed explanation. ๐Ÿซ 
      • This is the default choice for non-Darwin x86-32 targets.
    • sse โ€” Uses the scalar floating-point instructions from the SSE instruction set. This is supported by Pentium III and newer chips, and on the AMD side by Athlon-4, Athlon XP and Athlon MP chips. The earlier version of SSE only does single-precision arithmetic, so double and extended-precision maths still go through 387. A later version, found only in Pentium 4 and AMD x86-64 chips, handles double-precision arithmetic too. ๐Ÿ‘‘
      • For the x86-32 compiler, you need -march=cpu-type, -msse or -msse2 to turn on SSE extensions and make this option work. For the x86-64 compiler, those extensions are on by default.
      • The result should be a lot faster in most cases and dodges the numerical instability problems of 387 code. But it may break existing code that expects temporaries to be 80 bits. โš ๏ธ
      • This is the default choice for the x86-64 compiler, Darwin x86-32 targets, and for x86-32 targets with the SSE2 instruction set when -ffast-math is enabled.
    • sse,387, sse+387, both โ€” Tries to use both instruction sets at once. That effectively doubles the number of available registers, and on chips with separate execution units for 387 and SSE, it doubles the execution resources too. Use this with care! It's still experimental, because the GCC register allocator doesn't model separate functional units well, which leads to unstable performance. ๐Ÿ’€
  • -masm=dialect โ€” ๐Ÿ“ Outputs assembly instructions in the selected dialect. This also decides which dialect is used for basic asm and extended asm. The supported choices (in dialect order) are att or intel. The default is att. Darwin doesn't support intel. ๐ŸŒธ
  • -mieee-fp, -mno-ieee-fp โ€” ๐Ÿฆ‹ Controls whether or not the compiler uses IEEE floating-point comparisons. These handle the case where a comparison result is unordered correctly. โœจ
  • -m80387, -mhard-float โ€” ๐Ÿ’… Generates output containing 80387 instructions for floating point.
  • -mno-80387, -msoft-float โ€” ๐Ÿงš Generates output containing library calls for floating point instead.
    • > Warning: the libraries you need are not part of GCC. Normally the facilities of the machine's usual C compiler get used, but that can't be done directly when cross-compiling. You have to sort out your own suitable library functions for cross-compilation. ๐Ÿ˜ญ
    • On machines where a function returns floating-point results in the 80387 register stack, some floating-point opcodes may still get emitted even when -msoft-float is used. ๐Ÿ™ˆ
  • -mno-fp-ret-in-387 โ€” ๐Ÿซถ Doesn't use the FPU registers for function return values.
    • The usual calling convention has functions return float and double values in an FPU register, even if there's no FPU. The idea is that the operating system should emulate the FPU.
    • -mno-fp-ret-in-387 makes those values come back in ordinary CPU registers instead. ๐ŸŒˆ
  • -mno-fancy-math-387 โ€” ๐Ÿ’œ Some 387 emulators don't support the sin, cos and sqrt instructions for the 387. Use this option to avoid generating those instructions. It gets overridden when -march says the target CPU always has an FPU, so the instruction doesn't need emulating. These instructions also aren't generated unless you use the -funsafe-math-optimizations switch too. โญ
  • -malign-double, -mno-align-double โ€” ๐ŸŽ€ Controls whether GCC aligns double, long double and long long variables on a two-word boundary or a one-word boundary. Aligning double variables on a two-word boundary makes code run a bit faster on a Pentium, at the cost of more memory. ๐Ÿ“
    • On x86-64, -malign-double is enabled by default.
    • > Warning: if you use -malign-double, structures containing the types above get aligned differently from the published application binary interface specifications for x86-32. They won't be binary compatible with structures in code compiled without that switch. ๐Ÿ’€
  • -m96bit-long-double, -m128bit-long-double โ€” ๐Ÿ”ฅ These switches control the size of the long double type. The x86-32 application binary interface specifies 96 bits, so -m96bit-long-double is the default in 32-bit mode. ๐Ÿ‘‘

Okay so, the "long double" alignment situation is actually kind of wild ๐Ÿ’…. Modern chips (Pentium and newer) really want "long double" sitting on an 8- or 16-byte boundary, but inside arrays and structures that follow the ABI, that just isn't possible ๐Ÿ˜ญ. So -m128bit-long-double lines things up on a 16-byte boundary by tacking on an extra 32-bit zero of padding ๐Ÿซถ.

On the x86-64 compiler, -m128bit-long-double is already the default, because its ABI says "long double" is aligned on a 16-byte boundary โœจ.

Quick heads up, though: neither of these options gives you any extra precision over the x87 standard of 80 bits for a "long double" ๐Ÿ™ˆ.

Warning: if you override the default for your target ABI, the size of structures and arrays holding "long double" variables changes, and the function calling convention for functions taking "long double" gets modified too ๐Ÿ’€. That means your code is not binary-compatible with code compiled without that switch, so be careful bestie ๐Ÿฅบ.
  • -mlong-double-64 โ€” makes the "long double" type equivalent to "double" ๐Ÿ“. This is the default for the 32-bit Bionic C library.
  • -mlong-double-80 โ€” one of the three switches that control the size of the "long double" type ๐ŸŽ€. The original page doesn't give it any extra detail beyond that.
  • -mlong-double-128 โ€” makes the "long double" type equivalent to "__float128" ๐ŸŒˆ. This is the default for the 64-bit Bionic C library.
Warning: same deal here. Overriding the default for your target ABI changes the size of structures and arrays containing "long double" variables, and it modifies the function calling convention for functions taking "long double". So it's not binary-compatible with code compiled without that switch ๐Ÿ˜ญ.
  • -malign-data=type โ€” controls how GCC aligns variables ๐Ÿงš. The supported values for type are compat, which uses an increased alignment value that's compatible with GCC 4.8 and earlier; abi, which uses the alignment value specified by the psABI; and cacheline, which uses an increased alignment value to match the cache line size ๐Ÿ’œ. compat is the default.
  • -mlarge-data-threshold=threshold โ€” when -mcmodel=medium or -mcmodel=large is specified, any data objects bigger than threshold get placed into large data sections ๐Ÿฆ‹. The default is 65535.
  • -mrtd โ€” switches to a different function-calling convention ๐Ÿ”ฅ. Functions that take a fixed number of arguments return with the ret num instruction, which pops their arguments on the way out, so the caller saves one instruction since it doesn't have to pop them itself โœจ.

You can make one specific function use this calling sequence with the function attribute "stdcall" ๐Ÿ’…. You can also override -mrtd for a function with the function attribute "cdecl" ๐ŸŽ€.

Warning: this calling convention is incompatible with the one normally used on Unix, so you cannot use it if you need to call libraries compiled with the Unix compiler ๐Ÿ’€.

Also, you must provide function prototypes for all functions that take variable numbers of arguments (including "printf"), otherwise incorrect code gets generated for calls to those functions ๐Ÿ˜ญ. And seriously incorrect code results if you call a function with too many arguments ๐Ÿ™ˆ. Normally, extra arguments just get harmlessly ignored, but not here, bestie.

  • -mregparm=num โ€” controls how many registers are used to pass integer arguments ๐ŸŒธ. By default, no registers are used for passing arguments, and at most 3 registers can be used. You can control this for a specific function with the function attribute "regparm" ๐Ÿซถ.
Warning: if you use this switch and num is nonzero, you must build all modules with the same value, including any libraries. That includes the system libraries and startup modules ๐Ÿ’€.
  • -msseregparm โ€” uses SSE register passing conventions for float and double arguments and return values โญ. You can control this for a specific function with the function attribute "sseregparm" ๐Ÿ’œ.
Warning: if you use this switch, you must build all modules with the same value, including any libraries, the system libraries, and startup modules too ๐Ÿ˜ญ.
  • -mvect8-ret-in-mem โ€” returns 8-byte vectors in memory instead of MMX registers ๐Ÿงš. This is the default on VxWorks, so it matches the ABI of the Sun Studio compilers up until version 12 ๐Ÿ“. Only use this if you need to stay compatible with existing code made by those older compiler versions or older versions of GCC ๐Ÿฅน.
  • -mpc32, -mpc64, -mpc80 โ€” set the 80387 floating-point precision to 32, 64, or 80 bits ๐Ÿฆ‹.
    • -mpc32 rounds the significands of floating-point results to 24 bits (single precision) ๐ŸŒธ.
    • -mpc64 rounds them to 53 bits (double precision) ๐Ÿ’–.
    • -mpc80 rounds them to 64 bits (extended double precision), and this is the default โœจ.

When one of these is used, floating-point operations in higher precisions aren't available to you unless you set the FPU control word explicitly ๐Ÿ™ˆ.

Rounding to less than the default 80 bits can speed some programs up by 2% or more ๐Ÿ”ฅ. But heads up: some math libraries assume extended-precision (80-bit) floating-point operations are on by default. Routines in those libraries could lose a bunch of accuracy, typically through so-called "catastrophic cancellation", when this option drops the precision below extended precision ๐Ÿ’€.

  • -mdaz-ftz โ€” the flush-to-zero (FTZ) and denormals-are-zero (DAZ) flags in the MXCSR register are what control floating-point calculations ๐ŸŒˆ. SSE and AVX instructions, including scalar and vector ones, could benefit from enabling the FTZ and DAZ flags when you pass -mdaz-ftz โญ. Don't set the FTZ/DAZ flags when -mno-daz-ftz or -shared is specified. And -mdaz-ftz will set the FTZ/DAZ flags even with -shared, so keep that in mind ๐Ÿ‘€.
  • -mstackrealign โ€” realigns the stack at entry ๐ŸŽ€. On x86, this generates an alternate prologue and epilogue that realigns the runtime stack when necessary ๐Ÿซถ. It's super useful for mixing legacy code that keeps 4-byte stack alignment with modern code that keeps 16-byte stack alignment for SSE compatibility ๐Ÿ’œ. See also the attribute "force_align_arg_pointer", which you can apply to individual functions ๐Ÿฆ‹.
  • -mpreferred-stack-boundary=num โ€” tries to keep the stack boundary aligned to a 2 raised to num byte boundary ๐ŸŒธ. If you don't specify -mpreferred-stack-boundary, the default is 4 (16 bytes or 128 bits) โœจ.
Warning: when generating code for the x86-64 architecture with SSE extensions disabled, -mpreferred-stack-boundary=3 can keep the stack boundary aligned to an 8-byte boundary ๐Ÿ™ˆ. But since the x86-64 ABI requires 16-byte stack alignment, this is ABI incompatible. It's only meant for controlled environments where stack space is a seriously important limitation ๐Ÿฅบ. This option leads to wrong code when functions compiled with 16-byte stack alignment (like functions from a standard library) get called with a misaligned stack. In that case, SSE instructions may lead to misaligned memory access traps ๐Ÿ’€. On top of that, variable arguments are handled incorrectly for 16-byte aligned objects (including x87 long double and __int128), which leads to wrong results ๐Ÿ˜ญ. You must build all modules with -mpreferred-stack-boundary=3, including any libraries, the system libraries, and startup modules too ๐Ÿ’….
  • -mincoming-stack-boundary=num โ€” assumes the incoming stack is aligned to a 2 raised to num byte boundary ๐Ÿงš. If you don't specify -mincoming-stack-boundary, the one specified by -mpreferred-stack-boundary gets used ๐Ÿ“.

On Pentium and Pentium Pro, "double" and "long double" values should be aligned to an 8-byte boundary (see -malign-double), or they'll suffer significant run time performance penalties ๐Ÿข. On Pentium III, the Streaming SIMD Extension (SSE) data type "__m128" may not work properly if it isn't 16-byte aligned ๐Ÿ˜ญ.

To make sure these values are aligned properly on the stack, the stack boundary has to be as aligned as whatever value stored on the stack requires ๐Ÿซถ. Also, every function has to be generated so it keeps the stack aligned ๐Ÿ’œ. So calling a function compiled with a higher preferred stack boundary from a function compiled with a lower one most likely misaligns the stack ๐Ÿ™ˆ. It's recommended that libraries that use callbacks always stick with the default setting ๐ŸŒธ.

This extra alignment does eat up extra stack space and generally increases code size ๐Ÿฅน. Code that's sensitive to stack space usage, like embedded systems and operating system kernels, may want to reduce the preferred alignment to -mpreferred-stack-boundary=2 โœจ.

  • -mmmx ๐Ÿ’– unlocks the MMX instructions
  • -msse ๐ŸŒธ unlocks SSE
  • -msse2 ๐ŸŒธ unlocks SSE2
  • -msse3 ๐ŸŒธ unlocks SSE3
  • -mssse3 ๐ŸŒธ unlocks SSSE3
  • -msse4 โœจ unlocks SSE4.1 and SSE4.2 together
  • -msse4a ๐Ÿฆ‹ unlocks SSE4A, which is controlled separately from -msse4
  • -msse4.1 โœจ unlocks SSE4.1
  • -msse4.2 โœจ unlocks SSE4.2
  • -mavx ๐Ÿงš unlocks AVX
  • -mavx2 ๐Ÿงš unlocks AVX2
  • -mavx512f ๐Ÿ‘‘ unlocks AVX512F
  • -mavx512cd ๐Ÿ‘‘ unlocks AVX512CD
  • -mavx512vl ๐Ÿ‘‘ unlocks AVX512VL
  • -mavx512bw ๐Ÿ‘‘ unlocks AVX512BW
  • -mavx512dq ๐Ÿ‘‘ unlocks AVX512DQ
  • -mavx512ifma ๐Ÿ‘‘ unlocks AVX512IFMA
  • -mavx512vbmi ๐Ÿ‘‘ unlocks AVX512VBMI
  • -msha ๐Ÿ” unlocks SHA
  • -maes ๐Ÿ” unlocks AES
  • -mpclmul ๐Ÿ” unlocks PCLMUL
  • -mclflushopt ๐Ÿงน unlocks CLFLUSHOPT
  • -mclwb ๐Ÿงน unlocks CLWB
  • -mfsgsbase ๐ŸŽ€ unlocks FSGSBASE
  • -mptwrite ๐ŸŽ€ unlocks PTWRITE
  • -mrdrnd ๐ŸŽฒ unlocks RDRND
  • -mf16c ๐ŸŽ€ unlocks F16C
  • -mfma ๐Ÿ”ฅ unlocks FMA
  • -mpconfig ๐ŸŽ€ unlocks PCONFIG
  • -mwbnoinvd ๐ŸŽ€ unlocks WBNOINVD
  • -mfma4 ๐Ÿ”ฅ unlocks FMA4
  • -mprfchw ๐ŸŽ€ unlocks PREFETCHW
  • -mrdpid ๐ŸŽ€ unlocks RDPID
  • -mrdseed ๐ŸŽฒ unlocks RDSEED
  • -msgx ๐Ÿ” unlocks SGX
  • -mxop ๐ŸŽ€ unlocks XOP
  • -mlwp ๐ŸŽ€ unlocks LWP
  • -m3dnow ๐ŸŽ€ unlocks 3DNow!
  • -m3dnowa ๐ŸŽ€ unlocks enhanced 3DNow!
  • -mpopcnt ๐ŸŽ€ unlocks POPCNT
  • -mabm ๐ŸŽ€ unlocks ABM
  • -madx ๐ŸŽ€ unlocks ADX
  • -mbmi ๐ŸŽ€ unlocks BMI
  • -mbmi2 ๐ŸŽ€ unlocks BMI2
  • -mlzcnt ๐ŸŽ€ unlocks LZCNT
  • -mfxsr ๐ŸŽ€ unlocks FXSR
  • -mxsave ๐ŸŽ€ unlocks XSAVE
  • -mxsaveopt ๐ŸŽ€ unlocks XSAVEOPT
  • -mxsavec ๐ŸŽ€ unlocks XSAVEC
  • -mxsaves ๐ŸŽ€ unlocks XSAVES
  • -mrtm ๐ŸŽ€ unlocks RTM
  • -mhle ๐ŸŽ€ unlocks HLE
  • -mtbm ๐ŸŽ€ unlocks TBM
  • -mmwaitx ๐ŸŽ€ unlocks MWAITX
  • -mclzero ๐ŸŽ€ unlocks CLZERO
  • -mpku ๐ŸŽ€ unlocks PKU
  • -mavx512vbmi2 ๐Ÿ‘‘ unlocks AVX512VBMI2
  • -mavx512bf16 ๐Ÿ‘‘ unlocks AVX512BF16
  • -mavx512fp16 ๐Ÿ‘‘ unlocks AVX512-FP16
  • -mgfni ๐ŸŽ€ unlocks GFNI
  • -mvaes ๐Ÿ” unlocks VAES
  • -mwaitpkg ๐ŸŽ€ unlocks WAITPKG
  • -mvpclmulqdq ๐Ÿ” unlocks VPCLMULQDQ
  • -mavx512bitalg ๐Ÿ‘‘ unlocks AVX512BITALG
  • -mmovdiri ๐ŸŽ€ unlocks MOVDIRI
  • -mmovdir64b ๐ŸŽ€ unlocks MOVDIR64B
  • -menqcmd ๐ŸŽ€ unlocks ENQCMD
  • -muintr ๐ŸŽ€ unlocks UINTR
  • -mtsxldtrk ๐ŸŽ€ unlocks TSXLDTRK
  • -mavx512vpopcntdq ๐Ÿ‘‘ unlocks AVX512VPOPCNTDQ
  • -mavx512vp2intersect ๐Ÿ‘‘ unlocks AVX512VP2INTERSECT
  • -mavx512vnni ๐Ÿ‘‘ unlocks AVX512VNNI
  • -mavxvnni ๐Ÿงš unlocks AVXVNNI
  • -mcldemote ๐Ÿงน unlocks CLDEMOTE
  • -mserialize ๐ŸŽ€ unlocks SERIALIZE
  • -mamx-tile ๐Ÿ’œ unlocks AMXTILE
  • -mamx-int8 ๐Ÿ’œ unlocks AMXINT8
  • -mamx-bf16 ๐Ÿ’œ unlocks AMXBF16
  • -mhreset ๐ŸŽ€ unlocks HRESET
  • -mkl ๐ŸŽ€ unlocks KL
  • -mwidekl ๐ŸŽ€ unlocks WIDEKL
  • -mavxifma ๐Ÿงš unlocks AVXIFMA
  • -mavxvnniint8 ๐Ÿงš unlocks AVXVNNIINT8
  • -mavxneconvert ๐Ÿงš unlocks AVXNECONVERT
  • -mcmpccxadd ๐ŸŽ€ unlocks CMPCCXADD
  • -mamx-fp16 ๐Ÿ’œ unlocks AMX-FP16
  • -mprefetchi ๐ŸŽ€ unlocks PREFETCHI
  • -mraoint ๐ŸŽ€ unlocks RAOINT
  • -mamx-complex ๐Ÿ’œ unlocks AMX-COMPLEX
  • -mavxvnniint16 ๐Ÿงš unlocks AVXVNNIINT16
  • -msm3 ๐Ÿ” unlocks SM3
  • -msha512 ๐Ÿ” unlocks SHA512
  • -msm4 ๐Ÿ” unlocks SM4
  • -mapxf ๐Ÿ‘‘ unlocks APX_F
  • -musermsr ๐ŸŽ€ unlocks USER_MSR
  • -mavx10.1 ๐Ÿ‘‘ unlocks AVX10.1
  • -mavx10.1-256 ๐Ÿ‘‘ unlocks AVX10.1 at the 256-bit width
  • -mavx10.1-512 ๐Ÿ‘‘ unlocks AVX10.1 at the 512-bit width
  • -mavx10.2 ๐Ÿ‘‘ unlocks AVX10.2
  • -mamx-avx512 ๐Ÿ’œ unlocks AMX-AVX512
  • -mamx-tf32 ๐Ÿ’œ unlocks AMX-TF32
  • -mamx-transpose ๐Ÿ’œ unlocks AMX-TRANSPOSE
  • -mamx-fp8 ๐Ÿ’œ unlocks AMX-FP8
  • -mmovrs ๐ŸŽ€ unlocks MOVRS
  • -mamx-movrs ๐Ÿ’œ unlocks AMX-MOVRS

These switches are basically your permission slip to use all these fancy instruction sets: MMX, SSE, AVX512CD, AVX512VL, AVX512BW, AVX512DQ, AVX512IFMA, AVX512VBMI, SHA, AES, PCLMUL, CLFLUSHOPT, CLWB, FSGSBASE, PTWRITE, RDRND, F16C, FMA, PCONFIG, WBNOINVD, FMA4, PREFETCHW, RDPID, RDSEED, SGX, XOP, LWP, 3DNow!, enhanced 3DNow!, POPCNT, ABM, ADX, BMI, BMI2, LZCNT, FXSR, XSAVE, XSAVEOPT, XSAVEC, XSAVES, RTM, HLE, TBM, MWAITX, CLZERO, PKU, AVX512VBMI2, GFNI, VAES, WAITPKG, VPCLMULQDQ, AVX512BITALG, MOVDIRI, MOVDIR64B, AVX512BF16, ENQCMD, AVX512VPOPCNTDQ, AVX512VNNI, SERIALIZE, UINTR, HRESET, AMXTILE, AMXINT8, AMXBF16, KL, WIDEKL, AVXVNNI, AVX512-FP16, AVXIFMA, AVXVNNIINT8, AVXNECONVERT, CMPCCXADD, AMX-FP16, PREFETCHI, RAOINT, AMX-COMPLEX, AVXVNNIINT16, SM3, SHA512, SM4, APX_F, USER_MSR, AVX10.1, AVX10.2, AMX-AVX512, AMX-TF32, AMX-TRANSPOSE, AMX-FP8, MOVRS, AMX-MOVRS, or CLDEMOTE extended instruction sets. Every one of them has a matching -mno- option that turns its use back off. ๐Ÿ’…

These extensions are also available as built-in functions, so check out x86 Built-in Functions to see which functions get enabled and disabled by these switches. ๐Ÿงš

Heads up: -msse4 turns on both SSE4.1 and SSE4.2 support, and -mno-sse4 turns both off. Neither form touches SSE4A, which is handled on its own by -msse4a. ๐Ÿซถ

If you want SSE/SSE2 instructions generated automatically from floating-point code (instead of 387 instructions), look at -mfpmath=sse. โœจ

Fun fact that's kind of a plot twist: GCC suppresses SSEx instructions when -mavx is used. Instead, it generates new AVX instructions, or AVX equivalents for all the SSEx instructions, whenever they're needed. ๐Ÿฆ‹

These options let GCC use those extended instructions in the code it generates, even without -mfpmath=sse. Apps that do run-time CPU detection need to compile separate files for each supported architecture, using the right flags. Specifically, the file with the CPU detection code should be compiled without these options. ๐Ÿ™ˆ


  • -mdump-tune-features ๐Ÿ” makes GCC dump the names of the x86 performance tuning features along with their default settings. You can use those names with -mtune-ctrl=feature-list.
  • -mtune-ctrl=feature-list ๐ŸŽ›๏ธ gives you fine-grained control over x86 code generation features. feature-list is a comma-separated list of feature names. See also -mdump-tune-features. A feature gets turned on if its name has no ^ in front of it, and turned off if it does.
This one is really meant for GCC developers. Using it may lead to code paths that testing never covered, and it can potentially cause compiler ICEs or runtime errors. Proceed with caution, bestie. ๐Ÿ’€
  • -mno-default ๐Ÿงผ makes GCC turn off every tunable feature. See also -mtune-ctrl=feature-list and -mdump-tune-features.
  • -mcld ๐Ÿงญ tells GCC to emit a cld instruction in the prologue of functions that use string instructions. String instructions rely on the DF flag to choose between autoincrement and autodecrement mode. The ABI says DF should be cleared on function entry, but some operating systems break that rule by not clearing DF in their exception dispatchers. So the exception handler can get invoked with DF set, and then string instructions go in the wrong direction. This option can be turned on by default on 32-bit x86 targets if you configure GCC with the --enable-cld configure option. In that case, you can suppress generation of cld instructions with the -mno-cld compiler option. ๐ŸŒท
  • -mvzeroupper ๐Ÿงน tells GCC to emit a vzeroupper instruction before control flow transfers out of the function. This cuts down the AVX-to-SSE transition penalty, and it also removes unnecessary zeroupper intrinsics.
  • -mprefer-avx128 ๐Ÿฉต tells GCC to use 128-bit AVX instructions instead of 256-bit AVX instructions in the auto-vectorizer.
  • -mprefer-vector-width=opt ๐Ÿ“ tells GCC to use opt-bit vector width in instructions instead of the default for the selected platform. The choices are:
    • none ๐ŸŒซ๏ธ no extra limits on GCC beyond whatever the selected platform already defines
    • 128 ๐ŸŒธ prefers 128-bit vector width for instructions
    • 256 ๐ŸŒบ prefers 256-bit vector width for instructions
    • 512 ๐ŸŒˆ prefers 512-bit vector width for instructions
  • -mpartial-vector-fp-math ๐Ÿงฎ lets GCC generate floating-point operations that might affect the floating-point status flags on partial vectors, meaning vectors whose elements live in the low part of a 128-bit SSE register. Unless -fno-trapping-math is specified, the compiler keeps things correct by sanitizing all input operands so the unused upper part of the vector register is full of zeroes. Heads up, though: if you use built-in functions or inline assembly with partial vector arguments, NaNs, denormal values, or invalid values can leak into the upper part of the vector. That can cause performance issues when -fno-trapping-math is in effect. You can soften this by manually sanitizing the upper part of the partial vector argument register, or by using -mdaz-ftz to set the denormals-are-zero (DAZ) flag in the MXCSR register.

This option is enabled by default. โœ…

  • -mmove-max=bits ๐Ÿšš sets the maximum number of bits that can be moved from memory to memory efficiently to bits. The valid values are 128, 256, and 512.
  • -mstore-max=bits ๐Ÿ“ฆ sets the maximum number of bits that can be stored to memory efficiently to bits. The valid values are 128, 256, and 512.
  • -mnoreturn-no-callee-saved-registers ๐ŸŒ™ optimizes functions with the noreturn attribute or the _Noreturn specifier. It does this by not saving, in the function prologue, the callee-saved registers the function uses (except the BP register).
This one can mess with debugging the caller of the noreturn function, or any function further up the call stack, so it is not enabled by default. ๐Ÿ™ˆ
  • -mcx16 ๐Ÿ”’ lets GCC generate CMPXCHG16B instructions in 64-bit code to do compare-and-exchange operations on 16-byte aligned 128-bit objects. This is great for atomic updates of data structures bigger than one machine word. The compiler uses this instruction to implement __sync Builtins. For __atomic Builtins operating on 128-bit integers, though, a library call is always used.
  • -msahf ๐ŸŽˆ enables generation of SAHF instructions in 64-bit code. Early Intel Pentium 4 CPUs with Intel 64 support (before the Pentium 4 G1 step arrived in December 2005) didn't have the LAHF and SAHF instructions that AMD64 supports. These are load and store instructions, respectively, for certain status flags. In 64-bit mode, the SAHF instruction is used to optimize the fmod, drem, and remainder built-in functions. See Other Builtins for details.
  • -mmovbe ๐Ÿ”„ enables use of the movbe instruction to optimize byte swapping of four- and eight-byte entities.
  • -mshstk ๐Ÿ›ก๏ธ makes the shadow stack built-in functions from x86 Control-flow Enforcement Technology (CET) available.
  • -mcrc32 ๐Ÿงฎ enables the built-in functions __builtin_ia32_crc32qi, __builtin_ia32_crc32hi, __builtin_ia32_crc32si, and __builtin_ia32_crc32di to generate the crc32 machine instruction.
  • -mmwait โณ enables the built-in functions __builtin_ia32_monitor and __builtin_ia32_mwait to generate the monitor and mwait machine instructions.
  • -mrecip โšก enables use of RCPSS and RSQRTSS instructions (plus their vectorized variants RCPPS and RSQRTPS) with an extra Newton-Raphson step for more precision. They're used instead of DIVSS and SQRTSS (and their vectorized variants) for single-precision floating-point arguments. These instructions are only generated when -funsafe-math-optimizations is enabled together with -ffinite-math-only and -fno-trapping-math. The sequence has higher throughput than the non-reciprocal instruction, but its precision can drop by up to 2 ulp (for example, the inverse of 1.0 comes out as 0.99999994). ๐Ÿซ 

Also, GCC already implements 1.0f/sqrtf(x) using RSQRTSS (or RSQRTPS) with -ffast-math, or with the option combination above, so you don't need -mrecip for that. ๐Ÿ’…

And GCC emits that same sequence, with the extra Newton-Raphson step, for vectorized single-float division and vectorized sqrtf(x) with -ffast-math (or the option combination above). So again, -mrecip isn't needed for that either. โœจ

-mrecip=opt ๐ŸŽ€ this one controls which reciprocal estimate instructions are allowed to get used. opt is a comma-separated list, and you can slap a ! in front of any option to flip it ๐Ÿฆ‹

  • all โœจ turns on every estimate instruction
  • default ๐ŸŒธ turns on the default instructions, same as -mrecip
  • none ๐Ÿ™ˆ turns off all estimate instructions, same as -mno-recip
  • div ๐Ÿ’… turns on the approximation for scalar division
  • vec-div ๐Ÿ”ฅ turns on the approximation for vectorized division
  • sqrt ๐Ÿ’œ turns on the approximation for scalar square root
  • vec-sqrt ๐Ÿงš turns on the approximation for vectorized square root

So, for example, -mrecip=all,!sqrt turns on all of the reciprocal approximations except square root. Main character energy ๐Ÿ‘‘

-mveclibabi=type ๐ŸŒˆ this sets the ABI type for vectorizing intrinsics through an external library. The type can be svml for the Intel short vector math library, aocl for the math library (LibM) from AMD Optimizing CPU Libraries (AOCL), or acml for the end-of-life AMD core math library, which AOCL-LibM succeeded. To use this option you need both -ftree-vectorize and -funsafe-math-optimizations enabled, plus an SVML or ACML ABI-compatible library specified at link time. Not skipping that part, bestie ๐Ÿซถ

GCC currently emits calls to these names when -mveclibabi=svml is used, for the matching function types: "vmldExp2", "vmldLn2", "vmldLog102", "vmldPow2", "vmldTanh2", "vmldTan2", "vmldAtan2", "vmldAtanh2", "vmldCbrt2", "vmldSinh2", "vmldSin2", "vmldAsinh2", "vmldAsin2", "vmldCosh2", "vmldCos2", "vmldAcosh2", "vmldAcos2", "vmlsExp4", "vmlsLn4", "vmlsLog104", "vmlsPow4", "vmlsTanh4", "vmlsTan4", "vmlsAtan4", "vmlsAtanh4", "vmlsCbrt4", "vmlsSinh4", "vmlsSin4", "vmlsAsinh4", "vmlsAsin4", "vmlsCosh4", "vmlsCos4", "vmlsAcosh4" and "vmlsAcos4" ๐Ÿง

When -mveclibabi=aocl is used, it goes for these: "amd_vrs4_acosf", "amd_vrs16_acosf", "amd_vrd8_asin", "amd_vrs4_asinf", "amd_vrs8_asinf", "amd_vrs16_asinf", "amd_vrd2_atan", "amd_vrd8_atan", "amd_vrs4_atanf", "amd_vrs8_atanf", "amd_vrs16_atanf", "amd_vrd2_cos", "amd_vrd4_cos", "amd_vrd8_cos", "amd_vrs4_cosf", "amd_vrs8_cosf", "amd_vrs16_cosf", "amd_vrs4_coshf", "amd_vrs8_coshf", "amd_vrd2_erf", "amd_vrd4_erf", "amd_vrd8_erf", "amd_vrs4_erff", "amd_vrs8_erff", "amd_vrs16_erff", "amd_vrd2_exp", "amd_vrd4_exp", "amd_vrd8_exp", "amd_vrs4_expf", "amd_vrs8_expf", "amd_vrs16_expf", "amd_vrd2_exp10", "amd_vrs4_exp10f", "amd_vrd2_exp2", "amd_vrd4_exp2", "amd_vrd8_exp2", "amd_vrs4_exp2f", "amd_vrs8_exp2f", "amd_vrs16_exp2f", "amd_vrs4_expm1f", "amd_vrd2_log", "amd_vrd4_log", "amd_vrd8_log", "amd_vrs4_logf", "amd_vrs8_logf", "amd_vrs16_logf", "amd_vrd2_log10", "amd_vrs4_log10f", "amd_vrs8_log10f", "amd_vrs16_log10f", "amd_vrd2_log1p", "amd_vrs4_log1pf", "amd_vrd2_log2", "amd_vrd4_log2", "amd_vrd8_log2", "amd_vrs4_log2f", "amd_vrs8_log2f", "amd_vrs16_log2f", "amd_vrd2_pow", "amd_vrd4_pow", "amd_vrd8_pow", "amd_vrs4_powf", "amd_vrs8_powf", "amd_vrs16_powf", "amd_vrd2_sin", "amd_vrd4_sin", "amd_vrd8_sin", "amd_vrs4_sinf", "amd_vrs8_sinf", "amd_vrs16_sinf", "amd_vrd2_tan", "amd_vrd4_tan", "amd_vrd8_tan", "amd_vrs16_tanf", "amd_vrs4_tanhf", "amd_vrs8_tanhf" and "amd_vrs16_tanhf" ๐Ÿ’–

And when -mveclibabi=acml is used, you get these for the matching function types: "__vrd2_sin", "__vrd2_cos", "__vrd2_exp", "__vrd2_log", "__vrd2_log2", "__vrd2_log10", "__vrs4_sinf", "__vrs4_cosf", "__vrs4_expf", "__vrs4_logf", "__vrs4_log2f", "__vrs4_log10f" and "__vrs4_powf" ๐Ÿฆ‹

-mabi=name โœจ this generates code for the calling convention you pick. The valid values are sysv, which is the ABI used on GNU/Linux and other systems, and ms, which is the Microsoft ABI. The default is the Microsoft ABI when you're targeting Microsoft Windows, and the SysV ABI everywhere else. You can override this for specific functions with the function attributes "ms_abi" and "sysv_abi" ๐Ÿซถ

-mforce-indirect-call ๐Ÿ”ฅ forces every function call to be indirect. This is handy with Intel Processor Trace, since it gives more precise timing info for function calls ๐Ÿงš

-mmanual-endbr ๐Ÿ’œ only inserts the ENDBR instruction at function entry when you ask for it through the "cf_check" function attribute. It's useful alongside -fcf-protection=branch, letting you control ENDBR insertion at the function entry ๐ŸŒˆ

-mcet-switch ๐Ÿ™ˆ by default, CET instrumentation is off for switch statements that use a jump table, and indirect branch track is disabled. Jump tables live in read-only memory, so this doesn't directly cost you any hardening. But if the jump table index is attacker-controlled, CET might not constrain the indirect jump.

This option turns on CET instrumentation so indirect branch track is enabled for switch statements with jump tables, which leads to the jump targets being reachable via any indirect jumps. ๐Ÿ’€

-mcall-ms2sysv-xlogues ๐ŸŒธ because 64-bit ABIs differ, any Microsoft ABI function that calls a System V ABI function has to treat RSI, RDI and XMM6-15 as clobbered. By default, the code that saves and restores these registers is emitted inline, which makes the prologues and epilogues pretty lengthy. Using -mcall-ms2sysv-xlogues makes the prologues and epilogues use stubs in the static portion of libgcc to do those saves and restores instead. That shrinks function size, at the cost of a few extra instructions ๐Ÿ“

-mtls-dialect=type ๐Ÿ’… generates code to access thread-local storage using the gnu or gnu2 conventions. gnu is the conservative default. gnu2 is more efficient, but it may add compile- and run-time requirements that can't be satisfied on all systems ๐Ÿ‘‘

-mpush-args -mno-push-args ๐ŸŽ€ these use PUSH operations to store outgoing parameters. That's shorter and usually just as fast as the SUB/MOV method, and it's enabled by default. In some cases, turning it off may improve performance thanks to better scheduling and fewer dependencies โœจ

-maccumulate-outgoing-args ๐ŸŒˆ when enabled, the maximum amount of space needed for outgoing arguments is computed in the function prologue. This is faster on most modern CPUs thanks to reduced dependencies, better scheduling and less stack usage when the preferred stack boundary isn't equal to 2. The catch is a notable jump in code size ๐Ÿ™ˆ This switch implies -mno-push-args.

-mthreads ๐Ÿ’– supports thread-safe exception handling on MinGW. Programs that rely on thread-safe exception handling must compile and link all their code with -mthreads. When compiling, -mthreads defines -D_MT. When linking, it pulls in the special thread helper library -lmingwthrd, which cleans up per-thread exception-handling data ๐Ÿงš

-mms-bitfields -mno-ms-bitfields ๐Ÿฆ‹ these enable or disable bit-field layout that's compatible with the native Microsoft Windows compiler ๐Ÿ’…

If "packed" is used on a structure, or if bit-fields are used, the Microsoft ABI might lay out the structure differently than GCC normally does. Especially when moving packed data between functions compiled with GCC and the native Microsoft compiler, either through a function call or as data in a file, you might need to access either format ๐Ÿ˜ญ

This option is on by default for Microsoft Windows targets. You can also control it locally with variable or type attributes. For more info, see x86 Variable Attributes and x86 Type Attributes ๐ŸŒธ

The Microsoft structure layout algorithm is pretty simple, except for the bit-field packing. Whether a bit-field can straddle a storage-unit boundary, and the padding and alignment of structure members, come down to these rules:

  1. Structure members are stored sequentially, in the order they're declared. The first member has the lowest memory address and the last member has the highest.
  2. Every data object has an alignment requirement. For all data except structures, unions, and arrays, the alignment requirement is either the size of the object or the current packing size (set with either the "aligned" attribute or the "pack" pragma), whichever is less. For structures, unions, and arrays, the alignment requirement is the largest alignment requirement of any of its members. Every object gets an offset so that:
offset % alignment_requirement == 0
  1. Adjacent bit-fields get packed into the same 1-, 2-, or 4-byte allocation unit, as long as the integral types are the same size and the next bit-field fits into the current allocation unit without crossing the boundary imposed by the common alignment requirements of the bit-fields. ๐Ÿง

MSVC interprets zero-length bit-fields in the following ways ๐Ÿ”ฅ

  1. If a zero-length bit-field is inserted between two bit-fields that would normally be coalesced, the bit-fields are not coalesced.

For example:

struct
 {
   unsigned long bf_1 : 12;
   unsigned long : 0;
   unsigned long bf_2 : 12;
 } t1;

The size of "t1" is 8 bytes with the zero-length bit-field. If the zero-length bit-field were removed, "t1"'s size would be 4 bytes. ๐Ÿ’€

  1. If a zero-length bit-field is inserted after a bit-field, "foo", and the alignment of the zero-length bit-field is greater than that of the member that follows it, "bar", then "bar" is aligned as the type of the zero-length bit-field.

For example:

struct
 {
   char foo : 4;
   short : 0;
   char bar;
 } t2;

struct
 {
   char foo : 4;
   short : 0;
   double bar;
 } t3;

For "t2", "bar" is placed at offset 2, rather than offset 1. So the size of "t2" is 4. For "t3", the zero-length bit-field doesn't affect the alignment of "bar" or, as a result, the size of the structure. ๐Ÿซถ

Taking this into account, it is important to note the following: โœจ

Okay so bestie, the zero-length bit-field stuff is lowkey wild ๐Ÿ’€ Here's how it goes:

  1. Zero-length bit-fields that follow a normal bit-field can change the alignment of the whole struct, depending on their type ๐ŸŽ€ For example, t2 is 4 bytes, because its zero-length bit-field comes right after a normal bit-field and is of type short โœจ
  2. Even when a zero-length bit-field doesn't follow a normal bit-field, it can still mess with the struct's alignment ๐Ÿ™ˆ
struct
 {
   char foo : 6;
   long : 0;
 } t4;

Here, t4 takes up 4 bytes ๐Ÿ’–

  1. Zero-length bit-fields that come after non-bit-field members just get ignored ๐Ÿฆ‹
struct
 {
   char foo;
   long : 0;
   char bar;
 } t5;

Here, t5 takes up 2 bytes ๐ŸŒธ

  • -mno-align-stringops โ€” Don't align the destination of inlined string operations ๐Ÿ’… This makes the code smaller and can speed things up when the destination is already aligned but GCC doesn't realize it ๐Ÿซถ
  • -minline-all-stringops โ€” By default, GCC only inlines string operations when the destination is known to be aligned to at least a 4-byte boundary ๐Ÿ™„ This option inlines more, which makes the code bigger, but it might help code that leans on fast memcpy and memset for short lengths ๐Ÿ”ฅ It also turns on inline expansion of strlen for all pointer alignments โœจ
  • -minline-stringops-dynamically โ€” For string operations with an unknown size, use run-time checks ๐Ÿงš Small blocks get inline code and big blocks get a library call ๐Ÿ’œ
  • -mstringop-strategy=alg โ€” Overrides the internal heuristic that picks the algorithm used to inline string operations ๐Ÿ‘‘ The allowed values for alg are:
    • rep_byte, rep_4byte, rep_8byte โ€” Expand using the i386 rep prefix of the specified size ๐ŸŒˆ
    • byte_loop, loop, unrolled_loop โ€” Expand into an inline loop ๐Ÿ“
    • libcall โ€” Always use a library call ๐Ÿ“ž
  • -mmemcpy-strategy=strategy โ€” Overrides the heuristic that decides whether __builtin_memcpy gets inlined and which inline algorithm to use when the expected size of the copy is known ๐ŸŽ€ strategy is a comma-separated list of alg:max_size:dest_align triplets โœจ alg is one of the values from -mstringop-strategy, and max_size is the biggest byte size that alg is allowed to handle ๐Ÿ’– The last triplet has to use a max_size of -1 ๐Ÿ™ˆ The max_size values in the list must go in increasing order ๐Ÿฆ‹ The minimum byte size for alg is 0 for the first triplet, and max_size + 1 of the range before it for every other triplet ๐ŸŒธ
  • -mmemset-strategy=strategy โ€” Pretty much the same as -mmemcpy-strategy=, except it controls __builtin_memset expansion instead ๐Ÿ’…
  • -momit-leaf-frame-pointer โ€” Don't keep the frame pointer in a register for leaf functions ๐Ÿซถ This skips the instructions that save, set up, and restore frame pointers, and frees up an extra register in leaf functions ๐Ÿ”ฅ The option -fomit-leaf-frame-pointer also drops the frame pointer for leaf functions, but that might make debugging harder ๐Ÿ’€
  • -mtls-direct-seg-refs, -mno-tls-direct-seg-refs โ€” Controls whether TLS variables can be accessed with offsets from the TLS segment register (%gs for 32-bit, %fs for 64-bit), or whether the thread base pointer has to be added ๐Ÿ‘‘ Whether that's valid depends on the operating system, and on whether it maps the segment so it covers the whole TLS area ๐ŸŒˆ

For systems that use the GNU C Library, the default is on โœจ

  • -msse2avx, -mno-sse2avx โ€” Tells the assembler to encode SSE instructions with the VEX prefix ๐Ÿฆ‹ The option -mavx turns this on by default ๐Ÿ’œ
  • -mfentry, -mno-fentry โ€” If profiling is active (-pg), put the profiling counter call before the prologue ๐Ÿงš Note: on x86, the attribute ms_hook_prologue isn't possible at the moment for -mfentry and -pg ๐Ÿ™ˆ
  • -mrecord-mcount, -mno-record-mcount โ€” If profiling is active (-pg), generate a __mcount_loc section that holds pointers to each profiling call ๐ŸŽ€ This is super useful for automatically patching out calls ๐Ÿซถ
  • -mnop-mcount, -mno-nop-mcount โ€” If profiling is active (-pg), generate the calls to the profiling functions as NOPs ๐Ÿ’… This is handy when they should be patched in later on, dynamically ๐Ÿ”ฅ It's probably only useful together with -mrecord-mcount โœจ
  • -minstrument-return=type โ€” Instruments function exit in -pg -mfentry instrumented functions with a call to the specified function ๐Ÿ’– It only instruments true returns that end with ret, not sibling calls that end with a jump ๐Ÿฆ‹ Valid types are none to not instrument, call to generate a call to __return__, or nop5 to generate a 5-byte nop ๐ŸŒธ
  • -mrecord-return, -mno-record-return โ€” Generates a __return_loc section pointing to all of the return instrumentation code ๐Ÿงš
  • -mfentry-name=name โ€” Sets the name of the __fentry__ symbol that gets called at function entry for -pg -mfentry functions ๐Ÿ’œ
  • -mfentry-section=name โ€” Sets the name of the section where -mrecord-mcount calls get recorded (the default is __mcount_loc) ๐Ÿ‘‘
  • -mskip-rax-setup, -mno-skip-rax-setup โ€” When generating code for the x86-64 architecture with SSE extensions disabled, -mskip-rax-setup can skip setting up the RAX register when no variable arguments get passed in vector registers ๐ŸŒˆ
โš ๏ธ Warning: RAX is used to avoid saving vector registers on the stack needlessly when passing variable arguments ๐Ÿ˜ญ So if you use this option, callees may waste some stack space, misbehave, or jump to a random location ๐Ÿ’€ GCC 4.4 or newer doesn't have those issues, no matter what the RAX register value is ๐Ÿซถ
  • -m8bit-idiv, -mno-8bit-idiv โ€” On some processors, like Intel Atom, 8-bit unsigned integer divide is way faster than 32-bit/64-bit integer divide ๐Ÿ”ฅ This option generates a run-time check ๐Ÿ’… If both the dividend and the divisor are between 0 and 255, it uses the 8-bit unsigned divide instead of the 32-bit/64-bit one โœจ
  • -mavx256-split-unaligned-load, -mavx256-split-unaligned-store โ€” Splits 32-byte AVX unaligned loads and stores ๐Ÿฆ‹
  • -mstack-protector-guard=guard, -mstack-protector-guard-reg=reg, -mstack-protector-guard-offset=offset, -mstack-protector-guard-symbol=symbol โ€” Generates stack protection code using a canary at guard ๐Ÿ›ก๏ธ The supported locations are global for a global canary, or tls for a per-thread canary in the TLS block (the default) ๐Ÿ’– This option only does something when -fstack-protector or -fstack-protector-all is specified ๐ŸŒธ

With the tls choice, the options -mstack-protector-guard-reg=reg and -mstack-protector-guard-offset=offset also specify which segment register (%fs or %gs) to use as the base register for reading the canary, and what offset from that base register to use ๐ŸŽ€ The defaults for those come from the relevant ABI โœจ

-mstack-protector-guard-symbol=symbol overrides the offset with a symbol reference to a canary in the TLS block ๐Ÿ‘‘

  • -mgeneral-regs-only โ€” Generate code that only uses the general-purpose registers ๐Ÿ’œ This stops the compiler from using floating-point, vector, mask, and bound registers ๐Ÿซถ
  • -mrelax-cmpxchg-loop โ€” When emitting a compare-and-swap loop for __sync builtins and __atomic builtins that don't have a native instruction, this optimizes for the highly contended case ๐Ÿ”ฅ It issues an atomic load before the CMPXCHG instruction, and uses the PAUSE instruction to save CPU power when restarting the loop ๐Ÿ’…
  • -mindirect-branch=choice โ€” Converts indirect calls and jumps according to choice ๐Ÿฆ‹ The default is keep, which leaves indirect calls and jumps unmodified ๐ŸŒธ thunk converts them into call and return thunks ๐Ÿงš thunk-inline converts them into inlined call and return thunks ๐Ÿ’œ thunk-extern converts them into external call and return thunks, provided in a separate object file ๐Ÿ‘‘ You can control this for a specific function with the function attribute "indirect_branch" ๐ŸŒˆ
โš ๏ธ -mcmodel=large is incompatible with -mindirect-branch=thunk and -mindirect-branch=thunk-extern, since the thunk function may not be reachable in the large code model ๐Ÿ˜ญ

-mindirect-branch=thunk-extern is compatible with -fcf-protection=branch, since the external thunk can be made to enable control-flow check โœจ

  • -mfunction-return=choice โ€” Converts function returns according to choice ๐Ÿซถ The default is keep, which leaves function returns unmodified ๐Ÿ’… thunk converts function returns to call and return thunks ๐Ÿ”ฅ thunk-inline converts them to inlined call and return thunks ๐ŸŽ€ thunk-extern converts them to external call and return thunks, provided in a separate object file ๐Ÿ’– You can control this for a specific function with the function attribute "function_return" ๐ŸŒธ

-mindirect-return=thunk-extern is compatible with -fcf-protection=branch, since the external thunk can be made to enable control-flow check ๐Ÿฆ‹

โš ๏ธ -mcmodel=large is incompatible with -mfunction-return=thunk and -mfunction-return=thunk-extern, since the thunk function may not be reachable in the large code model ๐Ÿ˜ญ
  • -mindirect-branch-register โ€” Forces indirect calls and jumps to go via a register ๐Ÿงš
  • -mharden-sls=choice โ€” Generates code to mitigate straight line speculation (SLS) according to choice ๐Ÿ›ก๏ธ The default is none, which disables all SLS hardening ๐Ÿ’œ return enables SLS hardening for function returns ๐Ÿ‘‘ indirect-jmp enables it for indirect jumps ๐ŸŒˆ all enables all SLS hardening โœจ
  • -mindirect-branch-cs-prefix โ€” Adds a CS prefix to calls and jmps to an indirect thunk, with the branch target in the r8-r15 registers ๐Ÿฆ‹ This makes the call and jmp instructions 6 bytes long, so they can be swapped at run-time for lfence; call *%r8-r15 or lfence; jmp *%r8-r15 ๐Ÿ’–
  • -mapx-inline-asm-use-gpr32 โ€” For inline asm support with APX, the EGPR feature is disabled by default so you don't get a sneaky illegal instruction from EGPR ๐Ÿ™ˆ To turn on EGPR usage in inline asm, use this new compiler option, but you need to make sure the instruction actually supports EGPR ๐Ÿซถ

โœจ x86 Options (continued) โœจ

okay so these -m switches work on top of the ones above, but only on x86-64 processors in 64-bit environments ๐Ÿฆ‹

  • -mevex512, -mno-evex512 โ€” okay this one's about 512-bit vectors ๐Ÿ’– -mevex512 turns them on and -mno-evex512 turns them off. It's on by default if AVX512F is enabled, so you usually don't have to think about it ๐Ÿงš
  • -m32, -m64, -mx32, -m16, -miamcu โ€” these pick your environment ๐ŸŒธ
    • -m32 makes int, long, and pointer types 32 bits and generates code that runs in 32-bit mode ๐Ÿ’…
    • -m64 makes int 32 bits but long and pointers 64 bits, and generates code for the x86-64 architecture. bestie, on Darwin, only -m64 also turns off -fno-pic and -mdynamic-no-pic ๐Ÿ‘‘
    • -mx32 makes int, long, and pointers 32 bits, but still generates x86-64 code ๐ŸŽ€
    • -m16 is basically -m32 with one extra twist: it outputs the .code16gcc assembly directive at the start of the assembly output, so the binary can run in 16-bit mode ๐Ÿ™ˆ
    • -miamcu generates code that follows the Intel MCU psABI. it requires -m32 to be turned on, so don't skip that part ๐Ÿ˜ญ
  • -mno-red-zone โ€” the red zone is this 128-byte area just past the stack pointer that's mandated by the x86-64 ABI. signal or interrupt handlers won't touch it, so you can stash temporary data there without adjusting the stack pointer โœจ this flag turns that red zone off ๐Ÿ“
  • -mcmodel=small โ€” the program and its symbols must be linked in the lower 2 GB of the address space. pointers are 64 bits, and programs can be statically or dynamically linked. this is the default code model, so it's the one you get if you don't pick anything ๐Ÿ’œ
  • -mcmodel=kernel โ€” generates code for the kernel code model. the kernel runs in the negative 2 GB of the address space, and this model has to be used for Linux kernel code ๐Ÿ”ฅ
  • -mcmodel=medium โ€” the program is linked in the lower 2 GB of the address space, and small symbols get placed there too. symbols bigger than -mlarge-data-threshold go into large data or BSS sections and can live above 2GB. programs can be statically or dynamically linked ๐ŸŒˆ
  • -mcmodel=large โ€” makes no assumptions at all about addresses and section sizes. very chill, very flexible ๐Ÿฆ‹
  • -maddress-mode=long โ€” generates code for long address mode. this is only supported for 64-bit and x32 environments, and it's the default address mode for 64-bit environments โญ
  • -maddress-mode=short โ€” generates code for short address mode. this is only supported for 32-bit and x32 environments, and it's the default address mode for 32-bit and x32 environments too ๐Ÿงš
  • -mneeded, -mno-needed โ€” -mneeded emits the GNU_PROPERTY_X86_ISA_1_NEEDED GNU property for Linux targets. it's how the binary says which micro-architecture ISA level it needs to run ๐Ÿซถ
  • -mno-direct-extern-access โ€” without -fpic or -fPIC, this always uses the GOT pointer to reach external symbols. with -fpic or -fPIC, access to protected symbols gets treated like local symbols. the default is -mdirect-extern-access ๐Ÿ’…
โš ๏ธ shared libraries compiled with -mno-direct-extern-access and executables compiled with -mdirect-extern-access may not be binary compatible if protected symbols are used in both the shared libraries and the executable. be careful with that one, bestie ๐Ÿ˜ญ
  • -munroll-only-small-loops โ€” this is conservative small loop unrolling. it's enabled by default with O2, and it unrolls loops with fewer than 4 insns by 1 time. if you explicitly use -f[no-]unroll-[all-]loops, this flag gets disabled, so you don't get unrolling you didn't ask for ๐ŸŒธ
  • -mlam=choice โ€” LAM (linear-address masking) lets special bits in a pointer hold metadata. the default is none. with u48, pointer bits in positions 62:48 can be used for metadata, and with u57, pointer bits in positions 62:57 can be used ๐Ÿ”ฅ

โœจ x86 Windows Options โœจ


โœจ Xstormy16 Options โœจ

these options are defined for Xstormy16:

  • -msim โ€” picks startup files and a linker script that work with the simulator ๐Ÿงš

โœจ Xtensa Options โœจ

these options are supported for Xtensa targets ๐ŸŒˆ

  • -mconst16, -mno-const16 โ€” enables or disables the use of CONST16 instructions for loading constant values. fair warning, CONST16 isn't a standard Tensilica option right now. when it's enabled, CONST16 gets used in place of the standard L32R instructions every time. it's on by default only if L32R isn't available ๐Ÿ’–
  • -mfused-madd, -mno-fused-madd โ€” enables or disables fused multiply/add and multiply/subtract instructions in the floating-point option. this does nothing if the floating-point option isn't also enabled. turning fused instructions off forces the compiler to use separate instructions for the multiply and the add/subtract. that's useful when you need strict IEEE 754-compliant results, because fused instructions don't round the intermediate result. so they can give you more bits of precision than IEEE says you should get. turning them off also makes sure your output doesn't depend on the compiler's ability to combine multiply and add/subtract operations ๐Ÿฆ‹
  • -mserialize-volatile, -mno-serialize-volatile โ€” when enabled, GCC inserts MEMW instructions before volatile memory references so that sequential consistency is guaranteed. the default is -mserialize-volatile. use -mno-serialize-volatile if you want to leave out the MEMW instructions ๐Ÿ’…
  • -mforce-no-pic โ€” for targets like GNU/Linux, where all user-mode Xtensa code has to be position-independent code (PIC), this turns PIC off for compiling kernel code ๐Ÿ”ฅ
  • -mtext-section-literals, -mno-text-section-literals โ€” these control how literal pools are handled. the default is -mno-text-section-literals, which puts literals in a separate section of the output file. that lets the literal pool sit in data RAM/ROM, and it lets the linker merge literal pools from separate object files to kill redundant literals and shrink code size ๐ŸŒธ with -mtext-section-literals, literals get interspersed in the text section so they stay as close as possible to their references. that might be necessary for big assembly files. literals for each function go right before that function ๐Ÿงš
  • -mauto-litpools, -mno-auto-litpools โ€” also about literal pools ๐Ÿ’œ the default is -mno-auto-litpools, which puts literals in a separate section unless -mtext-section-literals is used. with -mauto-litpools, the assembler interleaves the literals into the text section itself. the compiler doesn't produce explicit .literal directives and loads literals into registers with MOVI instead of L32R, so the assembler can do relaxation and place literals wherever they're needed. this lets the assembler make several literal pools per function, which helps assemble very big functions that might not be possible with -mtext-section-literals โญ
  • -mtarget-align, -mno-target-align โ€” when enabled, GCC tells the assembler to automatically align instructions to cut branch penalties, at the cost of a bit of code density ๐ŸŽ€ the assembler tries to widen density instructions so branch targets and the instructions right after call instructions are aligned. if there aren't enough safe density instructions before a target to align it, no widening happens. the default is -mtarget-align. these options don't change how auto-aligned instructions like LOOP are treated, since the assembler always aligns those, either by widening density instructions or by inserting NOP instructions ๐Ÿ™ˆ
  • -mlongcalls, -mno-longcalls โ€” when enabled, GCC tells the assembler to turn direct calls into indirect calls, unless it can prove the target of the direct call is in range for the call instruction. this usually happens for calls to functions in other source files. specifically, the assembler turns a direct CALL into an L32R followed by a CALLX. the default is -mno-longcalls. use this in programs where the call target could be out of range ๐Ÿ’– this option lives in the assembler, not the compiler, so the assembly GCC generates still shows direct call instructions. look at the disassembled object code to see what really runs. also, the assembler uses an indirect call for every cross-file call, not just the ones that are actually out of range ๐Ÿ˜ญ
  • -mabi=name โ€” generates code for the specified ABI. the permissible values are call0 and windowed. the default ABI gets picked by the Xtensa core configuration ๐ŸŒˆ
  • -mabi=call0 โ€” function parameters get passed in registers a2 through a7, registers a12 through a15 are caller-saved, and register a15 may be used as a frame pointer. when this ABI is enabled, the C preprocessor symbol __XTENSA_CALL0_ABI__ is defined ๐Ÿฆ‹
  • -mabi=windowed โ€” function parameters get passed in registers a10 through a15. the called function rotates the register window by 8 registers on entry, so its arguments show up in registers a2 through a7. register a7 may be used as a frame pointer. the register window gets rotated 8 registers back on return. when this ABI is enabled, the C preprocessor symbol __XTENSA_WINDOWED_ABI__ is defined ๐Ÿ’…
  • -mextra-l32r-costs=n โ€” specifies an extra cost, in clock cycles, for instruction RAM/ROM access on L32R instructions. when you're optimizing for speed, this affects whether a constant gets loaded from the literal pool with L32R or synthesized from a smaller constant using a couple of arithmetic instructions ๐Ÿงš the default value is 0.

The -mstrict-align and -mno-strict-align flags are a vibe check on memory alignment ๐Ÿซถ They decide whether GCC avoids or allows memory accesses that might not be aligned on a natural object boundary, as the architecture specification describes. By default it's -mno-strict-align for cores that handle both unaligned loads and stores in hardware, and -mstrict-align for all other cores ๐ŸŒธ

๐Ÿฆ‹ zSeries Options ๐Ÿฆ‹

These are listed under

โœจ ENVIRONMENT โœจ

okay so this section is all about the environment variables that change how GCC works ๐Ÿ’– Some of them tell GCC which directories or prefixes to search when it's hunting for files, and others set up other parts of the compilation environment. Iconic behaviour, honestly ๐Ÿ’…

Also, you can name places to search with options like -B, -I and -L. Those take precedence over anything set in environment variables, and environment variables take precedence over whatever the configuration of GCC says. The hierarchy is real ๐Ÿ‘‘

LANG, LC_CTYPE, LC_MESSAGES, LC_ALL

These environment variables control how GCC uses localisation information, which lets it work with different national conventions ๐ŸŒˆ GCC checks the locale categories LC_CTYPE and LC_MESSAGES if it has been configured to do so. These categories can be set to any value your installation supports. A typical value is en_GB.UTF-8 for English in the United Kingdom, encoded in UTF-8 ๐Ÿ“

The LC_CTYPE variable specifies character classification. GCC uses it to work out the character boundaries in a string. This matters for some multibyte encodings that contain quote and escape characters which would otherwise be read as the end of a string or an escape ๐Ÿ™ˆ

The LC_MESSAGES variable specifies the language used in diagnostic messages โœจ

If LC_ALL is set, it overrides the values of LC_CTYPE and LC_MESSAGES. Otherwise, LC_CTYPE and LC_MESSAGES default to the value of the LANG environment variable. If none of these variables are set, GCC defaults to traditional C English behaviour. Main character energy ๐Ÿ‘‘

TMPDIR

If TMPDIR is set, it specifies the directory to use for temporary files ๐Ÿงš GCC uses temporary files to hold the output of one stage of compilation, which then becomes the input to the next stage. For example, the output of the preprocessor is the input to the compiler proper ๐Ÿ’œ

GCC_COMPARE_DEBUG

Setting GCC_COMPARE_DEBUG is nearly the same as passing -fcompare-debug to the compiler driver. Check the documentation for that option for more details ๐Ÿ”ฅ

GCC_EXEC_PREFIX

If GCC_EXEC_PREFIX is set, it specifies a prefix to use in the names of the subprograms that the compiler executes. No slash is added when this prefix is combined with the name of a subprogram, but you can make your prefix end with a slash if you want one ๐ŸŽ€

If GCC_EXEC_PREFIX is not set, GCC tries to figure out a sensible prefix based on the pathname it was invoked with ๐ŸŒˆ

If GCC can't find the subprogram using the specified prefix, it tries looking in the usual places for it ๐Ÿ’€

The default value of GCC_EXEC_PREFIX is prefix/lib/gcc/, where prefix is the prefix of the installed compiler. In many cases, prefix is the value of "prefix" you used when you ran the configure script ๐ŸŒธ

Other prefixes specified with -B take precedence over this prefix โœจ

This prefix is also used to find files such as crt0.o that are used for linking ๐Ÿฆ‹

The prefix also gets used in an unusual way when finding the directories to search for header files. For each standard directory whose name normally begins with /usr/local/lib/gcc (more precisely, with the value of GCC_INCLUDE_DIR), GCC tries replacing that beginning with the specified prefix to make an alternate directory name. So with -Bfoo/, GCC searches foo/bar just before it searches the standard directory /usr/local/lib/bar. If a standard directory begins with the configured prefix, then the value of prefix is replaced by GCC_EXEC_PREFIX when GCC looks for header files ๐Ÿ’…

COMPILER_PATH

The value of COMPILER_PATH is a colon-separated list of directories, much like PATH. GCC tries these directories when searching for subprograms, if it can't find them using GCC_EXEC_PREFIX ๐Ÿซถ

LIBRARY_PATH

The value of LIBRARY_PATH is a colon-separated list of directories, much like PATH ๐Ÿ“ When GCC is configured as a native compiler, it tries these directories when searching for special linker files, if it can't find them using GCC_EXEC_PREFIX. Linking with GCC also uses these directories when searching for ordinary libraries for the -l option. Directories specified with -L still come first ๐Ÿ‘‘

LANG

This variable is used to pass locale information to the compiler. One thing it affects is which character set gets used when character literals, string literals and comments are parsed in C and C++ ๐ŸŒˆ When the compiler is configured to allow multibyte characters, these values for LANG are recognised:

  • C-JIS โ€” recognise JIS characters ๐ŸŽ€
  • C-SJIS โ€” recognise SJIS characters ๐ŸŽ€
  • C-EUCJP โ€” recognise EUCJP characters ๐ŸŽ€

If LANG is not defined, or it has some other value, then the compiler uses mblen and mbtowc as defined by the default locale to recognise and translate multibyte characters ๐Ÿ™ˆ

GCC_EXTRA_DIAGNOSTIC_OUTPUT

If GCC_EXTRA_DIAGNOSTIC_OUTPUT is set to one of the following values, extra text gets printed to stderr whenever fix-it hints are emitted. -fdiagnostics-parseable-fixits and -fno-diagnostics-parseable-fixits take precedence over this environment variable โœจ

  • fixits-v1 โ€” emits parseable fix-it hints, the same as -fdiagnostics-parseable-fixits. Columns are counted in bytes, starting at byte 1 for the initial column ๐Ÿฆ‹
  • fixits-v2 โ€” the same as fixits-v1, except columns are counted as display columns, as per -fdiagnostics-column-unit=display ๐Ÿ’–

Some additional environment variables affect how the preprocessor behaves ๐Ÿงš

CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

Each of these variables holds a list of directories separated by a special character, much like PATH, and GCC looks in those directories for header files. The special character, PATH_SEPARATOR, depends on the target and is decided when GCC is built. For Microsoft Windows-based targets it's a semicolon, and for almost every other target it's a colon ๐Ÿ’œ

CPATH specifies a list of directories to be searched as if they were given with -I, but after any paths given with -I options on the command line. This environment variable gets used no matter which language is being preprocessed ๐ŸŒธ

The remaining environment variables only apply when preprocessing the particular language they're named for. Each one specifies a list of directories to be searched as if they were given with -isystem, but after any paths given with -isystem options on the command line ๐Ÿซถ

In all of these variables, an empty element tells the compiler to search its current working directory. Empty elements can appear at the beginning or end of a path. For instance, if the value of CPATH is :/special/include, that has the same effect as -I. -I/special/include ๐Ÿ“

DEPENDENCIES_OUTPUT

If this variable is set, its value specifies how to output dependencies for Make, based on the non-system header files processed by the compiler. System header files are ignored in the dependency output ๐Ÿ”ฅ

The value of DEPENDENCIES_OUTPUT can be just a file name. In that case the Make rules are written to that file, and the target name is guessed from the source file name. Or the value can have the form file target, in which case the rules are written to file using target as the target name ๐Ÿ‘‘

In other words, this environment variable is equivalent to combining the options -MM and -MF, with an optional -MT switch too ๐Ÿ’…

SUNPRO_DEPENDENCIES

This variable is the same as DEPENDENCIES_OUTPUT (see above), except system header files are not ignored, so it implies -M rather than -MM. However, the dependence on the main input file is omitted ๐ŸŒˆ

SOURCE_DATE_EPOCH

If this variable is set, its value specifies a UNIX timestamp to use in place of the current date and time in the "__DATE__" and "__TIME__" macros, so that the embedded timestamps become reproducible ๐Ÿงš

The value of SOURCE_DATE_EPOCH must be a UNIX timestamp, defined as the number of seconds (excluding leap seconds) since 01 Jan 1970 00:00:00, represented in ASCII. It's identical to the output of date +%s on GNU/Linux and other systems that support the %s extension in the date command ๐Ÿฆ‹

The value should be a known timestamp, such as the last modification time of the source or package, and it should be set by the build process ๐Ÿ’–

๐ŸŒธ BUGS ๐ŸŒธ

For instructions on reporting bugs, see <file:///usr/share/doc/gcc-15/README.Bugs> ๐Ÿž

๐Ÿฆ‹ FOOTNOTES ๐Ÿฆ‹

  1. On some systems, gcc -shared needs to build supplementary stub code for constructors to work. On multi-libbed systems, gcc -shared must select the correct support libraries to link against. Failing to supply the correct flags may lead to subtle defects. Supplying them in cases where they are not necessary is innocuous. -shared suppresses the addition of startup code that alters the floating-point environment, as done with -ffast-math, -Ofast or -funsafe-math-optimizations on some targets. ๐Ÿ™ˆ

๐Ÿ’– SEE ALSO ๐Ÿ’–

gpl(7), gfdl(7), fsf-funding(7), cpp(1), gcov(1), as(1), ld(1), gdb(1) and the Info entries for gcc, cpp, as, ld, binutils and gdb ๐Ÿ“š

๐Ÿงš AUTHOR ๐Ÿงš

See the Info entry for gcc, or <https://gcc.gnu.org/onlinedocs/gcc/Contributors.html>, for contributors to GCC ๐Ÿซถ

okay bestie, the legal bit. don't skip it ๐Ÿ™ˆ

Copyright (c) 1988-2025 Free Software Foundation, Inc. ๐Ÿ’– The FSF owns the copyright, and it's been keeping this manual glowing since 1988 ๐ŸŒˆ

You're allowed to copy, distribute, and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation ๐Ÿฆ‹ The two Invariant Sections are "GNU General Public License" and "Funding Free Software" ๐Ÿซถ You also get Front-Cover Texts and Back-Cover Texts, and those are spelled out below. A copy of the license lives in the gfdl(7) man page, so go check it out if you're curious ๐Ÿงš

(a) The FSF's Front-Cover Text is:

A GNU Manual

(b) The FSF's Back-Cover Text is:

You have freedom to copy and modify this GNU Manual, like GNU software. Copies published by the Free Software Foundation raise funds for GNU development. ๐Ÿ’œ

And that's the whole manual, bestie!! ๐ŸŽ€ This page was made for gcc-15, dated 2025-09-17, and it's all GCC(1) ๐Ÿ’… Now go compile something iconic โœจ๐ŸŒธ

๐Ÿ“ฆ where this page comes from