GCC(1)

๐Ÿ“ฆ
version 16-20260322 ยท 2 versions
, in ubuntu resolute

๐Ÿ“œ GCC(1) from GNU โœจ (part 1 of 130)

๐Ÿงš NAME ๐Ÿงš

okay so gcc is the GNU project's C and C++ compiler ๐Ÿ’… literally the main character of compiling ๐Ÿ‘‘

๐ŸŽ€ SYNOPSIS ๐ŸŽ€

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

Only the most useful options are listed here, bestie ๐Ÿซถ see below for the rest. g++ accepts mostly the same options as gcc โœจ

๐Ÿ’– DESCRIPTION ๐Ÿ’–

okay so when you invoke GCC, it normally does preprocessing, compilation, assembly and linking ๐Ÿ”ฅ The "overall options" let you stop this process at an intermediate stage. For example, the -c option says not to run the linker, so the output is the object files the assembler spits out ๐Ÿฆ‹

Other options get passed on to one or more stages of processing. 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 to touch them ๐Ÿ’…

Most of the command-line options you can use with GCC are useful for C programs. When an option is only useful with another language (usually C++), the explanation says so explicitly. If an option's description doesn't mention a source language, you can use it with all supported languages ๐ŸŒˆ

The usual way to run GCC is the executable called gcc, or machine-gcc when cross-compiling, or machine-gcc-version if you want a specific version of GCC. When you compile C++ programs, invoke GCC as g++ instead ๐Ÿ’œ

The gcc program takes options and file names as operands. Lots of options have multi-letter names, so you can't bundle single-letter options together. -dv is very different from -d -v ๐Ÿ˜ญ

You can mix options and other arguments, and 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 specified them. Also, the placement of the -l option is significant โญ

Lots of options have long names starting with -f or with -W---for, 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 the two forms, whichever one is not the default ๐Ÿงš

Some options take one or more arguments, usually separated from the option name by either a space or the equals sign (=). Unless documented otherwise, an argument can be either numeric or a string. Numeric arguments must typically be small unsigned decimal or hexadecimal integers, and hexadecimal arguments must begin with the 0x prefix ๐Ÿ”ข

Arguments to options that specify a size threshold can be arbitrarily large decimal or hexadecimal integers followed by a byte size suffix for a multiple of bytes. That's 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 are called byte-size in the rest of the text. For 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

Here's a summary of all the options, grouped by type ๐Ÿ’– The explanations live in the following sections.

๐Ÿฆ‹ 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  -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 --assemble  --compile  --dumpbase dumpbase
--dumpbase-ext auxdropsuf  --dumpdir dumppfx --language=language
--output=file  --pass-exit-codes --pipe  --preprocess  --verbose

๐Ÿ’… 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  --ansi

๐Ÿ”ฎ C++ Language Options

--compile-std-module -fabi-compat-version=n  -fabi-version=n
-fno-access-control  -faligned-new=[n]
-fno-assume-sane-operators-new-delete -fchar8_t  -fcheck-new
-fconcepts  -fconcepts-diagnostics-depth=n -fconstexpr-depth=n
-fconstexpr-cache-depth=n -fconstexpr-loop-limit=n
-fconstexpr-ops-limit=n -fcontracts
-fcontract-evaluation-semantic=[ignore|observe|enforce|quick_enforce]
-fcontracts-conservative-ipa  -fcontract-checks-outlined
-fcontract-disable-optimized-checks
-fcontracts-client-check=[none|pre|all]
-fcontracts-definition-check=[on|off] -fcoroutines
-fdiagnostics-all-candidates -fno-elide-constructors
-fno-enforce-eh-specs -fext-numeric-literals -fno-gnu-keywords
-fno-immediate-escalation -fno-implement-inlines
-fimplicit-constexpr -fno-implicit-inline-templates
-fno-implicit-templates -fmodule-header[=kind]
-fmodule-implicit-inline -fno-module-lazy
-fmodule-mapper=specification -fmodule-only -fmodules
-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
-freflection -fno-rtti  -fsized-deallocation -fstrict-enums
-fstrong-eval-order[=kind] -ftemplate-backtrace-limit=n
-ftemplate-depth=n -fno-threadsafe-statics  -fuse-cxa-atexit
-fno-use-cxa-get-exception-ptr -fno-weak  -nostdinc++
-fvisibility-inlines-hidden -fvisibility-ms-compat
-flang-info-include-translate[=header]
-flang-info-include-translate-not -flang-info-module-cmi[=module]
-stdlib=libstdc++,libc++ -Wabbreviated-auto-in-template-arg
-Wabi-tag  -Waligned-new[=kind] -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
-Wno-deprecated-literal-operator
-Wdeprecated-variadic-comma-omission -Weffc++
-Wno-elaborated-enum-base -Wno-exceptions
-Wno-expose-global-module-tu-local  -Wno-external-tu-local
-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
-Wno-sfinae-incomplete -Wstrict-null-sentinel
-Wno-subobject-linkage  -Wtemplates -Wno-non-c-typedef-for-linkage
-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]
-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 -fno-diagnostics-show-nesting
-fno-diagnostics-show-nesting-locations
-fdiagnostics-show-nesting-levels
-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]
-fdiagnostics-show-context[=depth]

โœจ Warning Options โœจ

okay bestie, this is the whole warnings wardrobe ๐Ÿ’… every single one of these is listed below, no cap ๐Ÿซถ

  • -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
  • -Wauto-profile
  • -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
  • -Wc++26-compat
  • -Wno-c++11-extensions
  • -Wno-c++14-extensions
  • -Wno-c++17-extensions
  • -Wno-c++20-extensions
  • -Wno-c++23-extensions
  • -Wcalloc-transposed-args
  • -Wcannot-profile
  • -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
  • -Wno-deprecated-openmp
  • -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-diag
  • -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
  • -Wkeyword-macro
  • -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-parameter=n
  • -Wunused-but-set-variable
  • -Wunused-but-set-variable=n
  • -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
  • --all-warnings
  • --extra-warnings
  • --no-warnings
  • --pedantic
  • --pedantic-errors

๐Ÿ” Static Analyzer Options ๐Ÿ”

the analyzer squad has arrived ๐Ÿฆ‹ here's every flag they came with, iykyk โœจ

  • -fanalyzer
  • -fanalyzer-assume-nothrow
  • -fanalyzer-call-summaries
  • -fanalyzer-checker=name
  • -fno-analyzer-feasibility
  • -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
  • -Wno-analyzer-throw-of-unexpected-type
  • -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 C and Objective-C only, so they're a little picky ๐ŸŽ€

  • -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
  • -Wmultiple-parameter-fwd-decl-lists
  • -Wnested-externs
  • -Wold-style-declaration
  • -Wold-style-definition
  • -Wstrict-prototypes
  • -Wtraditional
  • -Wtraditional-conversion
  • -Wdeclaration-after-statement
  • -Wpointer-sign

๐Ÿ› Debugging Options ๐Ÿ›

the debug drip is UNREAL ๐Ÿ’œ every flag is here, no skipping ๐Ÿงš

  • -g
  • -glevel
  • -gdwarf
  • -gdwarf-version
  • -gbtf
  • -gctf
  • -gctflevel
  • -gno-prune-btf
  • -ggdb
  • -gno-record-gcc-switches
  • -gstrict-dwarf
  • -gas-loc-support
  • -gas-locview-support
  • -gcodeview
  • -gcolumn-info
  • -gdwarf32
  • -gdwarf64
  • -gno-statement-frontiers
  • -gno-variable-location-views
  • -gvariable-location-views=incompat5
  • -ginternal-reset-location-views
  • -ginline-points
  • -gvms
  • -gz[=type]
  • -gsplit-dwarf
  • -gdescribe-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
  • -fvar-tracking-uninit
  • --debug

โœจ Optimization Options โœจ

okay bestie, the optimization drawer is SO full ๐Ÿ”ฅ here's every single flag, 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-profile-inlining
  • -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
  • -fcx-method
  • -fdata-sections
  • -fdce
  • -fdelayed-branch
  • -fdelete-null-pointer-checks
  • -fdep-fusion
  • -fdevirtualize
  • -fdevirtualize-speculatively
  • -fdevirtualize-at-ltrans
  • -fdse
  • -fearly-inlining
  • -fexcess-precision=style
  • -fexpensive-optimizations
  • -fext-dce
  • -ffast-math
  • -ffat-lto-objects
  • -ffinite-loops
  • -ffinite-math-only
  • -ffloat-store
  • -fforward-propagate
  • -ffp-contract=style
  • -ffp-int-builtin-inexact
  • -ffunction-sections
  • -ffuse-ops-with-volatile-access
  • -fgcse
  • -fgcse-after-reload
  • -fgcse-las
  • -fgcse-lm
  • -fgraphite-identity
  • -fgcse-sm
  • -fhoist-adjacent-loads
  • -fif-conversion
  • -fif-conversion2
  • -findirect-inlining
  • -finline-atomics
  • -finline-functions
  • -finline-functions-called-once
  • -finline-limit=n
  • -finline-small-functions
  • -finline-stringops[=fn]
  • -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-sra
  • -fipa-stack-alignment
  • -fipa-icf
  • -fipa-icf-functions
  • -fipa-icf-variables
  • -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=n
  • -flto-toplevel-asm-heuristics
  • -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-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
  • -fspeculatively-call-stored-functions
  • -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
  • -O
  • -O0
  • -O1
  • -O2
  • -O3
  • -Os
  • -Ofast
  • -Og
  • -Oz
  • --optimize

โœจ Program Instrumentation Options โœจ

the instrumentation drawer is next, bestie ๐Ÿ”๐Ÿ’œ profiling, coverage, sanitizers, hardening, the whole glam squad ๐Ÿซถ

  • -p
  • -pg
  • -fprofile-arcs
  • -coverage
  • -ftest-coverage
  • -fcondition-coverage
  • -fpath-coverage
  • -fprofile
  • -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
  • -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]
  • --coverage
  • --profile

โœจ Preprocessor Options โœจ

the preprocessor drawer ๐ŸŽ€ let's see what's in here, bestie ๐Ÿงš

  • -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
  • --comments
  • --comments-in-macros
  • --define-macro=macro[=defn]
  • --dependencies
  • --dump=letters
  • --imacros=file
  • --include=file
  • --no-integrated-cpp
  • --no-line-commands
  • --print-missing-file-dependencies
  • --traditional
  • --traditional-cpp
  • --trigraphs
  • --trace-includes
  • --undefine-macro=macro
  • --user-dependencies
  • --write-dependencies
  • --write-user-dependencies

โœจ Assembler Options โœจ

  • -Wa,option
  • -Xassembler option
  • --for-assembler=option

โœจ Linker Options โœจ

  • object-file-name
  • -flink-libatomic
  • -fuse-ld=linker
  • -llibrary
  • -nostartfiles
  • -nodefaultlibs
  • -nolibc
  • -nostdlib
  • -nostdlib++
  • -e 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
  • -Tbss=addr
  • -Tdata=addr
  • -Ttext=addr
  • -N
  • -n
  • -t
  • -Z
  • -z keyword
  • --entry=entry
  • --for-linker=option
  • --force-link=symbol
  • --no-standard-library
  • --pie
  • --static
  • --static-pie
  • --symbolic

โœจ Directory Options โœจ

  • -Bprefix
  • -Idir
  • -I-
  • -idirafter dir
  • -imacros file
  • -imultilib dir
  • -imultiarch dir
  • -iplugindir=dir
  • -iprefix file
  • -iquote dir
  • -isysroot dir
  • -isystem dir
  • -iwithprefix dir
  • -iwithprefixbefore dir
  • -Ldir
  • -no-canonical-prefixes
  • --no-sysroot-suffix
  • -nostdinc
  • -nostdinc++
  • --embed-dir=dir
  • --embed-directory=dir
  • --include-barrier
  • --include-directory=dir
  • --include-directory-after=dir
  • --include-prefix=prefix
  • --include-with-prefix=prefix
  • --include-with-prefix-after=prefix
  • --include-with-prefix-before=prefix
  • --no-canonical-prefixes
  • --no-standard-includes
  • --prefix=prefix
  • --sysroot=dir

โœจ Picolibc Options โœจ

  • --oslib=library
  • --crt0=[none|minimal|hosted|semihost]
  • --printf=[d|f|l|i|m]
  • --scanf=[d|f|l|i|m]

โœจ Code Generation Options โœจ

  • -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
  • -fwrapv-pointer
  • -fvisibility=[default|internal|hidden|protected]
  • -fstrict-volatile-bitfields
  • -fsync-libcalls
  • -fzero-init-padding-bits=value
  • -Qy
  • -Qn

๐ŸŽ€ Developer Options ๐ŸŽ€

okay bestie, this is the dev-only lineup ๐Ÿ’– Every flag below is copied exactly as the page lists it, so you can pick them out without any guesswork โœจ

  • -dletters โ€” bestie, look at this one ๐Ÿ’…
  • -dumpspecs โ€” main character flag ๐Ÿ‘‘
  • -dumpmachine โ€” iykyk ๐Ÿซถ
  • -dumpversion โ€” not me staring at this ๐Ÿ™ˆ
  • -dumpfullversion โ€” the vibe is immaculate โœจ
  • -fcallgraph-info[=su,da] โ€” a total slay ๐Ÿ’–
  • -fchecking โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -fchecking=n โ€” iconic ๐Ÿฆ‹
  • -fdbg-cnt-list โ€” periodt ๐Ÿ’…
  • -fdbg-cnt=counter-value-list โ€” pretty little option ๐ŸŒธ
  • -fdisable-ipa-pass_name โ€” extremely on brand ๐Ÿงš
  • -fdisable-rtl-pass_name โ€” sparkle moment โญ
  • -fdisable-rtl-pass-name=range-list โ€” bestie, look at this one ๐Ÿ’…
  • -fdisable-tree-pass_name โ€” main character flag ๐Ÿ‘‘
  • -fdisable-tree-pass-name=range-list โ€” iykyk ๐Ÿซถ
  • -fdump-debug โ€” not me staring at this ๐Ÿ™ˆ
  • -fdump-earlydebug โ€” the vibe is immaculate โœจ
  • -fdump-noaddr โ€” a total slay ๐Ÿ’–
  • -fdump-unnumbered โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -fdump-unnumbered-links โ€” iconic ๐Ÿฆ‹
  • -fdump-final-insns[=file] โ€” periodt ๐Ÿ’…
  • -fdump-internal-locations โ€” pretty little option ๐ŸŒธ
  • -fdump-ipa-all โ€” extremely on brand ๐Ÿงš
  • -fdump-ipa-cgraph โ€” sparkle moment โญ
  • -fdump-ipa-inline โ€” bestie, look at this one ๐Ÿ’…
  • -fdump-lang-all โ€” main character flag ๐Ÿ‘‘
  • -fdump-lang-switch โ€” iykyk ๐Ÿซถ
  • -fdump-lang-switch-options โ€” not me staring at this ๐Ÿ™ˆ
  • -fdump-lang-switch-options=filename โ€” the vibe is immaculate โœจ
  • -fdump-passes โ€” a total slay ๐Ÿ’–
  • -fdump-rtl-pass โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -fdump-rtl-pass=filename โ€” iconic ๐Ÿฆ‹
  • -fdump-statistics โ€” periodt ๐Ÿ’…
  • -fdump-tree-all โ€” pretty little option ๐ŸŒธ
  • -fdump-tree-switch โ€” extremely on brand ๐Ÿงš
  • -fdump-tree-switch-options โ€” sparkle moment โญ
  • -fdump-tree-switch-options=filename โ€” bestie, look at this one ๐Ÿ’…
  • -fcompare-debug[=opts] โ€” main character flag ๐Ÿ‘‘
  • -fcompare-debug-second โ€” iykyk ๐Ÿซถ
  • -fenable-kind-pass โ€” not me staring at this ๐Ÿ™ˆ
  • -fenable-kind-pass=range-list โ€” the vibe is immaculate โœจ
  • -fira-verbose=n โ€” a total slay ๐Ÿ’–
  • -flto-report โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -flto-report-wpa โ€” iconic ๐Ÿฆ‹
  • -fmem-report-wpa โ€” periodt ๐Ÿ’…
  • -fmem-report โ€” pretty little option ๐ŸŒธ
  • -fpre-ipa-mem-report โ€” extremely on brand ๐Ÿงš
  • -fpost-ipa-mem-report โ€” sparkle moment โญ
  • -fopt-info โ€” bestie, look at this one ๐Ÿ’…
  • -fopt-info-options[=file] โ€” main character flag ๐Ÿ‘‘
  • -fmultiflags โ€” iykyk ๐Ÿซถ
  • -fprofile-report โ€” not me staring at this ๐Ÿ™ˆ
  • -frandom-seed=string โ€” the vibe is immaculate โœจ
  • -fsched-verbose=n โ€” a total slay ๐Ÿ’–
  • -fsel-sched-verbose โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -fsel-sched-dump-cfg โ€” iconic ๐Ÿฆ‹
  • -fsel-sched-pipelining-verbose โ€” periodt ๐Ÿ’…
  • -fstats โ€” pretty little option ๐ŸŒธ
  • -fstack-usage โ€” extremely on brand ๐Ÿงš
  • -ftime-report โ€” sparkle moment โญ
  • -ftime-report-details โ€” bestie, look at this one ๐Ÿ’…
  • -fvar-tracking-assignments-toggle โ€” main character flag ๐Ÿ‘‘
  • -gtoggle โ€” iykyk ๐Ÿซถ
  • -print-autofdo-gcov-version โ€” not me staring at this ๐Ÿ™ˆ
  • -print-file-name=library โ€” the vibe is immaculate โœจ
  • -print-libgcc-file-name โ€” a total slay ๐Ÿ’–
  • -print-multi-directory โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -print-multi-lib โ€” iconic ๐Ÿฆ‹
  • -print-multi-os-directory โ€” periodt ๐Ÿ’…
  • -print-multiarch โ€” pretty little option ๐ŸŒธ
  • -print-prog-name=program โ€” extremely on brand ๐Ÿงš
  • -print-search-dirs โ€” sparkle moment โญ
  • -Q โ€” bestie, look at this one ๐Ÿ’…
  • -print-sysroot โ€” main character flag ๐Ÿ‘‘
  • -print-sysroot-headers-suffix โ€” iykyk ๐Ÿซถ
  • -save-temps โ€” not me staring at this ๐Ÿ™ˆ
  • -save-temps=cwd โ€” the vibe is immaculate โœจ
  • -save-temps=obj โ€” a total slay ๐Ÿ’–
  • -specs=file โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -time[=file] โ€” iconic ๐Ÿฆ‹
  • --dump=letters โ€” periodt ๐Ÿ’…
  • --print-autofdo-gcov-version โ€” pretty little option ๐ŸŒธ
  • --print-file-name=library โ€” extremely on brand ๐Ÿงš
  • --print-libgcc-file-file-name โ€” sparkle moment โญ
  • --print-multi-directory โ€” bestie, look at this one ๐Ÿ’…
  • --print-multi-lib โ€” main character flag ๐Ÿ‘‘
  • --print-multi-os-directory โ€” iykyk ๐Ÿซถ
  • --print-multiarch โ€” not me staring at this ๐Ÿ™ˆ
  • --print-prog-name=program โ€” the vibe is immaculate โœจ
  • --print-search-dirs โ€” a total slay ๐Ÿ’–
  • --print-sysroot โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • --print-sysroot-headers-suffix โ€” iconic ๐Ÿฆ‹
  • --save-temps โ€” periodt ๐Ÿ’…
  • --specs=file โ€” pretty little option ๐ŸŒธ
  • --param name=value โ€” extremely on brand ๐Ÿงš
the page spells this one --print-libgcc-file-file-name with the doubled "file", so copy it exactly ๐Ÿ‘€

๐ŸŒˆ Machine-Dependent Options ๐ŸŒˆ

these are the target-specific ones, and each chip family gets its own little list ๐Ÿฆ‹

โœจ AArch64 Options โœจ

  • -mabi=name โ€” bestie, look at this one ๐Ÿ’…
  • -mbig-endian โ€” main character flag ๐Ÿ‘‘
  • -mlittle-endian โ€” iykyk ๐Ÿซถ
  • -menable-sysreg-checking โ€” not me staring at this ๐Ÿ™ˆ
  • -mgeneral-regs-only โ€” the vibe is immaculate โœจ
  • -mcmodel=tiny โ€” a total slay ๐Ÿ’–
  • -mcmodel=small โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mcmodel=large โ€” iconic ๐Ÿฆ‹
  • -mstrict-align โ€” periodt ๐Ÿ’…
  • -momit-leaf-frame-pointer โ€” pretty little option ๐ŸŒธ
  • -mtls-dialect=desc โ€” extremely on brand ๐Ÿงš
  • -mtls-dialect=traditional โ€” sparkle moment โญ
  • -mtls-size=size โ€” bestie, look at this one ๐Ÿ’…
  • -mtp=name โ€” main character flag ๐Ÿ‘‘
  • -mfix-cortex-a53-835769 โ€” iykyk ๐Ÿซถ
  • -mfix-cortex-a53-843419 โ€” not me staring at this ๐Ÿ™ˆ
  • -mlow-precision-recip-sqrt โ€” the vibe is immaculate โœจ
  • -mlow-precision-sqrt โ€” a total slay ๐Ÿ’–
  • -mlow-precision-div โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mmax-vectorization โ€” iconic ๐Ÿฆ‹
  • -mautovec-preference=name โ€” periodt ๐Ÿ’…
  • -mpc-relative-literal-loads โ€” pretty little option ๐ŸŒธ
  • -msign-return-address=scope โ€” extremely on brand ๐Ÿงš
  • -mbranch-protection=features โ€” sparkle moment โญ
  • -mharden-sls=opts โ€” bestie, look at this one ๐Ÿ’…
  • -march=name โ€” main character flag ๐Ÿ‘‘
  • -mcpu=name โ€” iykyk ๐Ÿซถ
  • -mtune=name โ€” not me staring at this ๐Ÿ™ˆ
  • -moverride=string โ€” the vibe is immaculate โœจ
  • -mstack-protector-guard=guard โ€” a total slay ๐Ÿ’–
  • -mstack-protector-guard-reg=sysreg โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mstack-protector-guard-offset=offset โ€” iconic ๐Ÿฆ‹
  • -mtrack-speculation โ€” periodt ๐Ÿ’…
  • -moutline-atomics โ€” pretty little option ๐ŸŒธ
  • -mearly-ra โ€” extremely on brand ๐Ÿงš
  • -mearly-ldp-fusion โ€” sparkle moment โญ
  • -mlate-ldp-fusion โ€” bestie, look at this one ๐Ÿ’…
  • -msve-vector-bits=bits โ€” main character flag ๐Ÿ‘‘

โœจ Adapteva Epiphany Options โœจ

  • -mhalf-reg-file โ€” iykyk ๐Ÿซถ
  • -mprefer-short-insn-regs โ€” not me staring at this ๐Ÿ™ˆ
  • -mbranch-cost=num โ€” the vibe is immaculate โœจ
  • -mcmove โ€” a total slay ๐Ÿ’–
  • -mnops=num โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -msoft-cmpsf โ€” iconic ๐Ÿฆ‹
  • -msplit-lohi โ€” periodt ๐Ÿ’…
  • -mpost-inc โ€” pretty little option ๐ŸŒธ
  • -mpost-modify โ€” extremely on brand ๐Ÿงš
  • -mstack-offset=num โ€” sparkle moment โญ
  • -mround-nearest โ€” bestie, look at this one ๐Ÿ’…
  • -mlong-calls โ€” main character flag ๐Ÿ‘‘
  • -mshort-calls โ€” iykyk ๐Ÿซถ
  • -msmall16 โ€” not me staring at this ๐Ÿ™ˆ
  • -mfp-mode=mode โ€” the vibe is immaculate โœจ
  • -mmay-round-for-trunc โ€” a total slay ๐Ÿ’–
  • -mfp-iarith โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mvect-double โ€” iconic ๐Ÿฆ‹
  • -max-vect-align=num โ€” periodt ๐Ÿ’…
  • -msplit-vecmove-early โ€” pretty little option ๐ŸŒธ
  • -m1reg-reg โ€” extremely on brand ๐Ÿงš

โœจ AMD GCN Options โœจ

  • -march=gpu โ€” sparkle moment โญ
  • -mtune=gpu โ€” bestie, look at this one ๐Ÿ’…
  • -mgang-private-size=bytes โ€” main character flag ๐Ÿ‘‘
  • -msram-ecc=[on|off|any] โ€” iykyk ๐Ÿซถ
  • -mxnack=[on|off|any] โ€” not me staring at this ๐Ÿ™ˆ
  • -Wopenacc-dims โ€” the vibe is immaculate โœจ

โœจ ARC Options โœจ

  • -mbarrel-shifter โ€” a total slay ๐Ÿ’–
  • -mjli-always โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mcpu=cpu โ€” iconic ๐Ÿฆ‹
  • -mA6 โ€” periodt ๐Ÿ’…
  • -mARC600 โ€” pretty little option ๐ŸŒธ
  • -mA7 โ€” extremely on brand ๐Ÿงš
  • -mARC700 โ€” sparkle moment โญ
  • -mdpfp โ€” bestie, look at this one ๐Ÿ’…
  • -mdpfp-compact โ€” main character flag ๐Ÿ‘‘
  • -mdpfp-fast โ€” iykyk ๐Ÿซถ
  • -mno-dpfp-lrsr โ€” not me staring at this ๐Ÿ™ˆ
  • -mea โ€” the vibe is immaculate โœจ
  • -mmul32x16 โ€” a total slay ๐Ÿ’–
  • -mmul64 โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -matomic โ€” iconic ๐Ÿฆ‹
  • -mnorm โ€” periodt ๐Ÿ’…
  • -mspfp โ€” pretty little option ๐ŸŒธ
  • -mspfp-compact โ€” extremely on brand ๐Ÿงš
  • -mspfp-fast โ€” sparkle moment โญ
  • -msimd โ€” bestie, look at this one ๐Ÿ’…
  • -msoft-float โ€” main character flag ๐Ÿ‘‘
  • -mswap โ€” iykyk ๐Ÿซถ
  • -mlock โ€” not me staring at this ๐Ÿ™ˆ
  • -mswape โ€” the vibe is immaculate โœจ
  • -mxy โ€” a total slay ๐Ÿ’–
  • -misize โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -marclinux โ€” iconic ๐Ÿฆ‹
  • -marclinux_prof โ€” periodt ๐Ÿ’…
  • -mlong-calls โ€” pretty little option ๐ŸŒธ
  • -mmedium-calls โ€” extremely on brand ๐Ÿงš
  • -msdata โ€” sparkle moment โญ
  • -mirq-ctrl-saved โ€” bestie, look at this one ๐Ÿ’…
  • -mrgf-banked-regs โ€” main character flag ๐Ÿ‘‘
  • -mlpc-width=width โ€” iykyk ๐Ÿซถ
  • -G num โ€” not me staring at this ๐Ÿ™ˆ
  • -mvolatile-cache โ€” the vibe is immaculate โœจ
  • -mtp-regno=regno โ€” a total slay ๐Ÿ’–
  • -mbitops โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mcmem โ€” iconic ๐Ÿฆ‹
  • -munaligned-access โ€” periodt ๐Ÿ’…
  • -mauto-modify-reg โ€” pretty little option ๐ŸŒธ
  • -mno-brcc โ€” extremely on brand ๐Ÿงš
  • -mcase-vector-pcrel โ€” sparkle moment โญ
  • -mno-cond-exec โ€” bestie, look at this one ๐Ÿ’…
  • -mearly-cbranchsi โ€” main character flag ๐Ÿ‘‘
  • -mindexed-loads โ€” iykyk ๐Ÿซถ
  • -mlra-priority-none โ€” not me staring at this ๐Ÿ™ˆ
  • -mlra-priority-compact โ€” the vibe is immaculate โœจ
  • -mlra-priority-noncompact โ€” a total slay ๐Ÿ’–
  • -mmillicode โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -msize-level=level โ€” iconic ๐Ÿฆ‹
  • -mtune=cpu โ€” periodt ๐Ÿ’…
  • -mmultcost=num โ€” pretty little option ๐ŸŒธ
  • -mcode-density-frame โ€” extremely on brand ๐Ÿงš
  • -mmpy-option=multo โ€” sparkle moment โญ
  • -mdiv-rem โ€” bestie, look at this one ๐Ÿ’…
  • -mcode-density โ€” main character flag ๐Ÿ‘‘
  • -mll64 โ€” iykyk ๐Ÿซถ
  • -mfpu=fpu โ€” not me staring at this ๐Ÿ™ˆ
  • -mrf16 โ€” the vibe is immaculate โœจ
  • -mbranch-index โ€” a total slay ๐Ÿ’–

โœจ ARM Options โœจ

  • -mapcs-frame โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mapcs โ€” iconic ๐Ÿฆ‹
  • -mabi=name โ€” periodt ๐Ÿ’…
  • -mgeneral-regs-only โ€” pretty little option ๐ŸŒธ
  • -mno-sched-prolog โ€” extremely on brand ๐Ÿงš
  • -mlittle-endian โ€” sparkle moment โญ
  • -mbig-endian โ€” bestie, look at this one ๐Ÿ’…
  • -mbe8 โ€” main character flag ๐Ÿ‘‘
  • -mbe32 โ€” iykyk ๐Ÿซถ
  • -mfloat-abi=name โ€” not me staring at this ๐Ÿ™ˆ
  • -mfp16-format=name โ€” the vibe is immaculate โœจ
  • -mthumb-interwork โ€” a total slay ๐Ÿ’–
  • -mcpu=name โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -march=name โ€” iconic ๐Ÿฆ‹
  • -mfpu=name โ€” periodt ๐Ÿ’…
  • -mtune=name โ€” pretty little option ๐ŸŒธ
  • -mstructure-size-boundary=n โ€” extremely on brand ๐Ÿงš
  • -mabort-on-noreturn โ€” sparkle moment โญ
  • -mlong-calls โ€” bestie, look at this one ๐Ÿ’…
  • -msingle-pic-base โ€” main character flag ๐Ÿ‘‘
  • -mpic-register=reg โ€” iykyk ๐Ÿซถ
  • -mpic-data-is-text-relative โ€” not me staring at this ๐Ÿ™ˆ
  • -mnop-fun-dllimport โ€” the vibe is immaculate โœจ
  • -mpoke-function-name โ€” a total slay ๐Ÿ’–
  • -mthumb โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -marm โ€” iconic ๐Ÿฆ‹
  • -mtpcs-frame โ€” periodt ๐Ÿ’…
  • -mtpcs-leaf-frame โ€” pretty little option ๐ŸŒธ
  • -mcaller-super-interworking โ€” extremely on brand ๐Ÿงš
  • -mcallee-super-interworking โ€” sparkle moment โญ
  • -mtp=name โ€” bestie, look at this one ๐Ÿ’…
  • -mtls-dialect=dialect โ€” main character flag ๐Ÿ‘‘
  • -mword-relocations โ€” iykyk ๐Ÿซถ
  • -mfix-cortex-m3-ldrd โ€” not me staring at this ๐Ÿ™ˆ
  • -mfix-cortex-a57-aes-1742098 โ€” the vibe is immaculate โœจ
  • -mfix-cortex-a72-aes-1655431 โ€” a total slay ๐Ÿ’–
  • -munaligned-access โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mslow-flash-data โ€” iconic ๐Ÿฆ‹
  • -masm-syntax-unified โ€” periodt ๐Ÿ’…
  • -mrestrict-it โ€” pretty little option ๐ŸŒธ
  • -mpure-code โ€” extremely on brand ๐Ÿงš
  • -mcmse โ€” sparkle moment โญ
  • -mfix-cmse-cve-2021-35465 โ€” bestie, look at this one ๐Ÿ’…
  • -mstack-protector-guard=guard โ€” main character flag ๐Ÿ‘‘
  • -mstack-protector-guard-offset=offset โ€” iykyk ๐Ÿซถ
  • -mfdpic โ€” not me staring at this ๐Ÿ™ˆ
  • -mbranch-protection=features โ€” the vibe is immaculate โœจ
  • -mvectorize-with-neon-quad โ€” a total slay ๐Ÿ’–
  • -mvectorize-with-neon-double โ€” lowkey dev-only energy ๐Ÿ”ฅ

โœจ AVR Options โœจ

  • -mmcu=mcu โ€” iconic ๐Ÿฆ‹
  • -mabsdata โ€” periodt ๐Ÿ’…
  • -maccumulate-args โ€” pretty little option ๐ŸŒธ
  • -mcvt โ€” extremely on brand ๐Ÿงš
  • -mbranch-cost=cost โ€” sparkle moment โญ
  • -mfuse-add=level โ€” bestie, look at this one ๐Ÿ’…
  • -mfuse-move=level โ€” main character flag ๐Ÿ‘‘
  • -mfuse-move2 โ€” iykyk ๐Ÿซถ
  • -mcall-prologues โ€” not me staring at this ๐Ÿ™ˆ
  • -mgas-isr-prologues โ€” the vibe is immaculate โœจ
  • -mint8 โ€” a total slay ๐Ÿ’–
  • -mflmap โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mdouble=bits โ€” iconic ๐Ÿฆ‹
  • -mlong-double=bits โ€” periodt ๐Ÿ’…
  • -mno-call-main โ€” pretty little option ๐ŸŒธ
  • -mn_flash=size โ€” extremely on brand ๐Ÿงš
  • -mfract-convert-truncate โ€” sparkle moment โญ
  • -mno-interrupts โ€” bestie, look at this one ๐Ÿ’…
  • -mmain-is-OS_task โ€” main character flag ๐Ÿ‘‘
  • -mrelax โ€” iykyk ๐Ÿซถ
  • -mpmem-wrap-around โ€” not me staring at this ๐Ÿ™ˆ
  • -mrmw โ€” the vibe is immaculate โœจ
  • -mstrict-X โ€” a total slay ๐Ÿ’–
  • -mtiny-stack โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mrodata-in-ram โ€” iconic ๐Ÿฆ‹
  • -msplit-bit-shift โ€” periodt ๐Ÿ’…
  • -msplit-ldst โ€” pretty little option ๐ŸŒธ
  • -mshort-calls โ€” extremely on brand ๐Ÿงš
  • -mskip-bug โ€” sparkle moment โญ
  • -muse-nonzero-bits โ€” bestie, look at this one ๐Ÿ’…
  • -nodevicelib โ€” main character flag ๐Ÿ‘‘
  • -nodevicespecs โ€” iykyk ๐Ÿซถ
  • -Waddr-space-convert โ€” not me staring at this ๐Ÿ™ˆ
  • -Wmisspelled-isr โ€” the vibe is immaculate โœจ

โœจ Blackfin Options โœจ

  • -mcpu=cpu[-sirevision] โ€” a total slay ๐Ÿ’–
  • -msim โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -momit-leaf-frame-pointer โ€” iconic ๐Ÿฆ‹
  • -mspecld-anomaly โ€” periodt ๐Ÿ’…
  • -mcsync-anomaly โ€” pretty little option ๐ŸŒธ
  • -mlow-64k โ€” extremely on brand ๐Ÿงš
  • -mstack-check-l1 โ€” sparkle moment โญ
  • -mid-shared-library โ€” bestie, look at this one ๐Ÿ’…
  • -mleaf-id-shared-library โ€” main character flag ๐Ÿ‘‘
  • -mshared-library-id=n โ€” iykyk ๐Ÿซถ
  • -msep-data โ€” not me staring at this ๐Ÿ™ˆ
  • -mlong-calls โ€” the vibe is immaculate โœจ
  • -mfast-fp โ€” a total slay ๐Ÿ’–
  • -minline-plt โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mmulticore โ€” iconic ๐Ÿฆ‹
  • -mcorea โ€” periodt ๐Ÿ’…
  • -mcoreb โ€” pretty little option ๐ŸŒธ
  • -msdram โ€” extremely on brand ๐Ÿงš
  • -micplb โ€” sparkle moment โญ

โœจ C6X Options โœจ

  • -mbig-endian โ€” bestie, look at this one ๐Ÿ’…
  • -mlittle-endian โ€” main character flag ๐Ÿ‘‘
  • -march=cpu โ€” iykyk ๐Ÿซถ
  • -msim โ€” not me staring at this ๐Ÿ™ˆ
  • -msdata=sdata-type โ€” the vibe is immaculate โœจ
  • -mdsbt โ€” a total slay ๐Ÿ’–
  • -mlong-calls โ€” lowkey dev-only energy ๐Ÿ”ฅ

โœจ CRIS Options โœจ

  • -mcpu=cpu โ€” iconic ๐Ÿฆ‹
  • -march=cpu โ€” periodt ๐Ÿ’…
  • -mtune=cpu โ€” pretty little option ๐ŸŒธ
  • -mmax-stackframe=n โ€” extremely on brand ๐Ÿงš
  • -metrax4 โ€” sparkle moment โญ
  • -metrax100 โ€” bestie, look at this one ๐Ÿ’…
  • -mpdebug โ€” main character flag ๐Ÿ‘‘
  • -mcc-init โ€” iykyk ๐Ÿซถ
  • -mno-side-effects โ€” not me staring at this ๐Ÿ™ˆ
  • -mstack-align โ€” the vibe is immaculate โœจ
  • -mdata-align โ€” a total slay ๐Ÿ’–
  • -mconst-align โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -m32-bit โ€” iconic ๐Ÿฆ‹
  • -m16-bit โ€” periodt ๐Ÿ’…
  • -m8-bit โ€” pretty little option ๐ŸŒธ
  • -mno-prologue-epilogue โ€” extremely on brand ๐Ÿงš
  • -mbest-lib-options โ€” sparkle moment โญ
  • -moverride-best-lib-options โ€” bestie, look at this one ๐Ÿ’…
  • -mtrap-using-break8 โ€” main character flag ๐Ÿ‘‘
  • -mtrap-unaligned-atomic โ€” iykyk ๐Ÿซถ
  • -munaligned-atomic-may-use-library โ€” not me staring at this ๐Ÿ™ˆ
  • -sim โ€” the vibe is immaculate โœจ
  • -sim2 โ€” a total slay ๐Ÿ’–
  • -mmul-bug-workaround โ€” lowkey dev-only energy ๐Ÿ”ฅ

โœจ C-SKY Options โœจ

  • -march=arch โ€” iconic ๐Ÿฆ‹
  • -mcpu=cpu โ€” periodt ๐Ÿ’…
  • -mbig-endian โ€” pretty little option ๐ŸŒธ
  • -mlittle-endian โ€” extremely on brand ๐Ÿงš
  • -mfpu=fpu โ€” sparkle moment โญ
  • -mdouble-float โ€” bestie, look at this one ๐Ÿ’…
  • -mfdivdu โ€” main character flag ๐Ÿ‘‘
  • -mfloat-abi=name โ€” iykyk ๐Ÿซถ
  • -melrw โ€” not me staring at this ๐Ÿ™ˆ
  • -mistack โ€” the vibe is immaculate โœจ
  • -mmp โ€” a total slay ๐Ÿ’–
  • -mcp โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mcache โ€” iconic ๐Ÿฆ‹
  • -msecurity โ€” periodt ๐Ÿ’…
  • -mtrust โ€” pretty little option ๐ŸŒธ
  • -mdsp โ€” extremely on brand ๐Ÿงš
  • -medsp โ€” sparkle moment โญ
  • -mvdsp โ€” bestie, look at this one ๐Ÿ’…
  • -mdiv โ€” main character flag ๐Ÿ‘‘
  • -msmart โ€” iykyk ๐Ÿซถ
  • -mhigh-registers โ€” not me staring at this ๐Ÿ™ˆ
  • -manchor โ€” the vibe is immaculate โœจ
  • -mpushpop โ€” a total slay ๐Ÿ’–
  • -mmultiple-stld โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mconstpool โ€” iconic ๐Ÿฆ‹
  • -mstack-size โ€” periodt ๐Ÿ’…
  • -mccrt โ€” pretty little option ๐ŸŒธ
  • -mbranch-cost=n โ€” extremely on brand ๐Ÿงš
  • -msched-prolog โ€” sparkle moment โญ
  • -msim โ€” bestie, look at this one ๐Ÿ’…

โœจ Cygwin and MinGW Options โœจ

  • -mconsole โ€” main character flag ๐Ÿ‘‘
  • -mcrtdll=library โ€” iykyk ๐Ÿซถ
  • -mdll โ€” not me staring at this ๐Ÿ™ˆ
  • -mnop-fun-dllimport โ€” the vibe is immaculate โœจ
  • -mthreads โ€” a total slay ๐Ÿ’–
  • -municode โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mwin32 โ€” iconic ๐Ÿฆ‹
  • -mwindows โ€” periodt ๐Ÿ’…
  • -fno-set-stack-executable โ€” pretty little option ๐ŸŒธ
  • -fwritable-relocated-rdata โ€” extremely on brand ๐Ÿงš
  • -mpe-aligned-commons โ€” sparkle moment โญ
  • -muse-libstdc-wrappers โ€” bestie, look at this one ๐Ÿ’…

โœจ Darwin Options โœจ

  • -all_load โ€” main character flag ๐Ÿ‘‘
  • -allowable_client โ€” iykyk ๐Ÿซถ
  • -arch name โ€” not me staring at this ๐Ÿ™ˆ
  • -arch_errors_fatal โ€” the vibe is immaculate โœจ
  • -asm_macosx_version_min=version โ€” a total slay ๐Ÿ’–
  • -bind_at_load โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -bundle โ€” iconic ๐Ÿฆ‹
  • -bundle_loader โ€” periodt ๐Ÿ’…
  • -client_name โ€” pretty little option ๐ŸŒธ
  • -compatibility_version โ€” extremely on brand ๐Ÿงš
  • -current_version โ€” sparkle moment โญ
  • -dead_strip โ€” bestie, look at this one ๐Ÿ’…
  • -dependency-file โ€” main character flag ๐Ÿ‘‘
  • -dylib_file โ€” iykyk ๐Ÿซถ
  • -dylinker โ€” not me staring at this ๐Ÿ™ˆ
  • -dylinker_install_name โ€” the vibe is immaculate โœจ
  • -dynamic โ€” a total slay ๐Ÿ’–
  • -dynamiclib โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -exported_symbols_list โ€” iconic ๐Ÿฆ‹
  • -fapple-kext โ€” periodt ๐Ÿ’…
  • -fconstant-cfstrings โ€” pretty little option ๐ŸŒธ
  • -ffix-and-continue โ€” extremely on brand ๐Ÿงš
  • -filelist โ€” sparkle moment โญ
  • -findirect-data โ€” bestie, look at this one ๐Ÿ’…
  • -flat_namespace โ€” main character flag ๐Ÿ‘‘
  • -force_cpusubtype_ALL โ€” iykyk ๐Ÿซถ
  • -force_flat_namespace โ€” not me staring at this ๐Ÿ™ˆ
  • -framework name โ€” the vibe is immaculate โœจ
  • -gfull โ€” a total slay ๐Ÿ’–
  • -gused โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -headerpad_max_install_names โ€” iconic ๐Ÿฆ‹
  • -iframework โ€” periodt ๐Ÿ’…
  • -image_base โ€” pretty little option ๐ŸŒธ
  • -init symbol-name โ€” extremely on brand ๐Ÿงš
  • -install_name โ€” sparkle moment โญ
  • -keep_private_externs โ€” bestie, look at this one ๐Ÿ’…
  • -matt-stubs โ€” main character flag ๐Ÿ‘‘
  • -mconstant-cfstrings โ€” iykyk ๐Ÿซถ
  • -mdynamic-no-pic โ€” not me staring at this ๐Ÿ™ˆ
  • -mfix-and-continue โ€” the vibe is immaculate โœจ
  • -mkernel โ€” a total slay ๐Ÿ’–
  • -mmacosx-version-min=version โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mone-byte-bool โ€” iconic ๐Ÿฆ‹
  • -msymbol-stubs โ€” periodt ๐Ÿ’…
  • -mtarget-linker[=]version โ€” pretty little option ๐ŸŒธ
  • -nodefaultexport โ€” extremely on brand ๐Ÿงš
  • -nodefaultrpaths โ€” sparkle moment โญ
  • -pagezero_size โ€” bestie, look at this one ๐Ÿ’…
  • -preload โ€” main character flag ๐Ÿ‘‘
  • -read_only_relocs โ€” iykyk ๐Ÿซถ
  • -sectalign โ€” not me staring at this ๐Ÿ™ˆ
  • -sectcreate โ€” the vibe is immaculate โœจ
  • -seg_addr_table โ€” a total slay ๐Ÿ’–
  • -seg1addr โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -segaddr โ€” iconic ๐Ÿฆ‹
  • -segprot โ€” periodt ๐Ÿ’…
  • -segs_read_only_addr โ€” pretty little option ๐ŸŒธ
  • -segs_read_write_addr โ€” extremely on brand ๐Ÿงš
  • -sub_library โ€” sparkle moment โญ
  • -sub_umbrella โ€” bestie, look at this one ๐Ÿ’…
  • -twolevel_namespace โ€” main character flag ๐Ÿ‘‘
  • -twolevel_namespace_hints โ€” iykyk ๐Ÿซถ
  • -umbrella โ€” not me staring at this ๐Ÿ™ˆ
  • -undefined โ€” the vibe is immaculate โœจ
  • -unexported_symbols_list โ€” a total slay ๐Ÿ’–
  • -weak_framework name โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -weak_reference_mismatches โ€” iconic ๐Ÿฆ‹
  • -whatsloaded โ€” periodt ๐Ÿ’…
  • -whyload โ€” pretty little option ๐ŸŒธ
  • -F โ€” extremely on brand ๐Ÿงš
  • -ObjC โ€” sparkle moment โญ
  • -ObjC++ โ€” bestie, look at this one ๐Ÿ’…
  • -Wnonportable-cfstrings โ€” main character flag ๐Ÿ‘‘

โœจ DEC Alpha Options โœจ

  • -mno-fp-regs โ€” iykyk ๐Ÿซถ
  • -msoft-float โ€” not me staring at this ๐Ÿ™ˆ
  • -mieee โ€” the vibe is immaculate โœจ
  • -mieee-with-inexact โ€” a total slay ๐Ÿ’–
  • -mieee-conformant โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mfp-trap-mode=mode โ€” iconic ๐Ÿฆ‹
  • -mfp-rounding-mode=mode โ€” periodt ๐Ÿ’…
  • -mtrap-precision=mode โ€” pretty little option ๐ŸŒธ
  • -mbuild-constants โ€” extremely on brand ๐Ÿงš
  • -mcpu=cpu-type โ€” sparkle moment โญ
  • -mtune=cpu-type โ€” bestie, look at this one ๐Ÿ’…
  • -mbwx โ€” main character flag ๐Ÿ‘‘
  • -mmax โ€” iykyk ๐Ÿซถ
  • -mfix โ€” not me staring at this ๐Ÿ™ˆ
  • -mcix โ€” the vibe is immaculate โœจ
  • -msafe-bwa โ€” a total slay ๐Ÿ’–
  • -msafe-partial โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mfloat-vax โ€” iconic ๐Ÿฆ‹
  • -mfloat-ieee โ€” periodt ๐Ÿ’…
  • -mexplicit-relocs โ€” pretty little option ๐ŸŒธ
  • -msmall-data โ€” extremely on brand ๐Ÿงš
  • -mlarge-data โ€” sparkle moment โญ
  • -msmall-text โ€” bestie, look at this one ๐Ÿ’…
  • -mlarge-text โ€” main character flag ๐Ÿ‘‘
  • -mmemory-latency=time โ€” iykyk ๐Ÿซถ
  • -mtls-kernel โ€” not me staring at this ๐Ÿ™ˆ
  • -mtls-size=bitsize โ€” the vibe is immaculate โœจ
  • -mlong-double-128 โ€” a total slay ๐Ÿ’–
  • -mlong-double-64 โ€” lowkey dev-only energy ๐Ÿ”ฅ

โœจ eBPF Options โœจ

  • -mbig-endian โ€” iconic ๐Ÿฆ‹
  • -mlittle-endian โ€” periodt ๐Ÿ’…
  • -mframe-limit=bytes โ€” pretty little option ๐ŸŒธ
  • -mxbpf โ€” extremely on brand ๐Ÿงš
  • -mco-re โ€” sparkle moment โญ
  • -mjmpext โ€” bestie, look at this one ๐Ÿ’…
  • -mjmp32 โ€” main character flag ๐Ÿ‘‘
  • -malu32 โ€” iykyk ๐Ÿซถ
  • -mv3-atomics โ€” not me staring at this ๐Ÿ™ˆ
  • -mbswap โ€” the vibe is immaculate โœจ
  • -msdiv โ€” a total slay ๐Ÿ’–
  • -msmov โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mcpu=version โ€” iconic ๐Ÿฆ‹
  • -masm=dialect โ€” periodt ๐Ÿ’…
  • -minline-memops-threshold=bytes โ€” pretty little option ๐ŸŒธ
  • -Wco-re โ€” extremely on brand ๐Ÿงš

โœจ FR30 Options โœจ

  • -msmall-model โ€” sparkle moment โญ
  • -mno-lsim โ€” bestie, look at this one ๐Ÿ’…

โœจ FRV Options โœจ

  • -mgpr-32 โ€” main character flag ๐Ÿ‘‘
  • -mgpr-64 โ€” iykyk ๐Ÿซถ
  • -mfpr-32 โ€” not me staring at this ๐Ÿ™ˆ
  • -mfpr-64 โ€” the vibe is immaculate โœจ
  • -mhard-float โ€” a total slay ๐Ÿ’–
  • -msoft-float โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -malloc-cc โ€” iconic ๐Ÿฆ‹
  • -mfixed-cc โ€” periodt ๐Ÿ’…
  • -mdword โ€” pretty little option ๐ŸŒธ
  • -mdouble โ€” extremely on brand ๐Ÿงš
  • -mmedia โ€” sparkle moment โญ
  • -mmuladd โ€” bestie, look at this one ๐Ÿ’…
  • -mfdpic โ€” main character flag ๐Ÿ‘‘
  • -minline-plt โ€” iykyk ๐Ÿซถ
  • -mgprel-ro โ€” not me staring at this ๐Ÿ™ˆ
  • -multilib-library-pic โ€” the vibe is immaculate โœจ
  • -mlinked-fp โ€” a total slay ๐Ÿ’–
  • -mlong-calls โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -malign-labels โ€” iconic ๐Ÿฆ‹
  • -mlibrary-pic โ€” periodt ๐Ÿ’…
  • -macc-4 โ€” pretty little option ๐ŸŒธ
  • -macc-8 โ€” extremely on brand ๐Ÿงš
  • -mpack โ€” sparkle moment โญ
  • -mno-eflags โ€” bestie, look at this one ๐Ÿ’…
  • -mno-cond-move โ€” main character flag ๐Ÿ‘‘
  • -mno-optimize-membar โ€” iykyk ๐Ÿซถ
  • -mno-scc โ€” not me staring at this ๐Ÿ™ˆ
  • -mno-cond-exec โ€” the vibe is immaculate โœจ
  • -mno-vliw-branch โ€” a total slay ๐Ÿ’–
  • -mno-multi-cond-exec โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mno-nested-cond-exec โ€” iconic ๐Ÿฆ‹
  • -mtomcat-stats โ€” periodt ๐Ÿ’…
  • -mTLS โ€” pretty little option ๐ŸŒธ
  • -mtls โ€” extremely on brand ๐Ÿงš
  • -mcpu=cpu โ€” sparkle moment โญ

โœจ FT32 Options โœจ

  • -msim โ€” bestie, look at this one ๐Ÿ’…
  • -mnodiv โ€” main character flag ๐Ÿ‘‘
  • -mft32b โ€” iykyk ๐Ÿซถ
  • -mcompress โ€” not me staring at this ๐Ÿ™ˆ
  • -mnopm โ€” the vibe is immaculate โœจ

โœจ GNU/Linux Options โœจ

  • -mglibc โ€” a total slay ๐Ÿ’–
  • -muclibc โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mmusl โ€” iconic ๐Ÿฆ‹
  • -mbionic โ€” periodt ๐Ÿ’…
  • -mandroid โ€” pretty little option ๐ŸŒธ
  • -tno-android-cc โ€” extremely on brand ๐Ÿงš
  • -tno-android-ld โ€” sparkle moment โญ

โœจ H8/300 Options โœจ

  • -mrelax โ€” bestie, look at this one ๐Ÿ’…
  • -mh โ€” main character flag ๐Ÿ‘‘
  • -ms โ€” iykyk ๐Ÿซถ
  • -mn โ€” not me staring at this ๐Ÿ™ˆ
  • -msx โ€” the vibe is immaculate โœจ
  • -ms2600 โ€” a total slay ๐Ÿ’–
  • -mquickcall โ€” lowkey dev-only energy ๐Ÿ”ฅ
  • -mslowbyte โ€” iconic ๐Ÿฆ‹
  • -mexr โ€” periodt ๐Ÿ’…
  • -mint32 โ€” pretty little option ๐ŸŒธ
  • -malign-300 โ€” extremely on brand ๐Ÿงš

๐ŸŒธ HPPA Options (HPPA Options) ๐ŸŒธ

Okay bestie, the HPPA flags are literally so iconic ๐Ÿ’… Here's the whole squad:

  • -march=architecture-type
  • -mno-atomic-libcalls
  • -mcaller-copies
  • -mdisable-fpregs
  • -mdisable-indexing
  • -mordered
  • -mfast-indirect-calls
  • -mgas
  • -mgnu-ld
  • -mhp-ld
  • -mfixed-range=register-range
  • -mcoherent-ldcw
  • -mlinker-opt
  • -mlong-calls
  • -mlong-load-store
  • -mno-space-regs
  • -msoft-float
  • -mpa-risc-1-0
  • -mpa-risc-1-1
  • -mpa-risc-2-0
  • -mportable-runtime
  • -mschedule=cpu-type
  • -msoft-mult
  • -msio
  • -mwsio
  • -munix=unix-std
  • -nolibdld
  • -static
  • -threads

๐Ÿฆ‹ IA-64 Options (IA-64 Options) ๐Ÿฆ‹

The IA-64 crew is huge, so grab a snack ๐Ÿ“

  • -mbig-endian
  • -mlittle-endian
  • -mgnu-as
  • -mgnu-ld
  • -mno-pic
  • -mvolatile-asm-stop
  • -mregister-names
  • -msdata
  • -mconstant-gp
  • -mauto-pic
  • -minline-float-divide-min-latency
  • -minline-float-divide-max-throughput
  • -mno-inline-float-divide
  • -minline-int-divide-min-latency
  • -minline-int-divide-max-throughput
  • -mno-inline-int-divide
  • -minline-sqrt-min-latency
  • -minline-sqrt-max-throughput
  • -mno-inline-sqrt
  • -mdwarf2-asm
  • -mearly-stop-bits
  • -mfixed-range=register-range
  • -mtls-size=tls-size
  • -mtune=cpu-type
  • -milp32
  • -mlp64
  • -msched-br-data-spec
  • -msched-ar-data-spec
  • -msched-control-spec
  • -msched-br-in-data-spec
  • -msched-ar-in-data-spec
  • -msched-in-control-spec
  • -msched-spec-ldc
  • -msched-spec-control-ldc
  • -msched-stop-bits-after-every-cycle
  • -msched-count-spec-in-critical-path
  • -msel-sched-dont-check-control-spec
  • -msched-fp-mem-deps-zero-cost
  • -msched-max-memory-insns-hard-limit
  • -msched-max-memory-insns=max-insns

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

Small but mighty โœจ

  • -mbarrel-shift-enabled
  • -mdivide-enabled
  • -mmultiply-enabled
  • -msign-extend-enabled
  • -muser-enabled

๐Ÿ’œ LoongArch Options (LoongArch Options) ๐Ÿ’œ

The LoongArch list is giving main character energy ๐Ÿ‘‘

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

๐ŸŒˆ LynxOS Options (LynxOS Options) ๐ŸŒˆ

  • -mshared
  • -mthreads
  • -mlegacy-threads

๐ŸŽ€ M32C Options (M32C Options) ๐ŸŽ€

  • -mcpu=cpu
  • -msim
  • -memregs=number

๐Ÿ“ M32R/D Options (M32R/D Options) ๐Ÿ“

  • -m32r2
  • -m32rx
  • -m32r
  • -mdebug
  • -malign-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 (M680x0 Options) ๐Ÿซถ

The M680x0 lineup is absolutely stacked ๐Ÿ”ฅ

  • -march=arch
  • -mcpu=cpu
  • -mtune=tune
  • -m68000
  • -m68020
  • -m68020-40
  • -m68020-60
  • -m68030
  • -m68040
  • -m68060
  • -m68302
  • -m68332
  • -m68851
  • -mcpu32
  • -mfidoa
  • -m5200
  • -m5206e
  • -m528x
  • -m5307
  • -m5407
  • -mcfv4e
  • -mbitfield
  • -mc68000
  • -mc68020
  • -mrtd
  • -mdiv
  • -mshort
  • -mhard-float
  • -m68881
  • -msoft-float
  • -mpcrel
  • -malign-int
  • -mstrict-align
  • -msep-data
  • -mshared-library-id=n
  • -mid-shared-library
  • -mxgot
  • -mxtls
  • -mlong-jump-table-offsets

๐ŸŒธ MCore Options (MCore Options) ๐ŸŒธ

  • -mhardlit
  • -mdiv
  • -mrelax-immediates
  • -mwide-bitfields
  • -m4byte-functions
  • -mcallgraph-data
  • -mslow-bytes
  • -mno-lsim
  • -mlittle-endian
  • -mbig-endian
  • -m210
  • -m340
  • -mstack-increment

๐Ÿฆ‹ MicroBlaze Options (MicroBlaze Options) ๐Ÿฆ‹

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

๐Ÿ’– MIPS Options (MIPS Options) ๐Ÿ’–

Okay the MIPS section is the longest one, so get comfy bestie ๐Ÿ›‹๏ธโœจ

  • -EL
  • -EB
  • -mel
  • -meb
  • -march=arch
  • -mtune=arch
  • -mips1
  • -mips2
  • -mips3
  • -mips4
  • -mips32
  • -mips32r2
  • -mips32r3
  • -mips32r5
  • -mips32r6
  • -mips64
  • -mips64r2
  • -mips64r3
  • -mips64r5
  • -mips64r6
  • -mips16
  • -mmips16e2
  • -mflip-mips16
  • -minterlink-compressed
  • -minterlink-mips16
  • -mabi=abi
  • -mabicalls
  • -mshared
  • -mplt
  • -mxgot
  • -mgp32
  • -mgp64
  • -mfp32
  • -mfpxx
  • -mfp64
  • -mhard-float
  • -msoft-float
  • -mno-float
  • -msingle-float
  • -mdouble-float
  • -modd-spreg
  • -mabs=mode
  • -mnan=encoding
  • -mdsp
  • -mdspr2
  • -mmcu
  • -meva
  • -mvirt
  • -mxpa
  • -mcrc
  • -mginv
  • -mmicromips
  • -mmsa
  • -mloongson-mmi
  • -mloongson-ext
  • -mloongson-ext2
  • -mfpu=fpu-type
  • -msmartmips
  • -mpaired-single
  • -mdmx
  • -mips3d
  • -mmt
  • -mllsc
  • -mlong64
  • -mlong32
  • -msym32
  • -Gnum
  • -mno-local-sdata
  • -mno-extern-sdata
  • -mno-gopt
  • -membedded-data
  • -muninit-const-in-rodata
  • -mcode-readable=setting
  • -mno-data-in-code
  • -mcode-xonly
  • -msplit-addresses
  • -mexplicit-relocs
  • -mexplicit-relocs=release
  • -mno-check-zero-division
  • -mdivide-traps
  • -mdivide-breaks
  • -mno-load-store-pairs
  • -mstrict-align
  • -mno-unaligned-access
  • -mmemcpy
  • -mlong-calls
  • -mmad
  • -mimadd
  • -mno-fused-madd
  • -nocpp
  • -mfix-24k
  • -mfix-r4000
  • -mfix-r4400
  • -mfix-r5900
  • -mfix-r10000
  • -mfix-rm7000
  • -mfix-vr4120
  • -mfix-vr4130
  • -mfix-sb1
  • -mfix4300
  • -mr10k-cache-barrier=setting
  • -mflush-func=func
  • -mno-flush-func
  • -mbranch-cost=num
  • -mbranch-likely
  • -mcompact-branches=policy
  • -mno-fp-exceptions
  • -mvr4130-align
  • -msynci
  • -mno-lxc1-sxc1
  • -mno-madd4
  • -mno-relax-pic-calls
  • -mmcount-ra-address
  • -mframe-header-opt

๐Ÿงš MMIX Options (MMIX Options) ๐Ÿงš

  • -mlibfuncs
  • -mepsilon
  • -mabi=gnu
  • -mabi=mmixware
  • -mzero-extend
  • -mknuthdiv
  • -mtoplevel-symbols
  • -melf
  • -mbranch-predict
  • -mbase-addresses
  • -msingle-exit
  • -mset-data-start=address
  • -mset-program-start=address
  • -mno-set-program-start

๐Ÿ’œ MN10300 Options (MN10300 Options) ๐Ÿ’œ

  • -mmult-bug
  • -mno-mult-bug
  • -mam33
  • -mam33-2
  • -mam34
  • -mtune=cpu-type
  • -mno-return-pointer-on-d0
  • -mno-crt0
  • -mrelax
  • -mno-liw
  • -mno-setlb

๐Ÿ‘‘ Moxie Options (Moxie Options) ๐Ÿ‘‘

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

๐ŸŒˆ MSP430 Options (MSP430 Options) ๐ŸŒˆ

Okay the MSP430 list has some seriously specific vibes ๐Ÿ”ฅ

  • -msim
  • -masm-hex
  • -mmcu=name
  • -mlarge
  • -msmall
  • -mrelax
  • -mwarn-mcu
  • -mwarn-devices-csv
  • -mcode-region=where
  • -mdata-region=where
  • -muse-lower-region-prefix
  • -msilicon-errata=name[,name...]
  • -msilicon-errata-warn=name[,name...]
  • -mhwmult=type
  • -minrt
  • -mtiny-printf
  • -mmax-inline-shift=n

๐Ÿซถ NDS32 Options (NDS32 Options) ๐Ÿซถ

  • -mbig-endian
  • -mlittle-endian
  • -EB
  • -EL
  • -mabi=name
  • -mfloat-abi=name
  • -mreduced-regs
  • -mfull-regs
  • -malways-align
  • -malign-functions
  • -mfp-as-gp
  • -mcmov
  • -mhw-abs
  • -mext-perf
  • -mext-perf2
  • -mext-string
  • -mext-dsp
  • -mext-fpu-fma
  • -mext-fpu-sp
  • -mext-fpu-dp
  • -mv3push
  • -m16-bit
  • -mvh
  • -misr-vector-size=num
  • -misr-secure=num
  • -mcache-block-size=num
  • -march=arch
  • -mcpu=cpu
  • -mconfig-fpu=num
  • -mconfig-mul=type
  • -mconfig-register-ports=kind
  • -mcmodel=code-model
  • -mctor-dtor
  • -mrelax
  • -mrelax-hint
  • -msched-prolog-epilog
  • -mno-ret-in-naked-func
  • -malways-save-lp
  • -munaligned-access
  • -minline-asm-r15

๐Ÿ’… Nvidia PTX Options (Nvidia PTX Options) ๐Ÿ’…

  • -m64
  • -march=arch
  • -misa=arch
  • -march-map=arch
  • -mptx=version
  • -mmainkernel
  • -moptimize
  • -msoft-stack
  • -msoft-stack-reserve-local=size
  • -muniform-simt
  • -mgomp

๐Ÿฆ‹ OpenRISC Options (OpenRISC Options) ๐Ÿฆ‹

  • -mboard=name
  • -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 (PDP-11 Options) ๐ŸŽ€

  • -mfpu
  • -msoft-float
  • -mac0
  • -m40
  • -m45
  • -m10
  • -mint32
  • -mint16
  • -msplit
  • -munix-asm
  • -mdec-asm
  • -mgnu-asm
  • -mlra

๐Ÿงš PowerPC Options ๐Ÿงš

Soooo the PowerPC stuff lives somewhere else, bestie. Check out RS/6000 and PowerPC Options ๐Ÿ’–

๐ŸŒธ PRU Options (PRU Options) ๐ŸŒธ

  • -mmcu=mcu
  • -minrt
  • -mno-relax
  • -mloop
  • -mmul
  • -mfillzero
  • -mabi=variant

๐Ÿ’œ RISC-V Options (RISC-V Options) ๐Ÿ’œ

The RISC-V list is a whole vibe, iykyk ๐Ÿ”ฅโœจ

  • -mbranch-cost=N-instruction
  • -mabi=ABI-string
  • -mfdiv
  • -mdiv
  • -mno-fence-tso
  • -misa-spec=ISA-spec-string
  • -march=[ISA|Profile|Profile_ISA|processor-string]
  • -mcpu=processor-string
  • -mtune=processor-string
  • -mpreferred-stack-boundary=num
  • -msmall-data-limit=N-bytes
  • -msave-restore
  • -mno-shorten-memrefs
  • -mstrict-align
  • -mscalar-strict-align
  • -mno-vector-strict-align
  • -mcmodel=medlow
  • -mcmodel=medany
  • -mcmodel=large
  • -mexplicit-relocs
  • -mrelax
  • -mriscv-attribute
  • -malign-data=type
  • -mbig-endian
  • -mlittle-endian
  • -mstack-protector-guard=guard
  • -mstack-protector-guard-reg=reg
  • -mstack-protector-guard-offset=offset
  • -mcsr-check
  • -momit-leaf-frame-pointer
  • -mmovcc
  • -mno-inline-atomics
  • -mno-inline-strlen
  • -mno-inline-strcmp
  • -mno-inline-strncmp
  • -mstringop-strategy=strategy
  • -mtls-dialect=desc
  • -mtls-dialect=trad
  • -mrvv-vector-bits=value
  • -mrvv-max-lmul=value
  • -madjust-lmul-cost
  • -mmax-vectorization
  • -mno-autovec-segment

๐Ÿซถ RL78 Options (RL78 Options) ๐Ÿซถ

Last but not least, the RL78 crew ๐Ÿฆ‹๐Ÿ’–

  • -msim
  • -mallregs
  • -mrelax
  • -mes0
  • -mmul=none
  • -mmul=g13
  • -mmul=g14
  • -mmul=rl78
  • -mcpu=g10
  • -mcpu=g13
  • -mcpu=g14
  • -mcpu=rl78
  • -mg10
  • -mg13
  • -mg14
  • -mrl78
  • -msave-mduc-in-interrupts

๐Ÿ’… RS/6000 and PowerPC Options

Okay bestie, this list is absolutely massive ๐Ÿ’€ These are the flags for the PowerPC family, so they only matter when you're targeting that chip ๐Ÿงš

-mcpu=cpu-type -mtune=cpu-type -mcmodel=code-model -mprofile-kernel -mpowerpc64 -maltivec -mpowerpc-gpopt -mpowerpc-gfxopt -mmfcrf -mpopcntb -mpopcntd -mfprnd -mcmpb -mhard-dfp -mfull-toc -mminimal-toc -mno-fp-in-toc -mno-sum-in-toc -maix64 -maix32 -m64 -m32 -mxl-compat -mpe -malign-power -malign-natural -msoft-float -mhard-float -mmultiple -mupdate -mavoid-indexed-addresses -mfused-madd -mbit-align -mbit-word -mstrict-align -mrelocatable -mrelocatable-lib -mlittle -mlittle-endian -mbig -mbig-endian -mdynamic-no-pic -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 -mregnames -mlongcall -mpltseq -mtls-markers -mblock-move-inline-limit=num -mblock-compare-inline-limit=num -mblock-compare-inline-loop-limit=num -mblock-ops-unaligned-vsx -mstring-compare-inline-limit=num -misel -mvsx -mvrsave -mmulhw -mdlmzb -mprototype -msim -mmvme -mads -myellowknife -memb -meabi -msdata -msdata=opt -mreadonly-in-sdata -mvxworks -G num -mrecip -mrecip=opt -mno-recip -mrecip-precision -mveclibabi=type -mfriz -mpointers-to-nested-functions -msave-toc-indirect -mpower8-fusion -mcrypto -mhtm -mquad-memory -mquad-memory-atomic -mcompat-align-parm -mfloat128 -mfloat128-hardware -mgnu-attribute -mstack-protector-guard=guard -mstack-protector-guard-reg=reg -mstack-protector-guard-offset=offset -mprefixed -mpcrel -mmma -mrop-protect -mprivileged -mno-splat-word-constant -mno-splat-float-constant -mno-ieee128-constant -mno-warn-altivec-long

๐ŸŒธ RX Options

Heads up: these are the RX chip flags, so they're only for RX targets ๐ŸŽ€

-m64bit-doubles -m32bit-doubles -fpu -nofpu -mcpu=name -mbig-endian-data -mlittle-endian-data -msmall-data-limit=N -msim -mas100-syntax -mrelax -mmax-constant-size=N -mint-register=N -mpid -mno-allow-string-insns -mjsr -mno-warn-multiple-fast-interrupts -msave-acc-in-interrupts -mlra

๐Ÿงš S/390 and zSeries Options

-mtune=cpu-type -march=cpu-type -mhard-float -msoft-float -mhard-dfp -mlong-double-64 -mlong-double-128 -mbackchain -mpacked-stack -msmall-exec -mmvcle -m64 -m31 -mdebug -mesa -mzarch -mhtm -mvx -mzvector -mtpf-trace -mtpf-trace-skip -mmain -mfused-madd -mno-fused-madd -mwarn-framesize=framesize -mwarn-dynamicstack -mstack-size=stack-size -mno-stack-size -mstack-guard=stack-guard -mno-stack-guard -mstack-protector-guard=guard -mstack-protector-guard-record -mhotpatch=pre-halfwords,post-halfwords -mno-pic-data-is-text-relative -mindirect-branch=choice -mindirect-branch-jump=choice -mindirect-branch-call=choice -mfunction-return=choice -mfunction-return-mem=choice -mfunction-return-reg=choice -mindirect-branch-table -mfentry -mrecord-mcount -mnop-mcount -mpreserve-args -munaliged-symbols

๐Ÿ“ SH Options

-m1 -m2 -m2e -m2a -m2a-nofpu -m2a-single -m2a-single-only -m3 -m3e -m4 -m4-nofpu -m4-single -m4-single-only -m4-100 -m4-100-nofpu -m4-100-single -m4-100-single-only -m4-200 -m4-200-nofpu -m4-200-single -m4-200-single-only -m4-300 -m4-300-nofpu -m4-300-single -m4-300-single-only -m4-340 -m4-400 -m4-500 -m4a -m4al -m4a-nofpu -m4a-single -m4a-single-only -mb -ml -mdalign -mrelax -mbigtable -mbitops -mfmovd -mrenesas -mnomacsave -mieee -misize -minline-ic_invalidate -mprefergot -musermode -multcost=number -mdiv=strategy -mdivsi3_libfunc=name -mfixed-range=register-range -maccumulate-outgoing-args -matomic-model=atomic-model -mbranch-cost=num -mzdcbranch -mcbranch-force-delay-slot -mfsca -mfsrra -mpretend-cmove -mfdpic -mtas -mlra

๐ŸŒˆ Solaris 2 Options

-mclear-hwcap -mno-clear-hwcap -mimpure-text -mno-impure-text -gsctf

๐Ÿ’œ SPARC Options

-mcpu=cpu-type -mtune=cpu-type -mcmodel=code-model -mmemory-model=mem-model -m32 -m64 -mptr32 -mptr64 -mapp-regs -mfaster-structs -mflat -mfpu -mhard-float -msoft-float -mhard-quad-float -msoft-quad-float -mstack-bias -mstd-struct-return -munaligned-doubles -muser-mode -mv8plus -mvis -mvis2 -mvis3 -mvis3b -mvis4 -mvis4b -mcbcond -mfmaf -mfsmuld -mpopc -msubxc -mfix-at697f -mfix-ut699 -mfix-ut700 -mfix-gr712rc

๐Ÿซถ System V Options

-YP,paths -Ym,dir

๐Ÿ”ฅ V850 Options

-mlong-calls -mep -mprolog-function -mspace -mtda=n -msda=n -mzda=n -mv850 -mv850e3v5 -m850e2v4 -mv850e2v3 -mv850e2 -mv850e1 -mv850es -mv850e -mdisable-callt -mrelax -mlong-jumps -msoft-float -mhard-float -mloop -mrh850-abi -mghs -mgcc-abi -m8byte-align -mbig-switch -mapp-regs -msmall-sld -mno-strict-align -mjump-tables-in-data-section

๐Ÿฆ‹ VAX Options

-munix -mgnu -md -md-float -mg -mg-float -mlra -mvaxc-alignment -mqmath

โœจ Visium Options

-mdebug -msim -mfpu -mhard-float -msoft-float -mcpu=cpu-type -mtune=cpu-type -msv-mode -muser-mode

๐Ÿ‘‘ VMS Options

-mvms-return-codes -mdebug-main=prefix -mmalloc64 -mpointer-size=size

๐Ÿ’– VxWorks Options

-mrtp -msmp -mvthreads -non-static -Bstatic -Bdynamic -Xbind-lazy -Xbind-now

๐Ÿ’… x86 Options

-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 -mieee-fp -mrtd -malign-double -mpreferred-stack-boundary=num -mincoming-stack-boundary=num -mcld -mcx16 -msahf -mmovbe -mcrc32 -mmwait -mrecip -mrecip=opt -mvzeroupper -mstv -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 -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.2 -mamx-avx512 -mamx-tf32 -mmovrs -mamx-movrs -mavx512bmm -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 -mstack-arg-probe -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 -mrecord-return -mfentry-name=name -mfentry-section=name -mskip-rax-setup -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 -mtls-dialect=type -mrelax-cmpxchg-loop -mindirect-branch=choice -mfunction-return=choice -mindirect-branch-register -mharden-sls=choice -mindirect-branch-cs-prefix -mapx-inline-asm-use-gpr32 -mgather -mscatter -mneeded -mno-direct-extern-access -munroll-only-small-loops -mdispatch-scheduler -mlam=choice

๐ŸŽ€ x86 Windows Options

See Cygwin and MinGW Options.

๐ŸŒท Xstormy16 Options

-msim

๐ŸŒ™ Xtensa Options

-mconst16 -mforce-no-pic -mno-serialize-volatile -mtext-section-literals -mauto-litpools -mno-target-align -mlongcalls -mabi=abi-type -mextra-l32r-costs=cycles -mstrict-align

๐Ÿง zSeries Options

See S/390 and zSeries Options.

โœจ Options Controlling the Kind of Output

Okay, so compilation can have up to four stages ๐Ÿซถ: preprocessing, compilation proper, assembly and linking, always in that order. GCC can preprocess and compile several files into several assembler input files, or into just one assembler input file. Then each assembler input file makes an object file, and linking mashes all the object files together (the ones just compiled and the ones you passed in) into one executable file ๐Ÿ”ฅ

The file name suffix decides what kind of compilation happens for each input file ๐Ÿ’…

  • 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. Note that 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. Note that you must link with the libobjc library to make an Objective-C++ program work. Note that .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) ๐ŸŒธ

okay bestie, this is the file-suffix cheat sheet ๐Ÿ’… gcc looks at the ending of each filename to decide what kind of source it's dealing with ๐ŸŽ€

  • C++ source that must be preprocessed ๐ŸŒธ: file.cc, file.cp, file.cxx, file.cpp, file.CPP, file.c++, file.C. Heads up: in .cxx, the last two letters must both be a literal lowercase x, and .C means a literal capital C.
  • Objective-C++ source that must be preprocessed ๐Ÿฆ‹: file.mm, file.M.
  • Objective-C++ source that should not be preprocessed ๐Ÿงš: file.mii.
  • C++ header file ๐Ÿ’œ, which gets turned into a precompiled header or an Ada spec: file.hh, file.H, file.hp, file.hxx, file.hpp, file.HPP, file.h++, file.tcc.
  • Fixed form Fortran that should not be preprocessed ๐Ÿ“: file.f, file.for, file.ftn, file.fi.
  • Fixed form Fortran that must be preprocessed (with the traditional preprocessor) โœจ: file.F, file.FOR, file.fpp, file.FPP, file.FTN.
  • Free form Fortran that should not be preprocessed ๐ŸŒˆ: file.f90, file.f95, file.f03, file.f08, file.fii.
  • Free form Fortran that must be preprocessed (with the traditional preprocessor) ๐Ÿ‘‘: file.F90, file.F95, file.F03, file.F08.
  • COBOL source code ๐Ÿ’พ: file.cob, file.COB, file.cbl, file.CBL.
  • Go source code ๐Ÿน: file.go.
  • D source code ๐Ÿ”ฅ: file.d.
  • D interface file โญ: file.di.
  • D documentation code (Ddoc) ๐Ÿ“–: file.dd.
  • Ada source code with a library unit declaration ๐ŸŒธ, meaning 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 files are also called specs: file.ads.
  • Ada source code with a library unit body ๐Ÿฆ‹, meaning a subprogram or package body. These files are also called bodies: file.adb.
  • Assembler code ๐Ÿซถ: file.s.
  • Assembler code that must be preprocessed ๐Ÿ’…: file.S, file.sx.
  • other ๐ŸŽ€: an object file that gets fed straight into linking. Any filename with no recognized suffix is treated this way.

You can also name the input language yourself with the -x option, so gcc doesn't have to guess from the suffix ๐Ÿง 

  • -x language, --language=language, --language language โœจ โ€” explicitly sets the language for the input files that come after it, instead of letting the compiler pick a default from the filename suffix. This applies to every following input file until the next -x option shows up. The possible values for language are:
c  c-header  cpp-output
c++  c++-header  c++-system-header c++-user-header c++-cpp-output
c++-system-module
objective-c  objective-c-header  objective-c-cpp-output  objc-cpp-output
objective-c++  objective-c++-header  objective-c++-cpp-output
objc++-cpp-output
assembler  assembler-with-cpp
ada  adascil  adawhy
cobol
d
f77  f77-cpp-input  f95  f95-cpp-input
go
modula-2  modula-2-cpp-output
rust
algol68
lto
โš ๏ธ bestie, -x does not pick a language standard for you ๐Ÿ™ˆ For example, -x f77 may also need -std=legacy for some older source code.
  • -x none ๐Ÿ’€ โ€” turns off any language specification, so the files after it get handled by their filename suffixes, exactly like -x was never used at all.

If you only want some stages of compilation, you can use -x (or filename suffixes) to say where gcc should start, and one of -c, -S, or -E to say where it should stop ๐Ÿ›‘ Heads up, some combos (like -x cpp-output -E) make gcc do absolutely nothing ๐Ÿ˜ญ

  • -c, --compile ๐ŸŒท โ€” compiles or assembles the source files but does not link. The linking stage just 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 get quietly ignored.

  • -S, --assemble ๐ŸŒ™ โ€” stops after the compilation proper and does not assemble. The output is an assembler code file for each input file that isn't already assembler code.

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

Input files that don't need compiling get ignored.

  • -E, --preprocess ๐Ÿ”ฎ โ€” stops after preprocessing and doesn't run the compiler proper. The output is preprocessed source code, sent straight to standard output.

Input files that don't need preprocessing get ignored.

  • -o file, --output=file, --output file ๐ŸŽฏ โ€” puts the primary output in file. This works for whatever you're making, whether that's an executable, an object file, an assembler file, or preprocessed C.

If you don't specify -o, the defaults are: an executable goes in a.out, the object file for source.suffix goes in source.o, its assembler file goes in source.s, a precompiled header file goes in source.suffix.gch, and all the preprocessed C source goes to standard output ๐Ÿง

Even though -o only names the primary output, it also affects how the auxiliary and dump outputs get named. Check the examples below! Unless overridden, both auxiliary and dump outputs land 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's file type. For dump outputs, the dump file's suffix gets tacked onto the input file's suffix. In compilation commands, the base name of both auxiliary and dump outputs is the primary output's base name. 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 is just that base name. Otherwise, they get concatenated with a dash in between ๐Ÿ’–

gcc -c foo.c ...

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

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

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

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

A linker output does things a little differently and prefixes the aux and dump outputs instead:

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

This will generally name the aux outputs dir/foobar-foo.* and dir/foobar-bar.*, and the 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 the aux outputs are named dir/foo.* and the dump outputs are named dir/foo.c.*.

You can tweak the location and names of auxiliary and dump outputs with the options -dumpbase, -dumpbase-ext, -dumpdir, -save-temps=cwd, and -save-temps=obj ๐Ÿ› ๏ธ

  • -dumpbase dumpbase, --dumpbase dumpbase ๐ŸŽ€ โ€” sets the base name for auxiliary and dump output files. It does not change the name of the primary output file. Intermediate outputs, when they get preserved, don't count as primary outputs. They count as auxiliary outputs instead:
gcc -save-temps -S foo.c

This saves the (no longer temporary) preprocessed file in foo.i, then compiles to the implied output file foo.s. Meanwhile:

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

This preprocesses to save-foo.i, compiles to save-foo.s (now an intermediate, so 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, like the ones requested by -fdump-* options, keep the input name's suffix. Aux output files, like the ones requested by other non-dump options (for example -save-temps, -gsplit-dwarf, -fcallgraph-info), drop it.

You can get similar suffix differentiation between dump and aux outputs 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 (like -dumpdir or -save-temps=*) gets 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.*, ignoring the dir/ in -o, the ./ prefix implied by -save-temps=cwd, and the pfx- in -dumpdir.

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

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

This creates foo.o and bar.o as primary outputs. It also avoids overwriting the aux and dump outputs by using the dumpbase as a prefix, creating aux and dump outputs named main-foo.* and main-bar.*.

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

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

This will name the aux outputs dir/foo.* and the dump outputs dir/foo.c.*. Notice how their basenames come from the input name, but the directory still defaults to the output's directory.

An 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 the aux outputs dir/foo.* and dir/bar.*, and the dump outputs dir/foo.c.* and dir/bar.c.*. LTO recompilation during linking will use dir/foobar. as the prefix for dumps and aux files ๐ŸŒˆ

  • -dumpbase-ext auxdropsuf, --dumpbase-ext auxdropsuf โ€” the suffix-dropper for aux files ๐Ÿฆ‹

Okay so when gcc is forming the name of an auxiliary output file (not a dump file, those are different, bestie), it drops the trailing auxdropsuf from dumpbase before it tacks on any suffixes. If you don't specify it, it defaults to the suffix of a default dumpbase. That means the suffix of the input file when -dumpbase isn't on the command line, or when dumpbase is combined with dumppfx. Literally so sneaky, but it works ๐Ÿ’…

gcc foo.c -c -o dir/foo.o -dumpbase x-foo.c -dumpbase-ext .c ...

This one is giving main character energy ๐Ÿ‘‘ It creates dir/foo.o as the main output, puts the auxiliary outputs in dir/x-foo.* (using the location of the primary output), and drops the .c suffix from the dumpbase. Dump outputs keep the suffix though, so you get dir/x-foo.c.* โœจ

This option gets ignored if it doesn't match the suffix of a specified dumpbase, except as an alternative to the executable suffix when gcc is appending the linker output base name to dumppfx. Here's how that slays:

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

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

  • -dumpdir dumppfx, --dumpdir dumppfx โ€” the prefix princess ๐ŸŽ€

When gcc forms the name of an auxiliary or dump output file, it uses dumppfx as a prefix. Iconic.

gcc -dumpdir pfx- -c foo.c ...

This creates foo.o as the primary output, with auxiliary outputs named pfx-foo.*. It combines the given dumppfx with the default dumpbase, which comes from the default primary output, which in turn comes from the input name. Dump outputs also take the input name suffix, so you get pfx-foo.c.* ๐Ÿงš

If you want dumppfx to act as a directory name, it has to end with a directory separator. Don't skip this part, it's lowkey important ๐Ÿ™ˆ

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.*. It combines the given dumppfx with the default dumpbase derived from the primary output name. Dump outputs take the input name suffix too: dir/bar.c.* ๐Ÿ’œ

By default, dumppfx is 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 you give more than one of these, the last one prevails:

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

This one outputs foo.o, with auxiliary outputs named foo.*, because -save-temps=* overrides the dumppfx from the earlier -dumpdir option. It doesn't matter 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 specify -dumpbase, the dumpbase (minus an auxdropsuf suffix) plus a dash gets appended to the dumppfx. This happens whether that dumppfx is explicit or defaulted. If the dumpbase has any directory components, it overrides the dumppfx instead of getting appended. This way every compilation gets its own differently-named aux and dump outputs, which is so organized ๐Ÿซถ

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

This sends 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 contrast that with a single-input compilation:

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

Applying -dumpbase to a single source doesn't compute and append a separate dumpbase per input file. 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 goes through the dumpbase- transformation above. For example, that's the compilation of foo.c and bar.c from before, just without -c. If you specify neither -dumpdir nor -dumpbase, the linker output base name gets appended to the default dumppfx instead, plus a dash. That base name has the auxdropsuf removed if you specified it, or the executable suffix removed otherwise. Now, careful, because this is where it gets different:

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

This one does not append the output name main to dumppfx, because -dumpdir was explicitly specified. The idea is that an explicit dumppfx can include the output name as part of the prefix, if you want that. Only an explicitly specified -dumpbase would get combined with it, so gcc doesn't just throw away a meaningful option ๐Ÿฆ‹

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 earlier, but it's different with an explicit -dumpdir that stops the combination. That's true even if -save-temps=* overrides it:

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

Here the auxiliary outputs are foo.*, and the dump outputs are foo.c.*. They end up in the current working directory, as requested by -save-temps=cwd ๐ŸŒธ

Okay, here's the whole thing summed up in a slightly imprecise but super intuitive way. The primary output name gets split into a directory part and a basename part. The dumppfx is set to the directory part, unless -dumpdir or -save-temps=* overrides it. The dumpbase is set to the basename part, unless -dumpbase overrides it. If there are multiple inputs or you're linking, this dumpbase can combine with dumppfx and get 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 because they keep the suffix from dumpbase ๐Ÿ’–

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

When lto-wrapper runs sub-compilers, it appends the LTO stage names to the dumppfx it received. It makes sure the dumppfx contains a directory component so that it overrides any -dumpdir. Then it passes that along as -dumpbase to the sub-compilers ๐Ÿงš

  • -v, --verbose โ€” the show-your-work option ๐Ÿ”ฅ

This prints (on standard error output) the commands executed to run the stages of compilation. It also prints the version number of the compiler driver program, plus the version of the preprocessor and the compiler proper. Very receipts-y ๐Ÿงพ

  • -### โ€” the "what would you run" option ๐Ÿ™ˆ

This is like -v, except the commands are not executed. Arguments get quoted unless they only contain alphanumeric characters or "./-_". It's super useful for shell scripts that want to capture the command lines the driver generates, so they can keep track of what's going on behind the scenes โœจ

  • --help โ€” the menu, but make it cute ๐Ÿ’œ

This prints (on the standard output) a description of the command-line options that gcc understands. If you also specify -v, then --help gets passed on to the various processes gcc invokes, so each one can show you the command-line options it accepts. And if you specify -Wextra (before the --help option), command-line options that have no documentation attached are shown too. Undocumented options get to come to the party ๐ŸŽ‰

  • --target-help โ€” the target-specific menu ๐ŸŒˆ

This prints (on the standard output) a description of the target-specific command-line options for each tool. For some targets, extra target-specific information may also be printed ๐Ÿฆ‹

  • --help={class|[^]qualifier}[,...] โ€” the filtered menu, bestie ๐ŸŽ€

This 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 โ€” shows every optimization option the compiler supports โญ
  • warnings โ€” shows every option that controls the warning messages the compiler produces ๐Ÿ”ฅ
  • target โ€” shows target-specific options. Unlike --target-help, though, target-specific options of the linker and assembler are not displayed. Those tools don't currently support the extended --help= syntax, so they get left out ๐Ÿ™ˆ
  • params โ€” shows the values the --param option recognizes ๐Ÿ’…
  • language โ€” shows 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 pick the common class to see it ๐Ÿ‘‘
  • common โ€” shows the options that are common to all languages ๐Ÿซถ

Here are the supported qualifiers:

  • undocumented โ€” only shows the options that are undocumented ๐ŸŒธ
  • joined โ€” shows options that take an argument appearing after an equal sign in the same continuous piece of text, like --help=target ๐Ÿ’–
  • separate โ€” shows options that take an argument appearing as a separate word after the original option, like -o output-file ๐Ÿงš

So if you want every undocumented target-specific switch the compiler supports, you'd type:

--help=target,undocumented

You can invert the sense of a qualifier by putting the ^ character in front of it. For example, to display all binary warning options (the ones that are either on or off and don't take an argument) that have a description, use:

--help=warnings,^joined,^undocumented
The argument to --help= should not consist solely of inverted qualifiers. Don't be that person ๐Ÿ™…โ€โ™€๏ธ

Combining several classes is possible, but it usually restricts the output so much that there's nothing to display. There's one case where it totally works, though: 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 successive 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 (the main character always wins) ๐Ÿ‘‘

If you slip -Q in before --help= on the command line, the text --help= spits out gets a total glow-up โœจ. Instead of describing the options, it tells you whether each one is enabled, disabled, or set to a specific value ๐Ÿ’–. This only works if the compiler actually knows that value at the point where --help= shows up ๐Ÿซถ.

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 reacts to whatever command-line options came before it, so you can totally find out which optimizations are on at -O2 with:

-Q -O2 --help=optimizers

Or if you want the receipts ๐Ÿงพ, you can figure out which binary optimizations are enabled by -O3 by running:

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 โ€” Shows the version number and copyrights of the GCC you invoked ๐ŸŒธ.
  • -pass-exit-codes, --pass-exit-codes โ€” Okay, so normally gcc exits with the code of 1 if any phase of the compiler returns a non-success return code ๐Ÿ˜ฌ. With this flag, it instead returns the numerically highest error from any phase that reported an error ๐Ÿ‘‘. The C, C++, and Fortran front ends return 4 if they hit an internal compiler error ๐Ÿ’€.
  • -pipe, --pipe โ€” Uses pipes instead of temporary files to pass stuff between the different stages of compilation ๐Ÿงš.
This one is a little risky bestie ๐Ÿ™ˆ. It fails to work on some systems where the assembler can't read from a pipe. The GNU assembler has no trouble though โœจ.
  • -wrapper โ€” Invokes all subcommands under a wrapper program ๐ŸŽ€. You pass the wrapper's name and its parameters as a comma separated list ๐Ÿ’….
gcc -c t.c -wrapper gdb,--args

This runs all the subprograms of gcc under gdb --args, so the invocation of cc1 becomes gdb --args cc1 .... ๐Ÿ“.

  • -ffile-prefix-map=old=new โ€” When you compile files living in the directory old, any references to them in the compilation result get recorded as if those files lived in new instead ๐Ÿฆ‹. Specifying this is the same as specifying all the individual -f*-prefix-map options ๐Ÿ’œ. It's a great way to make reproducible builds that don't care about location ๐ŸŒˆ. 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 โ€” Normally, for the -f*-prefix-map options, the old prefix gets compared against the filename that would be referenced in the result using textual comparison of the prefixes ๐Ÿ“. That comparison ignores character case on case insensitive filesystems, and treats slashes and backslashes as equal on DOS based filesystems ๐Ÿซ . -fcanon-prefix-map makes those comparisons happen on canonicalized paths of old and the referenced filename โœจ.
  • -fplugin=name.so โ€” Loads the plugin code in the file name.so, which is assumed to be a shared object that the compiler will dlopen'd ๐Ÿ”Œ. The base name of the shared object file is used to identify 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, generates the matching Ada specs ๐ŸŒธ.
  • -fada-spec-parent=unit โ€” Works together with -fdump-ada-spec[-slim] above, and generates Ada specs as child units of the parent unit ๐Ÿ‘‘.
  • -fdump-go-spec=file โ€” For input files in any language, generates matching Go declarations in file ๐Ÿน. This makes "const", "type", "var", and "func" declarations, which can be a super useful way to start writing a Go interface to code written in some other language ๐Ÿซถ.
  • @file โ€” Reads command-line options from file ๐Ÿ“œ. The options read get inserted in place of the original @file option ๐Ÿฆ‹.
    • If file does not exist, or can't be read, then the option is treated literally and does not get removed ๐Ÿ™ˆ.
    • Options in file are separated by whitespace ๐Ÿ’–. A whitespace character can be included in an option by surrounding the entire option in either single or double quotes ๐ŸŽ€.
    • Any character (including a backslash) can be included by prefixing the character to be included with a backslash ๐ŸŒˆ.
    • The file may itself contain additional @file options, and any such options get processed recursively ๐Ÿ”.

โœจ 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 would for C programs (usually with the name gcc) ๐Ÿ‘‘.

However, using gcc does not add the C++ library ๐Ÿ˜ญ. That's where g++ comes in ๐Ÿ’–. It's a program that calls GCC and automatically specifies linking against the C++ library ๐Ÿ”—. It treats .c, .h and .i files as C++ source files instead of C source files, unless -x is used ๐Ÿ™ˆ. 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 specify many of the same command-line options you'd use for compiling programs in any language ๐ŸŒˆ. You can also use command-line options meaningful for C and related languages, or options that are meaningful only for C++ programs ๐Ÿ’œ.


๐Ÿ’… Options Controlling C Dialect ๐Ÿ’…

The following options control the dialect of C (or languages derived from C, such as C++, Objective-C and Objective-C++) that the compiler accepts ๐ŸŽ€:

  • -ansi, --ansi โ€” In C mode, this is the same as -std=c90. In C++ mode, it's the same as -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, plus 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 does not turn off other GNU extensions that have no meaning in ISO C90, like omitting the middle term of a "?:" expression ๐Ÿฆ‹.

On the other hand, 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 ๐ŸŒˆ. As a result, some strict-conforming programs may get rejected ๐Ÿ’€. The particular standard is used by -Wpedantic 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 ๐Ÿ‘€.

A value for this option must be provided. 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). See <https://gcc.gnu.org/projects/c-status.html> for more information ๐Ÿ”—. The names c9x and iso9899:199x are deprecated ๐Ÿ˜ฌ.
  • c11, c1x, iso9899:2011 โ€” ISO C11, the 2011 revision of the ISO C standard โœจ. This standard 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 standard 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 ๐Ÿšง. The 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 ๐Ÿšง. The 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 ๐Ÿ™ˆ.

Okay, the -std list keeps going, and bestie, these are the ones that get a little spicy ๐ŸŒถ๏ธ

  • 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. The name gnu++1z is deprecated.
  • c++20, c++2a โ€” ๐Ÿ‘‘ The 2020 ISO C++ standard plus amendments. C++20 modules support is still experimental and needs to be enabled with -fmodules option. The name c++2a is deprecated.
  • gnu++20, gnu++2a โ€” ๐Ÿ”ฅ GNU dialect of -std=c++20. This is the default for C++ code. C++20 modules support is still experimental and needs to be enabled with -fmodules option. 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 โ€” ๐Ÿ“ Dumps prototyped declarations for every function declared and/or defined in a translation unit, header files included, into the file you name. It's silently ignored in any language other than C.

Besides the declarations, the file leaves comments that tell you where each one came from (source file and line). The character right after the line number and colon tells you whether the declaration was implicit, prototyped or unprototyped: I for implicit, N for new or O for old. The character after that tells you whether it came from a declaration or a definition: C for declaration or F for definition. For function definitions, you also get a K&R-style list of arguments, followed by their declarations, all inside comments and placed after the declaration. ๐Ÿซถ

  • -fno-asm โ€” ๐Ÿ™…โ€โ™€๏ธ Stops asm, inline and typeof from being treated as keywords, so you can use those words as identifiers. You can use __asm__, __inline__ and __typeof__ instead. In C, -ansi implies -fno-asm.

In C++, inline is a standard keyword and this switch doesn't touch it. You might want -fno-gnu-keywords instead, which turns off typeof but leaves asm and inline alone. In C99 mode (-std=c99 or -std=gnu99), this switch only affects asm and typeof, since inline is a standard keyword in ISO C99. In C23 mode (-std=c23 or -std=gnu23), this switch only affects asm, since typeof is a standard keyword in ISO C23. ๐Ÿ’…

  • -fno-builtin, -fno-builtin-function โ€” ๐Ÿšซ Don't recognize built-in functions that do not begin with __builtin_ as prefix.

Normally GCC generates special code for certain built-in functions so they run more efficiently. For instance, calls to alloca can turn into single instructions that adjust the stack directly, and calls to memcpy can become inline copy loops. The result is often both smaller and faster, but the function calls don't show up as calls anymore. That means you can't set a breakpoint on them, and you can't change what the functions do by linking against a different library. ๐Ÿ™ƒ Also, once a function is recognized as built-in, GCC may use what it knows about that function to warn you about problems with calls to it, or to generate more efficient code, even if the result still contains calls to that function. For example, you get warnings with -Wformat for bad calls to printf when printf is built in and strlen is known not to modify global memory.

With -fno-builtin-function, only the built-in function is disabled. function must not begin with __builtin_. If you name a function that isn't built-in in this version of GCC, this option is ignored. There is no corresponding -fbuiltin-function option. If you want to enable built-in functions selectively when 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 โ€” ๐Ÿคทโ€โ™€๏ธ Allows conditional expressions with mismatched types in the second and third arguments. The value of such an expression is void. This option is not supported for C++.
  • -ffreestanding โ€” ๐Ÿ›ธ Asserts that compilation targets a freestanding environment. This implies -fno-builtin. A freestanding environment is one where the standard library may not exist, and program startup may not necessarily be at main. The most obvious example is an OS kernel. This is equivalent to -fno-hosted.
  • -fgimple โ€” ๐Ÿง  Enables parsing of function definitions marked with __GIMPLE. It's an experimental feature that allows unit testing of GIMPLE passes.
  • -fgnu-tm โ€” ๐Ÿงต When -fgnu-tm is specified, 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 an experimental feature whose interface may change in future versions of GCC, as the official specification changes. Heads up: not all architectures are supported for this feature.

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

โš ๏ธ The transactional memory feature is not supported with non-call exceptions (-fnon-call-exceptions).
  • -fgnu89-inline โ€” ๐Ÿ•ฐ๏ธ Tells GCC to use the traditional GNU semantics for inline functions when in C99 mode.

Using this option is roughly equivalent to adding the gnu_inline function attribute to all inline functions.

The option -fno-gnu89-inline explicitly tells GCC to use the C99 semantics for inline when in C99 or gnu99 mode (aka 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 โ€” ๐Ÿ  Asserts that compilation targets a hosted environment. This implies -fbuiltin. A hosted environment is one where the entire standard library is available, and where main has a return type of int. Examples are nearly everything except a kernel. This is equivalent to -fno-freestanding.
  • -flax-vector-conversions โ€” ๐Ÿซ  Allows implicit conversions between vectors with differing numbers of elements and/or incompatible element types. This option should not be used for new code. โŒ
  • -fms-extensions โ€” ๐ŸชŸ Accepts some non-standard constructs used in Microsoft header files.

In C++ code, this lets member names in structures be similar to previous type declarations:

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

Some cases of unnamed fields in structures and unions are only accepted with this option.

Note that this option is off for all targets except for x86 targets using ms-abi.

  • -fpermitted-flt-eval-methods=style โ€” ๐ŸŽฏ ISO/IEC TS 18661-3 defines new permissible values for FLT_EVAL_METHOD that indicate that operations and constants with a semantic type that is an interchange or extended format should be evaluated to the precision and range of that type. These new values are a superset of those permitted under C99/C11, which doesn't specify the meaning of other positive values of FLT_EVAL_METHOD. So code that conforms to C11 may not have been written expecting the possibility of the new values.

-fpermitted-flt-eval-methods decides whether the compiler should allow only the values of FLT_EVAL_METHOD specified in C99/C11, or the extended set of values specified in ISO/IEC TS 18661-3.

style is either c11 or ts-18661-3, as appropriate.

The default in a standards compliant mode (-std=c11 or similar) is -fpermitted-flt-eval-methods=c11. The default in a GNU dialect (-std=gnu11 or similar) is -fpermitted-flt-eval-methods=ts-18661-3.

The -fdeps-* options are used to extract structured dependency information for a source. This involves figuring out what resources provided by other source files will be required to compile the source, as well as what resources the source itself provides. This information can be used to add required dependencies between compilation rules of dependent sources based on their contents, rather than requiring that such information be reflected within the build tools as well. ๐Ÿงฉ

  • -fdeps-file=file โ€” ๐Ÿ“‚ Where to write structured dependency information.
  • -fdeps-format=format โ€” ๐Ÿ“ The format to use for structured dependency information. p1689r5 is the only supported format right now. Note that when this argument is specified, the output of -MF is stripped of some information (namely C++ modules) so that it doesn't use extended makefile syntax that most tools don't understand.
  • -fdeps-target=file โ€” ๐ŸŽฏ Analogous to -MT but for structured dependency information. This indicates the target that will ultimately need any required resources and provide any resources extracted from the source that may be required by other sources.
  • -fplan9-extensions โ€” ๐Ÿน Accepts some non-standard constructs used in Plan 9 code.

This enables -fms-extensions, permits passing pointers to structures with anonymous fields to functions that expect pointers to elements of the type of the field, and permits referring 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 โ€” okay so these decide if a bit-field is signed or unsigned, but only when your declaration doesn't already say signed or unsigned ๐ŸŽ€. By default a bit-field is signed, and honestly that's the consistent move because basic integer types like int are signed too โœจ.
  • -fsigned-char โ€” let char be signed, just like signed char ๐Ÿ’–. This is the same thing as -fno-unsigned-char, which is the negative form of -funsigned-char. And -fno-signed-char is the same as -funsigned-char ๐Ÿซถ.
  • -funsigned-char โ€” flip it and let char be unsigned, like unsigned char ๐Ÿฆ‹. Every machine has its own default for what char should be: either like unsigned char or like signed char ๐ŸŒˆ.
    • Ideally a portable program always uses signed char or unsigned char whenever it depends on the signedness of an object ๐Ÿ’….
    • But a ton of programs were written with 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, lets you make those programs work with the opposite default ๐Ÿ‘‘.
    • char is always a distinct type from each of signed char or 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) โ€” this controls when the trailing array of a structure counts as a flexible array member when you're accessing its elements ๐ŸŒธ. The value of level sets how strict it is ๐Ÿ’œ.
    • -fstrict-flex-arrays is the same as -fstrict-flex-arrays=3, which is the strictest ๐Ÿ”ฅ. A trailing array only counts as flexible when it's declared as a flexible array member per C99 and onwards ๐Ÿ“œ.
    • The negative form -fno-strict-flex-arrays is the same as -fstrict-flex-arrays=0, which is the least strict ๐Ÿ“. In this case, all trailing arrays of structures are treated as flexible array members ๐ŸŒˆ.
    • Levels 1 and 2 live in between 0 and 3. They exist to support older code that uses the GCC zero-length array extension ([0]) or one-element arrays ([1]) as flexible array members ๐Ÿ™ˆ. At level 1, the trailing array counts as flexible when it's declared as either [], [0], or [1] โญ. At level 2, it counts when it's declared as either [] or [0] ๐Ÿฆ‹.
    • You can control this for one specific trailing array field of a structure with the variable attribute "strict_flex_array" attribute ๐Ÿ’….
    • The -fstrict_flex_arrays option interacts with the -Wstrict-flex-arrays option ๐Ÿซถ.
  • -fsso-struct=endianness โ€” sets the default scalar storage order of structures and unions to the endianness you choose ๐ŸŽ€. The accepted values are big-endian, little-endian, and native, which is the native endianness of the target and the default ๐Ÿ’–. This option is not supported for C++ ๐Ÿ’€.
Warning: the -fsso-struct switch makes GCC generate code that is not binary compatible with code generated without it if the endianness you specified isn't the native endianness of the target ๐Ÿ˜ญ๐Ÿšจ.

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

This section is all about the command-line options that only matter for C++ programs ๐Ÿ’œ. Good news: you can use most of the GNU compiler options no matter what language your program is in ๐ŸŒธ. For example, you could 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 โญ.

Here's a list of options that are only for compiling C++ programs ๐ŸŒˆ:

  • --compile-std-module โ€” this one builds the compiled module interfaces for the <bits/stdc++.h> header unit and the std and std.compat modules before compiling any source files you explicitly pass on the command line ๐Ÿฆ‹. It's meant for building simple programs that use import std; with a single command ๐Ÿ’….
  • -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 across G++ versions 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 all the way through G++ 4.9 ๐Ÿ‘‘.
    • Version 3 fixes an error in mangling a constant address used as a template argument ๐Ÿ’–.
    • Version 4, first seen in G++ 4.5, implements a standard mangling for vector types ๐Ÿงš.
    • Version 5, first seen 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, first seen 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, first seen in G++ 4.8, treats nullptr_t as a builtin type and fixes the mangling of lambdas in default argument scope ๐Ÿ“.
    • Version 8, first seen in G++ 4.9, fixes the substitution behavior of function types with function-cv-qualifiers ๐Ÿซถ.
    • Version 9, first seen in G++ 5.2, fixes the alignment of nullptr_t โญ.
    • Version 10, first seen in G++ 6.1, adds mangling of attributes that affect type identity, like ia32 calling convention attributes (e.g. stdcall) ๐Ÿฆ‹.
    • Version 11, first seen 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, first seen 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, first seen in G++ 8.2, fixes that accidental change from version 12 ๐Ÿ’–.
    • Version 14, first seen in G++ 10, fixes the mangling of the nullptr expression ๐Ÿงš.
    • Version 15, first seen in G++ 10.3, fixes the G++ 10 ABI tag regression ๐Ÿ’….
    • Version 16, first seen in G++ 11, changes the mangling of __alignof__ so it's distinct from alignof, plus dependent operator names ๐ŸŒˆ.
    • Version 17, first seen in G++ 12, fixes the layout of classes that inherit from aggregate classes with default member initializers in C++14 and up ๐ŸŽ€.
    • Version 18, first seen in G++ 13, fixes manglings of lambdas that have additional context โœจ.
    • Version 19, first seen in G++ 14, fixes manglings of structured bindings to include ABI tags, handles cv-qualified [[no_unique_address]] members, and adds mangling of C++20 constraints on function templates ๐Ÿ”ฅ.
    • Version 20, first seen in G++ 15, fixes manglings of lambdas in static data member initializers ๐ŸŒธ.
    • Version 21, first seen in G++ 16, fixes unnecessary captures in noexcept lambdas (c++/119764), the layout of a base class with all explicitly defaulted constructors (c++/120012), and the mangling of class and array objects with implicitly zero-initialized non-trailing subobjects (c++/121231) ๐Ÿ‘‘.
    • 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 is 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, -faligned-new=alignment โ€” 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 specify how much alignment (in bytes) that function provides ๐Ÿ‘‘. But honestly, few users will need to override the default of alignof(std::max_align_t) ๐Ÿซถ.
    • This flag is enabled by default for -std=c++17 โญ.

โœจ OPTIONS โœจ (continued)

  • -fno-assume-sane-operators-new โ€” okay so, the C++ standard lets you swap out the global new, new[], delete, and delete[] operators ๐Ÿงš, but ngl most programs don't bother and just use the built-in ones. The standard also lets the compiler skip those calls when they come 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 gives you control over 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 lets most optimizations happen across those calls, and it's something the implementation-provided operators satisfy unless malloc implementation details are observable in the code or malloc hooks are used. It might not be satisfied 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 โ€” ooh, this one's giving C++20 realness ๐ŸŒธ. It enables support for char8_t, including a brand 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 the new __cpp_char8_t and __cpp_lib_char8_t feature test macros.

This option also lets you overload functions for ordinary strings 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

It also 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;
โš ๏ธ bestie, heads up: the change to the types of UTF-8 string and character literals breaks things that were fine under ISO C++11 and later. The code below 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 โ€” the vibe here is double-checking ๐Ÿ”. It makes sure the pointer returned by operator new is non-null before you try to modify the storage allocated. This check is normally unnecessary, because the C++ standard says operator new only returns 0 if it is declared throw(). In that case the compiler always checks the return value, even without this option. In all other cases, when operator new has a non-empty exception specification, memory exhaustion is signalled by throwing std::bad_alloc. See also new (nothrow) โœจ.
  • -fconcepts โ€” concepts, the main character energy of templates ๐Ÿ‘‘. This enables support for the C++ Concepts feature for constraining template arguments. With -std=c++20 and above, Concepts are part of the language standard, so -fconcepts defaults to on.

Some constructs that were allowed by the earlier C++ Extensions for Concepts Technical Specification, ISO 19217 (2015), but never made it into the standard, could additionally be enabled by -fconcepts-ts. The option -fconcepts-ts was deprecated in GCC 14 and removed in GCC 15 ๐Ÿ’€. Users are expected to convert their code to C++20 concepts.

  • -fconcepts-diagnostics-depth=n โ€” this controls how deep the error replay goes when GCC recursively diagnoses a constraint satisfaction failure ๐Ÿฆ‹. You set the maximum error replay depth with n. The default is 1.
  • -fconstexpr-depth=n โ€” this sets the maximum nested evaluation depth for C++11 constexpr functions to n. A limit is needed to detect endless recursion during constant expression evaluation. The minimum specified by the standard is 512 ๐ŸŒˆ.
  • -fconstexpr-cache-depth=n โ€” this sets the maximum level of nested evaluation depth for C++11 constexpr functions that will be cached to n. It's a heuristic that trades off compilation speed (when the cache avoids repeated calculations) against memory consumption (when the cache grows very large from highly recursive evaluations). The default is 8. Very few users are likely to want to adjust it, but if your code does heavy constexpr calculations, you might want to experiment to find which value works best for you ๐Ÿง .
  • -fconstexpr-fp-except โ€” Annex F of the C standard says IEC559 floating point exceptions hit at compile time should not stop compilation. C++ compilers have historically not followed this guidance and instead treat 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 saying a particular operation is undefined ๐Ÿ”ฅ.
constexpr float inf = 1./0.; // OK with -fconstexpr-fp-except
  • -fconstexpr-loop-limit=n โ€” this sets the maximum number of iterations for a loop in C++14 constexpr functions to n. A limit is needed to catch infinite loops during constant expression evaluation. The default is 262144 (1<<18) ๐Ÿ”.
  • -fconstexpr-ops-limit=n โ€” this caps the maximum number of operations during a single constexpr evaluation. Even when the number of iterations of a single loop is limited by the loop limit above, you can still get trouble. If there are several nested loops, each with lots of iterations that are still smaller than that limit, or if too many expressions need to be evaluated in the body of some loop (or even outside of a loop), the constexpr evaluation might take way too long ๐Ÿ˜ฉ. The default is 33554432 (1<<25).
  • -fcontracts โ€” enables support for the C++ Contracts feature, as specified in the C++26 working draft (N5003) ๐ŸŽ€. On violation of an enforced or observed contract (see -fcontract-evaluation-semantic below), a violation handler gets called. The standard library provides a default handler that emits information about the contract that failed.

You can replace the default violation handler by defining:

void
handle_contract_violation (const std::contracts::contract_violation&);
  • -fcontract-evaluation-semantic=semantic โ€” sets the semantic that contracts get evaluated with to semantic. The available evaluation modes are:
    • "ignored" โ€” the contract checks get elided, with no checking added at either compile time or runtime ๐Ÿ™ˆ.
    • "observed" โ€” the contract checks are performed at runtime. If one fails, a handler is called that lets you do things like logging or emitting runtime warnings. When the handler returns, execution of the code continues. At compile time, the checks are performed for constexpr evaluations, and in that case a diagnostic is emitted if the check fails.
    • "enforce" โ€” the contract checks are performed and the handler is called if one fails. When the handler returns, execution of the code is terminated so it can't keep going past the failed case ๐Ÿ›‘. At compile time, the checks are applied to constexpr, and if one fails, the program is ill-formed and an error is emitted.
    • "quick_enforce" โ€” the contract checks are performed at runtime and, if one fails, execution is terminated immediately, without calling any handler โšก. At compile time there's no difference between this and the "enforce" case, since no handler is invoked at compile time.
  • -fcontracts-conservative-ipa โ€” this stops inter-procedural analysis from taking action when the body of an inline function is visible in a given translation unit. It allows for the case where contract evaluation conditions are permitted to differ between translation units, which means those actions could be potentially incorrect ๐Ÿซ .
  • -fcontract-checks-outlined โ€” by default, the contract checks for pre and post conditions are expanded in-line into function bodies. This option makes a small function instead (containing the checks), which then gets called at the relevant position. That lets different criteria apply to the compilation of the checks and to the rest of the function body ๐Ÿ’….
  • -fcontract-disable-optimized-checks โ€” when pre and post condition checks are outlined (using -fcontract-checks-outlined), this option disables the optimization steps for those functions that can otherwise avoid unwanted elision of checking steps ๐Ÿ™…โ€โ™€๏ธ.
  • -fcontracts-client-check=mode โ€” this option enables insertion of checks at call sites, which is called caller-side checking ๐Ÿ“ž. The value of mode decides which contract checks get made:
    • "none" โ€” no caller or client side checking (this is the default).
    • "pre" โ€” preconditions are checked at the caller or client side.
    • "all" โ€” both pre- and post-conditions are checked at the caller or client side.
  • -fcontracts-definition-check=mode โ€” this option lets you disable callee or definition-side checks, which are otherwise enabled by default whenever the contracts feature is active ๐ŸŒธ. The variable mode has the values "on" and "off".

Okay bestie, this batch is where the C++ stuff gets spicy ๐ŸŒถ๏ธ Let's go ๐Ÿงš

  • -fcoroutines โ€” turns on support for the C++ coroutines extension โœจ. With -std=c++20 and above, coroutines are literally part of the language standard, so -fcoroutines defaults to on ๐Ÿ’–.
  • -fdiagnostics-all-candidates โ€” lets the C++ front end list every candidate during an overload resolution failure ๐Ÿ‘€. That includes the times when a deleted function is the one that got picked ๐Ÿ’€.
  • -fdump-lang-, -fdump-lang-switch, -fdump-lang-switch-options, -fdump-lang-switch-options=filename โ€” this is the C++ debug dump squad ๐Ÿฆ‹. It controls how C++-specific info gets dumped. The options and filename parts work the same way as in -fdump-tree. These switch values are the vibes you can pick:
    • all โ€” turns on everything listed below ๐ŸŒˆ.
    • class โ€” dumps the class hierarchy info ๐ŸŒธ. Virtual table info gets dumped too, unless slim is specified.
    • module โ€” dumps module info ๐Ÿงฉ. The options lineno (locations), graph (reachability), blocks (clusters), uid (serialization), alias (mergeable), asmname (Elrond), eh (mapper) & vops (macros) can add extra details.
    • raw โ€” dumps the raw internal tree data ๐Ÿ“.
    • tinst โ€” dumps the sequence of template instantiations, indented so you can see how deep the recursion goes ๐Ÿชž. The lineno option adds the source location where the instantiation got triggered. The details option also dumps the pre-instantiation substitutions, like the ones done during template argument deduction.

Lines in the .tinst dump start with I for an instantiation, S for another substitution, and R[IS] for the reopened context of a deferred instantiation ๐Ÿ“œ.

  • -fno-elide-constructors โ€” the C++ standard lets the compiler skip making a temporary that only exists to initialize another object of the same type ๐Ÿ™ˆ. This flag turns that optimization OFF and makes G++ call the copy constructor every single time ๐Ÿ˜ค. It also makes G++ call trivial member functions that would otherwise get expanded inline.

In C++17, the compiler is required to skip those temporaries, but this flag still affects trivial member functions ๐Ÿ’….

  • -fno-enforce-eh-specs โ€” skips generating the run-time code that checks for exception specification violations ๐Ÿ™…โ€โ™€๏ธ. Heads up, this breaks the C++ standard ๐Ÿšจ. It can still be handy for shrinking code size in production builds, kinda like defining NDEBUG โœจ. It does NOT give your code permission to throw exceptions that break the exception specs ๐Ÿ›‘. The compiler still optimizes based on those specs, so if an unexpected exception gets thrown, you get undefined behavior at run time ๐Ÿ’€.
  • -fextern-tls-init, -fno-extern-tls-init โ€” okay this one is a lot ๐Ÿซถ. The C++11 and OpenMP standards let thread_local and threadprivate variables have dynamic (run-time) initialization. To make that work, every use of one of these variables goes through a wrapper function that does any needed initialization. When the use and the definition live 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're sure that no use in a non-defining TU needs to trigger dynamic initialization (because the variable is statically initialized, or because a use in the defining TU runs before any uses in another TU), you can skip the 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++ front end fold calls to std::move, std::forward, std::addressof, std::to_underlying and std::as_const ๐Ÿงšโ€โ™€๏ธ. Unlike inlining, folding means no debug info gets generated for those calls. Since nobody really needs to debug these, this flag is on by default unless -fno-inline is active ๐Ÿ’….
  • -fno-gnu-keywords โ€” stops typeof from being treated as a keyword, so you can use the word as a regular identifier ๐ŸŒˆ. You can use __typeof__ as the keyword instead โœจ. This is implied by the strict ISO C++ dialects: -ansi, -std=c++98, -std=c++11, etc.
  • -fno-immediate-escalation โ€” skips immediate function escalation, which is when certain functions get promoted to consteval ๐Ÿช„. This is specified in P2564R3. Check out this 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);
}

This 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 ๐Ÿ‘‘. That means 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) ๐Ÿงฉ. But with -fno-immediate-escalation, f does NOT get promoted to consteval. 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 โœจ. You can use it in C++14 mode or later. This can change initialization from dynamic to static, plus other optimizations ๐Ÿš€.
  • -fno-implicit-templates โ€” never emits code for non-inline templates that get instantiated implicitly (aka by use) ๐Ÿ™ˆ. Only explicit instantiations get code emitted. If you use this flag, you HAVE to structure your code so it includes all the explicit instantiations you need, or you'll get undefined symbols at link time ๐Ÿ˜ญ.
  • -fno-implicit-inline-templates โ€” don't emit code for implicit instantiations of inline templates either ๐ŸŒธ. By default, inlines get handled differently so that compiles with and without optimization need the same set of explicit instantiations ๐Ÿซถ.
  • -fno-implement-inlines โ€” to save space, this skips emitting 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 get called ๐Ÿ’€.
  • -fmodules, -fno-modules โ€” enables support for C++20 modules ๐Ÿงš. -fno-modules is usually not needed, since that's the default ๐Ÿ™Œ. Even though modules are a C++20 feature, selecting that standard version doesn't implicitly turn them on yet ๐Ÿค”.
  • -fmodule-header, -fmodule-header=user, -fmodule-header=system โ€” compiles a header file into an importable header unit ๐ŸŽ€.
  • -fmodule-implicit-inline โ€” member functions defined inside their class definitions are NOT implicitly inline for modular code ๐Ÿ™ƒ. This is different from traditional C++ behavior, and there are good reasons for it ๐Ÿง . It can still cause some trouble when porting code ๐Ÿ˜ฌ. This option makes those function definitions implicitly inline. BUT it generates an ABI incompatibility, so you have to use it everywhere or nowhere ๐Ÿšจ. (Definitions outside of a named module stay implicitly inline no matter what ๐ŸŒˆ.)
  • -fno-module-lazy โ€” disables 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 an oracle that answers queries for module name to filename mappings ๐Ÿ”ฎ. If you don't specify one, the CXX_MODULE_MAPPER environment variable gets used. If that's unset too, an in-process default is provided ๐Ÿงšโ€โ™€๏ธ.
  • -fmodule-only โ€” only emits the Compiled Module Interface and skips any object file ๐Ÿ“œ.
  • -fms-extensions โ€” disables Wpedantic warnings about constructs used in MFC, like implicit int and getting a pointer to a member function through non-standard syntax ๐ŸŽ€.
  • -fnew-inheriting-ctors โ€” enables the P0136 change 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 on by default unless -fabi-version=10 or lower is specified.
  • -fnew-ttp-matching โ€” enables the P0522 resolution to Core issue 150, which covers template template parameters and default arguments ๐Ÿงฉ. This lets a template with default template arguments be used as an argument for a template template parameter that has fewer template parameters ๐ŸŒธ. This flag is on by default for -std=c++17 ๐Ÿ’–.
  • -fno-nonansi-builtins โ€” disables built-in declarations of functions that aren't required by ANSI/ISO C ๐Ÿ™…โ€โ™€๏ธ. These include ffs, alloca, _exit, index, bzero, conjf, and other related functions ๐Ÿ“.
  • -fnothrow-opt โ€” treats a throw() exception specification as if it were a noexcept specification ๐Ÿช„. This reduces or gets rid of the text size overhead compared to a function with no exception specification ๐Ÿ“‰. If the function has local variables with non-trivial destructors, the exception spec 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 kind of exception specification results in a call to terminate instead of unexpected โš ๏ธ.
  • -fno-operator-names โ€” stops the operator name keywords and, bitand, bitor, compl, not, or and xor from being treated as synonyms for keywords ๐Ÿ™…โ€โ™€๏ธ.
  • -fno-optional-diags โ€” disables 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 that has multiple meanings within a class ๐ŸŒˆ.
  • -fno-pretty-templates โ€” ๐Ÿ’… When an error mentions a function template specialization, GCC normally prints the template signature along with the template arguments and any typedefs or typenames in it. So you get something like void f(T) [with T = int] instead of void f(int), which makes it clear which template is involved โœจ. For class template specializations, GCC leaves out any template arguments that match the defaults for that template. If either of these makes errors harder to read instead of easier ๐Ÿ™ˆ, use -fno-pretty-templates to turn them off.
  • -frange-for-ext-temps โ€” ๐Ÿ” This turns on lifetime extension for temporaries in C++ range-based for loops. With -std=c++23 and newer it's already part of the language standard, so the temporaries live until the end of the loop by default ๐Ÿ’–. This flag lets you get that same behavior in earlier versions of the standard.
  • -freflection โ€” ๐Ÿงช Turns on experimental C++26 Reflection ๐Ÿฆ‹.
  • -fno-rtti โ€” ๐Ÿชถ This disables generating the info for every class with virtual functions that C++ run-time type identification relies on (the stuff behind dynamic_cast and typeid). If you don't use those features, this can save you some space ๐Ÿ’œ. Exception handling uses the same info, but G++ only generates it when it's actually needed. The dynamic_cast operator still works for casts that don't need run-time type information, which means casts to void * or to unambiguous base classes ๐ŸŒธ.
โš ๏ธ Mixing code compiled with -frtti and code compiled with -fno-rtti may not work. For example, your program might fail to link if a class compiled with -fno-rtti is used as a base for a class compiled with -frtti ๐Ÿ˜ญ.
  • -fsized-deallocation โ€” ๐Ÿงน This turns on 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 super useful for your own replacement deallocation functions, like ones that use the size of the object to make deallocation faster ๐Ÿš€. It's on by default under -std=c++14 and above. The -Wsized-deallocation flag warns you about spots that might want to add a definition ๐Ÿ‘€.

  • -fstrict-enums โ€” ๐ŸŽฏ This lets the compiler optimize based on the assumption that a value of an enumerated type can only be one of the enumeration's values. Basically, it's a value that fits in the minimum number of bits needed to represent all the enumerators, as defined by the C++ standard ๐Ÿ“. This assumption might not hold if your program uses a cast to turn an arbitrary integer into the enumerated type, so be careful ๐Ÿ’€. This option does nothing for an enumeration type with a fixed underlying type.
  • -fstrong-eval-order, -fstrong-eval-order=kind โ€” ๐Ÿ‘‘ This makes member access, array subscripting, and shift expressions evaluate left to right, while assignment evaluates right to left, just like it was adopted for C++17 ๐Ÿซถ. -fstrong-eval-order is the same as -fstrong-eval-order=all, and it's on by default with -std=c++17 or later.

-fstrong-eval-order=some only turns on the ordering for member access and shift expressions, and it's the default for C++ dialects before C++17 ๐ŸŒˆ.

-fstrong-eval-order=none is the same as -fno-strong-eval-order ๐Ÿงƒ.

  • -ftemplate-backtrace-limit=n โ€” ๐Ÿ“š Sets the maximum number of template instantiation notes shown for a single warning or error to n. The default is 10 ๐Ÿ”Ÿ.
  • -ftemplate-depth=n โ€” ๐ŸŒ€ Sets the maximum instantiation depth for template classes to n. You need a limit like this so that endless recursion during template class instantiation can be caught ๐Ÿ›‘. ANSI/ISO C++ conforming programs must not rely on a maximum depth greater than 17, which was changed to 1024 in C++11 ๐Ÿ“œ. The default is 900, because in some situations the compiler can run out of stack space before it ever hits 1024 ๐Ÿฅฒ.
  • -fno-threadsafe-statics โ€” ๐Ÿงต Skips the extra code that uses the routines from the C++ ABI for thread-safe initialization of local statics. If your code doesn't need to be thread-safe, this can shave off a bit of code size โœ‚๏ธ.
  • -fuse-cxa-atexit โ€” ๐Ÿšช Registers destructors for objects with static storage duration using the __cxa_atexit function instead of atexit. This 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 โ€” ๐Ÿ™… Stops the use of the __cxa_get_exception_ptr runtime routine. This makes std::uncaught_exception incorrect, but you need it if that runtime routine isn't available on your system ๐Ÿซ .
  • -fvisibility-inlines-hidden โ€” ๐Ÿซฅ This switch tells GCC that you won't try to compare pointers to inline functions or methods when the two functions' addresses are taken in different shared objects ๐Ÿค.

With this on, 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 they're used inside that DSO ๐Ÿฆ‹. This can make a huge difference to load and link times for a DSO, since it massively shrinks the dynamic export table when a library leans heavily on templates ๐Ÿš€.

This isn't exactly the same as marking methods as hidden directly, because it doesn't affect static variables local to the function, and it doesn't make the compiler decide the function is defined in only one shared object ๐Ÿค”.

You can give a method explicit visibility to cancel out the switch for that method. For example, if you do want to compare pointers to a particular inline method, mark it with default visibility ๐Ÿ’…. Marking the enclosing class with explicit visibility has no effect โœจ. Explicitly instantiated inline methods aren't affected by this option, since their linkage might otherwise cross a shared library boundary ๐Ÿงฑ.

  • -fvisibility-ms-compat โ€” ๐Ÿชž This flag tries to use visibility settings to make GCC's C++ linkage model compatible with Microsoft Visual Studio's. It makes these changes to GCC's linkage model:
    1. It sets the default visibility to hidden, like -fvisibility=hidden ๐Ÿ™ˆ.
    2. Types are not hidden by default, but their members are ๐Ÿซฃ.
    3. The One Definition Rule is relaxed for types without explicit visibility specifications that are defined in more than one shared object. Those declarations are allowed if they'd be allowed without this option ๐ŸŒท.

In new code, it's better to use -fvisibility=hidden and export the classes you actually want to be externally visible ๐Ÿง. Unfortunately, code might rely on the Visual Studio behavior, perhaps by accident ๐Ÿ˜ฌ.

Some consequences of these changes: static data members of the same type, with the same name, defined in different shared objects are different, so changing one doesn't change the other ๐Ÿงฉ. Also, pointers to function members defined in different shared objects may not compare equal ๐Ÿ™ƒ. When this flag is given, defining types with the same name differently is a violation of the ODR. So don't do it, bestie ๐Ÿšซ.

  • -fno-weak โ€” ๐Ÿ•ธ๏ธ Don't use weak symbol support, even if the linker provides it. By default, G++ uses weak symbols when they're available. This option only exists for testing and shouldn't be used by end users, since it results in inferior code and has no benefits ๐Ÿ™…โ€โ™€๏ธ. 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 off, those suffixes are treated as C++11 user-defined literal numeric suffixes ๐Ÿง . It's on by default for all pre-C++11 dialects and all GNU dialects: -std=c++98, -std=gnu++98, -std=gnu++11, and -std=gnu++14. It's off by default for ISO C++11 onwards (-std=c++11, ...) ๐ŸŒ™.
  • -nostdinc++ โ€” ๐Ÿšซ Doesn't search for header files in the standard directories specific to C++, but it still searches the other standard directories ๐Ÿ“‚. (This option is used when building the C++ library ๐Ÿ—๏ธ.)
  • -flang-info-include-translate, -flang-info-include-translate-not, -flang-info-include-translate=header โ€” ๐Ÿ—ฃ๏ธ These inform you about include translation events. The first one notes accepted include translations, and the second notes declined ones ๐Ÿ™…. The header form informs you about include translations relating to 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 โ€” ๐Ÿ“ฆ These tell you about Compiled Module Interface pathnames. The first notes all CMI pathnames that get read โœ…. The module form notes when a specific module's CMI is read. module can be a named module or a header-unit, which is indicated by either being a pathname containing directory separators or 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 choices: libstdc++, which is the default and the 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 option switches G++ to use headers from the library you specify, and it emits -lstdc++ or -lc++ respectively when a C++ runtime is needed for linking ๐Ÿ”—.

In addition, these warning options only have meanings for C++ programs ๐Ÿ’œ:

  • -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 about ABI tags ๐Ÿ“–.
  • -Wno-abbreviated-auto-in-template-arg โ€” ๐Ÿ™ˆ Disables the error for an auto placeholder type used within a template argument list to declare a C++20 abbreviated function template. For example:
void f(S<auto>);

Okay so this feature was proposed in the Concepts TS, but it did not make it into C++20 ๐Ÿ’…. In the actual standard, a placeholder in a parameter declaration has to show up as a decl-specifier, so anything else is an error ๐Ÿ™ˆ. If you need to chill, -fpermissive or -Wno-error=abbreviated-auto-in-template-arg can downgrade it to a warning ๐Ÿซถ.

  • -Wcomma-subscript (C++ and Objective-C++ only) โ€” Warns about a comma expression sitting inside a subscript ๐Ÿ’€. This was deprecated in C++20 and is getting removed in C++23, but a comma expression wrapped in ( ) is still totally fine โœจ. Here's the vibe:
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 exists and supports the right number and types of arguments ๐Ÿ‘‘. G++ will still accept the older syntax for code that's invalid in C++23 but used to be valid-yet-deprecated in C++20, and it gives you a pedantic warning you can turn off with -Wno-comma-subscript ๐ŸŒธ.

Enabled by default with -std=c++20 unless -Wno-deprecated, and after -std=c++23 regardless of -Wno-deprecated. Before -std=c++20, it's enabled with 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 that has no explicitly written deduction guides ๐Ÿ”. It's the "wait, did this succeed only because the compiler made up the guides?" warning, which might not be what you meant ๐Ÿ™ˆ. Some style guides only allow CTAD on types that specifically opt in, meaning types designed to support it ๐Ÿฆ‹. You can suppress this 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 totally 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 gets bound to a temporary whose lifetime has already ended ๐Ÿ˜ญ. Here's a classic:
int n = 1;
const int& r = std::max(n - 1, n + 1); // r is dangling

In that example, two temporaries get created, one for each argument, and a reference to one of them is returned ๐Ÿ’€. Both temporaries get 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 actually safe here, like std::use_facet. They take and return a reference but don't return one of their 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, and then the warning is disabled for all of its member functions โœจ.

-Wdangling-reference also warns about code like this ๐Ÿ‘‘:

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

Here 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 ๐Ÿ’€.

It does not warn 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, meaning the class is a non-union class that has a pointer data member and a trivial destructor ๐ŸŒˆ. The warning can be disabled with 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 but a non-virtual destructor ๐Ÿ™ˆ. It's unsafe to delete a derived-class instance through a base-class pointer 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 โœจ.
  • -Wdeprecated-copy-dtor (C++ and Objective-C++ only) โ€” Basically the same deal as -Wdeprecated-copy, but it also deprecates the implicit copy operations if the class has a user-provided destructor ๐Ÿ’….

  • -Wno-deprecated-enum-enum-conversion (C++ and Objective-C++ only) โ€” Turns off the warning for when the usual arithmetic conversions get applied to operands where one is an enum and the other is a different enum ๐Ÿ™…โ€โ™€๏ธ. This conversion was deprecated in C++20 ๐Ÿงƒ. 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, this warning can be enabled by -Wenum-conversion or -Wdeprecated ๐ŸŒธ.

  • -Wno-deprecated-enum-float-conversion (C++ and Objective-C++ only) โ€” Turns off the warning for when the usual arithmetic conversions get applied to operands where one is an enum and the other is a floating-point type ๐ŸŒŠ. This conversion was deprecated in C++20 ๐Ÿฅน. 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, this warning can be enabled by -Wenum-conversion or -Wdeprecated ๐ŸŽ€.

  • -Wdeprecated-literal-operator (C++ and Objective-C++ only) โ€” Warns that declaring a user-defined literal operator with a space before the suffix is deprecated ๐Ÿ“. This warning is enabled by default in C++23, or with explicit -Wdeprecated ๐ŸŒˆ. Look at the difference:
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) โ€” Warns that leaving out the comma before the varargs ... at the end of a function parameter list is deprecated ๐Ÿ™ˆ. This warning is enabled by default in C++26, or with explicit -Wdeprecated ๐Ÿ”ฎ. Here's the breakdown:
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 above, this warns if an (invalid) additional enum-base gets used in an elaborated-type-specifier ๐Ÿงฉ. That means an enum with a given underlying type and no enumerator list, used in a declaration other than just a standalone declaration of the enum ๐Ÿซ . It's enabled by default โœ…. This warning gets upgraded to an error with -pedantic-errors ๐Ÿ˜ญ.
  • -Wno-init-list-lifetime (C++ and Objective-C++ only) โ€” Turns off the warning about uses of std::initializer_list that are likely to end up with dangling pointers ๐Ÿซฃ. The underlying array for an initializer_list gets treated like a normal C++ temporary object, so it's super easy to accidentally keep a pointer to the array after its lifetime is over ๐Ÿ’€. Here are the sneaky cases:
    • 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 value returned 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 created for the right side of the assignment only lives until the end of the full-expression, so at the next statement the initializer_list 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 gets 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 โ€” Warns when a function never produces a constant expression ๐ŸงŠ. In C++20 and earlier, every constexpr function and function template has to have at least one set of function arguments, in at least one instantiation, where an invocation could be an evaluated subexpression of a core constant expression ๐Ÿ“. C++23 removed this restriction, so it's now possible to have a constexpr function or function template where no invocation satisfies the requirements of a core constant expression ๐ŸŒธ.

This one's tricky in C++20 ๐Ÿ™ˆ. It's enabled as a pedantic warning by default in C++20 and earlier, but in C++23 you can turn on -Winvalid-constexpr and it becomes an ordinary warning โœจ. Like this:

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 โœจ

Okay so this one checks that every imported macro definition is still valid once compilation finishes ๐Ÿ”. It's not on by default, because figuring that out takes extra processing. It's super useful when you're putting together sets of header-units and want your macros to stay consistent across them ๐Ÿ’œ.

๐ŸŽ€ -Wno-literal-suffix ๐ŸŽ€ (C++ and Objective-C++ only)

Bestie, this one stops the warning when a string or character literal gets 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 working, especially code that uses the formatting macros from <inttypes.h> ๐Ÿงš. 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 begin with an underscore. Those names are reserved for future standardization, so GCC is looking out for you ๐Ÿซถ. These warnings are enabled by default โœจ.

๐Ÿ’– -Wno-narrowing ๐Ÿ’– (C++ and Objective-C++ only)

For C++11 and later, narrowing conversions get diagnosed by default, because the standard says so ๐Ÿ‘‘. A narrowing conversion from a constant is an error, and a narrowing conversion from a non-constant is a warning. -Wno-narrowing quiets that diagnostic ๐Ÿ™ˆ. Just know that it doesn't change the meaning of well-formed code, since narrowing is still ill-formed in SFINAE contexts โš ๏ธ.

With -Wnarrowing in C++98, you get a warning when a narrowing conversion that C++11 prohibits shows up inside { }. For example:

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)

This one warns when a noexcept-expression evaluates to false because of a call to a function that doesn't have a non-throwing exception specification (throw() or "noexcept"), even though the compiler knows that function never throws ๐Ÿค”.

๐ŸŒˆ -Wnoexcept-type ๐ŸŒˆ (C++ and Objective-C++ only)

It's giving C++17 plot twist ๐ŸŒ€. 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)

Okay, this one is a lot, but it's important ๐Ÿ˜ญ. It 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 could bypass a non-trivial or deleted constructor or copy assignment, break const-correctness or encapsulation, or corrupt virtual table pointers. Messing with the representation of these objects can violate invariants that the class's member functions depend on ๐Ÿ’€.

For example, the memset call below is undefined, because it modifies a non-trivial class object, so it gets diagnosed:

std::string str = "abc";
memset (&str, 0, sizeof str);

The safe way to initialize or clear the storage of these types is to use the right constructor or assignment operator, if one exists ๐Ÿซถ.

-Wclass-memaccess is enabled by -Wall. If you explicitly cast the pointer to the class object to void *, or to a type that the raw memory function can safely access, the warning goes away ๐Ÿ’….

๐Ÿ“ -Wnon-virtual-dtor ๐Ÿ“ (C++ and Objective-C++ only)

This warns when a class has virtual functions and an accessible non-virtual destructor, either on the class itself or on an accessible polymorphic base class. In that situation, deleting a derived instance through a pointer to the class or its base is possible but unsafe ๐Ÿ˜ฌ. This warning is automatically enabled if -Weffc++ is specified.

Heads up: the -Wdelete-non-virtual-dtor option (enabled by -Wall) is the one you should reach for, because it warns about the unsafe cases without false positives ๐Ÿ’–.

๐ŸŒธ -Wregister ๐ŸŒธ (C++ and Objective-C++ only)

This warns on uses of the "register" storage class specifier, except when it's part of the GNU Explicit Register Variables extension ๐Ÿ”. Using "register" as a storage class specifier 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)

This warns when the order of member initializers in your code doesn't match the order they actually get executed 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 ๐Ÿ™ˆ. This warning is enabled by -Wall โœจ.

๐Ÿ”ฅ -Wno-pessimizing-move ๐Ÿ”ฅ (C++ and Objective-C++ only)

This one warns when a call to std::move prevents copy elision ๐Ÿšซ. The typical copy elision scenario is returning from a function with a class return type, where the returned expression is the name of a non-volatile automatic object that isn't a function parameter and has the same type as the function's return type. Like:

struct T {
...
};
T fn()
{
  T t;
  ...
  return std::move (t);
}

But in this example, the std::move call is the thing that stops copy elision ๐Ÿ˜ญ. This warning is enabled by -Wall ๐Ÿ’….

๐Ÿฆ‹ -Wno-redundant-move ๐Ÿฆ‹ (C++ and Objective-C++ only)

This warns about redundant calls to std::move. In other words, it's when a move 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);
}

Here, the std::move call is redundant ๐Ÿ’€. Since G++ implements Core Issue 1579, another example is:

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

In this example, copy elision isn't applicable because the type of the returned expression and the function's return type differ. Even so, G++ treats the return value as if it were designated by an rvalue ๐Ÿคฏ. This warning is enabled by -Wextra ๐ŸŒˆ.

๐ŸŒŸ -Wrange-loop-construct ๐ŸŒŸ (C++ and Objective-C++ only)

This warns when a C++ range-based for-loop creates an unnecessary copy ๐Ÿซง. That can happen when the range declaration isn't a reference but probably should be. For example:

struct S { char arr[128]; };
void fn () {
  S arr[5];
  for (const auto x : arr) { ... }
}

It won't warn when the copied type is trivially-copyable and smaller than 64 bytes ๐Ÿงš.

It also warns when a loop variable in a range-based for-loop gets initialized with a value of a different type, which results in a copy. For example:

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

In the example above, every iteration creates and destroys a temporary value of type "double", and the reference "const double &" is bound to it ๐Ÿ˜ต. This warning is enabled by -Wall โœจ.

๐Ÿ’œ -Wredundant-tags ๐Ÿ’œ (C++ and Objective-C++ only)

This 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 ambiguity ๐Ÿ™ƒ. For example:

struct foo;
struct foo *p;   // warn that keyword struct can be eliminated

In this example, there's no warning:

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)

Don't warn if a class type has a base or a field whose type uses the anonymous namespace or depends on a type with no linkage ๐Ÿ™Œ. If type A depends on type B with no linkage or internal linkage, defining A in multiple translation units is an ODR violation, because what B means is different in each one ๐Ÿ˜ฌ. 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 too. 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)

This warns about violations of the following style guidelines from Scott Meyers' Effective C++ series of books. Literally every C++ girlie should know these ๐Ÿ“š๐Ÿ‘‘:

  • 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 switches on -Wnon-virtual-dtor ๐Ÿ’…, which is one of the effective C++ recommendations, and the check gets extended to flag a missing virtual destructor in accessible non-polymorphic base classes too ๐Ÿ‘€โœจ.

Heads up though ๐Ÿ™ˆ the standard library headers don't follow all of these guidelines, so you'll get noise from them. Use grep -v to filter those warnings out ๐Ÿงน๐Ÿ’œ

  • -Wno-exceptions (C++ and Objective-C++ only) โ€” ๐Ÿ™…โ€โ™€๏ธ Turns off the warning for when one exception handler gets shadowed by another, which can be a sign your handlers are in the wrong order ๐ŸŽ€
  • -Wsfinae-incomplete (C++ and Objective-C++ only) โ€” ๐Ÿ‘€ Warns when a class is incomplete, or a function with an auto return type hasn't been deduced yet, in a spot where that causes substitution failure instead of an error, and then the class or function gets defined later in the translation unit. This is messy because template instantiations or concept checks could give different results depending on whether they happen before or after the definition ๐Ÿ˜ญ. This warning is on by default ๐ŸŒธ. -Wsfinae-incomplete=2 adds a warning at the point of substitution failure, so it's easier to track down the problems the default mode flags ๐Ÿ”.
  • -Wstrict-null-sentinel (C++ and Objective-C++ only) โ€” ๐Ÿง Warns about using an uncasted NULL as a sentinel. When you compile with only GCC, this counts as 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. Just know this isn't portable across different compilers ๐Ÿ™ƒ.
  • -Wno-non-c-typedef-for-linkage (C++ and Objective-C++ only) โ€” ๐Ÿ™… Disables the pedwarn for unnamed classes that have a typedef name for linkage purposes and contain C++-specific stuff: members, base classes, default member initializers, or lambda expressions, including those on nested member classes ๐Ÿงš
typedef struct {
  int a; // non-static data members are ok
  struct T { int b; }; // member classes too
  enum E { E1, E2, E3 }; // member enumerations as well
  int c = 42; // default member initializers are not ok
  struct U : A { int c; }; // classes with base classes are not ok
  typedef int V; // typedef is not ok
  using W = int; // using declaration is not ok
  decltype([](){}) x; // lambda expressions not ok
} S;

In all of these cases, you need to add the tag name S after the struct keyword ๐Ÿซถ

  • -Wno-non-template-friend (C++ and Objective-C++ only) โ€” ๐Ÿฆ‹ Disables warnings for non-template friend functions declared inside a template. In super old GCC versions that came before the ISO standard was implemented, something like friend int foo(int), where the friend's name is an unqualified-id, could get read as a specific specialization of a template function. This warning exists to catch those compatibility problems, and it's enabled by default ๐Ÿ’…
  • -Wold-style-cast (C++ and Objective-C++ only) โ€” ๐Ÿ‘€ Warns if you use an old-style (C-style) cast to a non-void type in a C++ program. The new-style casts (dynamic_cast, static_cast, reinterpret_cast, and const_cast) are way less vulnerable to unintended effects and much easier to search for ๐Ÿ”Žโœจ
  • -Woverloaded-virtual, -Woverloaded-virtual=n (C++ and Objective-C++ only) โ€” ๐Ÿšจ Warns when a function declaration hides virtual functions from a base class. For example:
struct A {
  virtual void f();
};

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

So the A class version of f gets hidden in B, and code like this:

B* b;
b->f();

fails to compile ๐Ÿ˜ญ

If the different signatures are on purpose and not an accident, the simplest fix is adding a using-declaration to the derived class to un-hide the base function, like using A::f; in B ๐Ÿฆ‹

The optional level suffix controls what happens when all the declarations in the derived class override virtual functions in the base class, even if not every base function is overridden ๐Ÿง

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 ever used virtually, the user might have decided the base class semantics for some of the overloads are totally fine ๐Ÿ’œ

At level 1, this case doesn't warn. At level 2, it does. -Woverloaded-virtual by itself selects level 2, and 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 into a plain pointer ๐ŸŒธ
  • -Wsign-promo (C++ and Objective-C++ only) โ€” โš ๏ธ Warns when overload resolution picks a promotion from an unsigned or enumerated type to a signed type, instead of a conversion to an unsigned type of the same size. Older versions of G++ tried to keep the unsignedness, but the standard mandates the current behavior, so no cap, that's the rule now ๐Ÿ™ƒ
  • -Wtemplates (C++ and Objective-C++ only) โ€” ๐Ÿ“œ Warns when a primary template declaration shows up. Some coding rules ban templates, so this can help enforce that rule ๐Ÿ’…. The warning is inactive inside system header files like the STL, so you can still use the STL ๐Ÿซถ. You can also still instantiate or specialize templates โœจ
  • -Wmismatched-new-delete (C++ and Objective-C++ only) โ€” ๐Ÿ’€ Warns about mismatches between calls to operator new or operator delete and the matching allocation or deallocation function. This covers operator delete being called on pointers returned from mismatched forms of operator new, or from other functions that allocate objects where operator delete isn't a suitable deallocator, plus calls to other deallocation functions on pointers returned from operator new where that deallocation function isn't suitable ๐Ÿ˜ญ

For example, the delete expression in this function gets diagnosed because it doesn't match the array form of the new expression that the pointer came from. The call to free gets diagnosed too ๐Ÿงน

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 handles mismatches involving allocation and deallocation functions other than operator new and operator delete ๐Ÿฆ‹. And -Wmismatched-new-delete is included in -Wall ๐Ÿ’œ

  • -Wmismatched-tags (C++ and Objective-C++ only) โ€” ๐ŸŽ€ Warns about declarations of structs, classes, and class templates (plus their specializations) where the class-key doesn't match the definition, or the first declaration if no definition is given. In the example below, the struct Object in the argument list of draw sets it off. To avoid it, either remove the redundant struct class-key or swap it for class so it matches the definition ๐Ÿ’…
class Object {
public:
  virtual ~Object () = 0;
};
void draw (struct Object*);

To be clear, declaring a class with the class-key struct isn't wrong, as the example above shows ๐Ÿ™‚. -Wmismatched-tags is just there to help you keep a consistent style for class declarations ๐ŸŒˆ. In code meant to be portable to Windows-based compilers, the warning helps prevent unresolved references caused by different name mangling for symbols declared with different class-keys ๐Ÿงš. You can use this option on its own or together with -Wredundant-tags ๐Ÿซถ

  • -Wmultiple-inheritance (C++ and Objective-C++ only) โ€” ๐Ÿ™… Warns when a class is defined with multiple direct base classes. Some coding rules disallow multiple inheritance, so this can help enforce that rule ๐Ÿ’–. It's inactive inside system header files like the STL, so the STL is still fair game ๐ŸŒธ. You can still define classes that indirectly use multiple inheritance โœจ
  • -Wvirtual-inheritance โ€” ๐Ÿ‘‘ Warns when a class is defined with a virtual direct base class. Some coding rules disallow multiple inheritance, so this can enforce that rule ๐Ÿ“. Inactive inside system header files like the STL, so you can still use the STL ๐Ÿซถ. You can also define classes that indirectly use virtual inheritance ๐Ÿ’œ
  • -Wno-virtual-move-assign (C++ and Objective-C++ only) โ€” โš ๏ธ Suppresses warnings about inheriting from a virtual base with a non-trivial C++11 move assignment operator. This is dangerous ๐Ÿ˜ญ because 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 to avoid moving from a moved-from object, you can disable this warning ๐Ÿ™Œ
  • -Wnamespaces โ€” ๐ŸŽ€ Warns when a namespace definition is opened. Some coding rules disallow namespaces, so this can help enforce that rule ๐Ÿ’…. Inactive inside system header files like the STL, so you can still use the STL ๐Ÿซถ. You can also use using directives and qualified names ๐ŸŒธ
  • -Wno-template-body (C++ and Objective-C++ only) โ€” ๐Ÿงš Disables diagnosing errors when parsing a template. Instead, an error only gets issued when the template is instantiated ๐Ÿฆ‹. This flag can also downgrade those errors into warnings with -Wno-error= or -fpermissive โœจ
  • -Wno-template-id-cdtor (C++ and Objective-C++ only) โ€” ๐Ÿ™ˆ Disables the warning about using a simple-template-id as the declarator-id of a constructor or destructor. That became invalid in C++20 via DR 2237 ๐Ÿ˜ญ. For example:
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 enabled by -Wc++20-compat ๐ŸŒˆ๐Ÿ’œ

  • -Wtemplate-names-tu-local โ€” okay bestie, this one warns when a template body quietly ends up exposing a translation-unit-local entity ๐Ÿซฃ Usually, pointing at a TU-local thing (like an internal linkage declaration) from 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's not counted as an exposure, so no error pops up ๐Ÿคทโ€โ™€๏ธ

This can quietly make variable or function templates unusable ๐Ÿ’€ if they reference such an entity, because other translation units that import the template will never be able to instantiate it. This warning tries to sniff out cases where that might happen ๐Ÿ•ต๏ธโ€โ™€๏ธ An explicit instantiation silences it ๐Ÿคซ

This flag is enabled by -Wextra โœจ

  • -Wno-expose-global-module-tu-local โ€” an exposure of a TU-local entity from a module interface is invalid ๐Ÿšซ because it can cause ODR violations and show up as link errors or other unexpected behaviour. The annoying part is that lots of existing libraries declare TU-local entities in their interface, and dodging those exposures can be genuinely tricky ๐Ÿ˜ฉ

As an extension, GCC lets you expose internal variables and functions that were declared in the global module fragment ๐Ÿ‘€ This warning tells you when that kind of invalid exposure happens, and you can silence it with diagnostic pragmas either right at the exposure site or at the point where the internal declaration is declared.

When combined with -Wtemplate-names-tu-local, GCC will also warn about non-exposure references to TU-local entities in template bodies ๐Ÿ“ฃ Those templates can still be instantiated in other TUs, but the exposure risks above still apply.

This warning is enabled by default, and gets upgraded to an error by -pedantic-errors ๐Ÿ”ฅ

  • -Wno-external-tu-local โ€” warn when naming a TU-local entity outside the translation unit it was declared in ๐Ÿ™…โ€โ™€๏ธ These declarations get ignored during name lookup. This can happen when doing ADL from a template declared in the same TU as the internal function:
export module M;
template <typename T> void foo(T t) {
  bar(t);
}
struct S {} s;
static void bar(S) {}  // internal linkage

// instantiating foo(s) from outside this TU can see ::bar,
// but naming it there is ill-formed.

Workaround alert ๐Ÿง  this can be fixed by making "bar" attached to the global module, using extern "C++".

This warning is enabled by default, and gets upgraded to an error by -pedantic-errors ๐Ÿ’…

  • -Wno-terminate (C++ and Objective-C++ only) โ€” turns off the warning about a throw-expression that will immediately result in a call to "terminate" ๐Ÿ™ˆ
  • -Wno-vexing-parse (C++ and Objective-C++ only) โ€” the most vexing parse ๐Ÿ˜ญ This warns when a declaration looks like a variable definition, but C++ says it has to be read as a function declaration. Iconic behaviour, honestly:
void f(double a) {
  int i();        // extern int i (void);
  int n(int(a));  // extern int n (int);
}

Another example, because once is never enough ๐Ÿ™ƒ

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 ๐Ÿ› ๏ธ for example, it can suggest 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 kinda pointless.
  • -Wvolatile (C++ and Objective-C++ only) โ€” warns about deprecated uses of the volatile qualifier ๐Ÿฅ€ This covers a bunch of vibes:
    • postfix and prefix ++ and -- expressions on volatile-qualified types
    • simple assignments where the left operand is a volatile-qualified non-class type, for their value
    • compound assignments where the left operand is a volatile-qualified non-class type
    • a volatile-qualified function return type
    • a volatile-qualified parameter type
    • structured bindings of a volatile-qualified type

This usage was deprecated in C++20, so it's giving "retire this habit" ๐Ÿ’€

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


  • -Waligned-new, -Waligned-new=[none|global|all] โ€” warn about a new-expression of a type that needs more 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, -Wplacement-new=n โ€” warn about placement new expressions with undefined behavior ๐Ÿšจ For example, the placement new below gets diagnosed because it tries to construct an array of 64 integers in a buffer that's only 64 bytes large ๐Ÿ’€
char buf [64];
new (buf) int[64];

This warning is enabled by default โœจ

  • -Wplacement-new=1 โ€” this is the default level ๐Ÿง At this level, the warning skips some strictly undefined constructs that GCC allows as extensions for compatibility with legacy code. For example, the new expression below is not diagnosed here, even though it's undefined behaviour according to the C++ standard, 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 โ€” this level 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 its size is less than the size of the object being constructed ๐Ÿ“ The previous example gets flagged here, but the flexible member array extension below slides past 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, -Wcatch-value=n (C++ and Objective-C++ only) โ€” warn about catch handlers that don't catch via reference ๐ŸŽ€ -Wcatch-value=1 (or just -Wcatch-value for short) warns about polymorphic class types caught by value. -Wcatch-value=2 warns about all class types caught by value. -Wcatch-value=3 warns about all types that aren't caught by reference. -Wcatch-value is enabled by -Wall ๐Ÿซถ
  • -Wconditionally-supported (C++ and Objective-C++ only) โ€” warn for conditionally-supported (C++11 [intro.defs]) constructs ๐Ÿงš
  • -Wno-defaulted-function-deleted (C++ and Objective-C++ only) โ€” warn 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've 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 behaviour at runtime ๐Ÿซฃ This warning is enabled by default โœ…
  • -Wextra-semi (C++, Objective-C++ only) โ€” warn about redundant semicolons ๐Ÿงต There are a few sneaky contexts where an extra semicolon can show up:
    • a semicolon after in-class function definitions. This is valid in all C++ dialects and is never a pedwarn ๐Ÿ™„
struct S {
  void foo () {};
};
  • an extra semicolon at namespace scope. This has been allowed since C++11, so it's a pedwarn in C++98 ๐Ÿ“
struct S {
};
;
  • an extra semicolon in class definitions. This 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) โ€” controls warnings when a base class is inaccessible in a class derived from it due to ambiguity ๐Ÿ•ต๏ธ This 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 the use of C++11 inheriting constructors when the base class being inherited from has a C variadic constructor ๐Ÿฆ„ This 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 usually gives meaningful results. This flag is for users who know they're writing nonportable code and who've deliberately chosen to ignore the warning about it ๐Ÿง 

The restrictions on offsetof may be relaxed in a future version of the C++ standard ๐Ÿ”ฎ

  • -Wsized-deallocation (C++ and Objective-C++ only) โ€” warns about a definition of an unsized deallocation function, like these:
void operator delete (void *) noexcept;
void operator delete[] (void *) noexcept;

โ€ฆthat shows up without a definition of the corresponding sized deallocation function ๐Ÿšฉ

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

...or vice versa ๐Ÿ™ˆ This one's switched on by -Wextra along with -fsized-deallocation ๐Ÿ’…


  • -Wsuggest-final-types โ€” Okay so this one warns about types with virtual methods that would be way cleaner if they were declared with the C++11 final specifier, or, if it's possible, declared in an anonymous namespace ๐Ÿงš It's giving devirtualization, because it lets GCC more aggressively devirtualize the polymorphic calls โœจ It hits harder with link-time optimization, since the class hierarchy graph info is more complete there ๐Ÿ’œ
  • -Wsuggest-final-methods โ€” Warns about virtual methods that would glow up if they were declared with final, or, if possible, if their type were declared in an anonymous namespace or with the final specifier ๐ŸŒธ Same deal as above: it's more effective with link-time optimization, where the class hierarchy graph is more complete. Bestie tip straight from the manual: consider 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 ๐Ÿฆ‹
  • -Wno-conversion-null (C++ and Objective-C++ only) โ€” Do not warn for conversions between NULL and non-pointer types. Just so you know, -Wconversion-null is enabled by default ๐Ÿ™„

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

(NOTE: This manual does not describe the Objective-C and Objective-C++ languages themselves.

This section is all about the command-line options that only matter for Objective-C and Objective-C++ programs ๐ŸŒˆ You can still use most of the language-independent GNU compiler options too! 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, but you can use the other options with any language GCC supports ๐Ÿ’–

Note that 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 is a list of options that are only for compiling Objective-C and Objective-C++ programs ๐Ÿ‘‘

  • -fconstant-string-class=class-name โ€” Use class-name as the name of the class to instantiate for each literal string written with the syntax "@"..."" ๐Ÿงš The default class name 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 also present, overrides the -fconstant-string-class setting and causes "@"..."" literals to be laid out as constant CoreFoundation strings. Note that -fconstant-cfstrings is an alias for the target-specific -mconstant-cfstrings equivalent ๐Ÿ“
  • -fgnu-runtime โ€” Generate object code compatible with the standard GNU Objective-C runtime. This is the default for most types of systems ๐ŸŒธ
  • -fnext-runtime โ€” Generate output compatible with the NeXT runtime. This is the default for NeXT-based systems, including Darwin / macOS ๐ŸŽ The macro "__NEXT_RUNTIME__" is predefined if (and only if) this option is used.
  • -fno-nil-receivers โ€” Assume that all Objective-C message dispatches ("[receiver message:arg]") in this translation unit make sure the receiver is not "nil". This lets the runtime use more efficient entry points ๐Ÿš€ This option is only available in conjunction with the NeXT runtime and ABI version 0 or 1.
  • -fobjc-abi-version=n โ€” Use 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 nothing is specified, the default is Version 0 on 32-bit target machines, and Version 2 on 64-bit target machines.
  • -fobjc-call-cxx-cdtors โ€” Okay, this one's a whole vibe ๐Ÿ’… For each Objective-C class, it checks if 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 non-trivial default constructors on any such instance variables, in order, and then returns self. Similarly, it checks if any instance variable is a C++ object with a non-trivial destructor, and if so, it synthesizes a special "- (void) .cxx_destruct" method, which runs all such 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, and not the ones inherited from superclasses. It's the responsibility of the Objective-C runtime to invoke all such methods in an object's inheritance hierarchy. The "- (id) .cxx_construct" methods are invoked by the runtime immediately after a new object instance is allocated, and the "- (void) .cxx_destruct" methods are invoked immediately before the runtime deallocates an object instance. As of this writing, only the NeXT runtime on Mac OS X 10.4 and later has support for invoking the "- (id) .cxx_construct" and "- (void) .cxx_destruct" methods ๐Ÿ˜ญ
  • -fobjc-direct-dispatch โ€” Allow fast jumps to the message dispatcher. On Darwin, this is accomplished via the comm page โšก
  • -fobjc-exceptions โ€” Enable 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 not in conjunction with the NeXT runtime on Mac OS X 10.2 and earlier) ๐Ÿ’€
  • -fobjc-gc โ€” Enable garbage collection (GC) in Objective-C and Objective-C++ programs. This option is only available with the NeXT runtime. The GNU runtime has a different garbage collection implementation that doesn't require special compiler flags ๐Ÿงน
  • -fobjc-nilcheck โ€” For the NeXT runtime with version 2 of the ABI, check for a nil receiver in method invocations before doing the actual method call. This is the default, and it can be disabled using -fno-objc-nilcheck. Class methods and super calls are never checked for nil in this way, no matter what this flag is set to. Currently this flag does nothing when the GNU runtime, or an older version of the NeXT runtime ABI, is used ๐Ÿ™ƒ
  • -fobjc-std=objc1 โ€” Conform 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 does not affect conformance to C/C++ standards, which is controlled by the separate C/C++ dialect option flags. When this option is used with the Objective-C or Objective-C++ compiler, any Objective-C syntax that GCC 4.0 doesn't recognize is rejected. This is useful if you need to make sure your Objective-C code can be compiled with older versions of GCC ๐Ÿงธ
  • -freplace-objc-classes โ€” Emit a special marker telling ld(1) not to statically link in the resulting object file, and allow dyld(1) to 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 with the NeXT runtime on Mac OS X 10.3 and later.
  • -fzero-link โ€” When compiling for the NeXT runtime, the compiler usually swaps calls to objc_getClass("...") (when the class name is known at compile time) for static class references that get initialized at load time, which boosts run-time performance ๐Ÿ”ฅ Specifying -fzero-link suppresses this and keeps the calls to objc_getClass("..."). That's useful in Zero-Link debugging mode, since it lets you modify individual class implementations during program execution. The GNU runtime currently always retains 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. This can cause shadowing between instance variables and other variables declared either locally inside a class method or globally with the same name ๐Ÿ˜ต Specifying -fno-local-ivars disables this behavior, which avoids variable shadowing issues ๐Ÿ’…
  • -fivar-visibility=[public|protected|private|package] โ€” Sets the default instance variable visibility to the specified option, so that instance variables declared outside the scope of any access modifier directives 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) โ€” Don't warn if a property for an Objective-C object has no assign semantics specified ๐Ÿ™Œ
  • -Wno-protocol (Objective-C and Objective-C++ only) โ€” Okay, so if a class is declared to implement a protocol, a warning gets issued for every method in the protocol that the class doesn't implement. The default behavior is to warn for every method not explicitly implemented in the class, even if the method implementation is inherited from the superclass ๐Ÿฅบ If you use -Wno-protocol, then methods inherited from the superclass are considered implemented, and no warning is issued for them ๐ŸŒท
  • -Wobjc-root-class (Objective-C and Objective-C++ only) โ€” okay so this one yells if a class interface has no superclass ๐Ÿ‘€ Most classes inherit from NSObject (or Object), for example. If you're making a class that's supposed to be a root class, you can silence the warning by marking its interface with __attribute__((objc_root_class)) ๐Ÿ’…
  • -Wselector (Objective-C and Objective-C++ only) โ€” this one flags when you've got multiple methods with different types sharing the same selector during compilation ๐Ÿ” The check runs on the method list at the very end of compilation. It also checks every selector used in a @selector(...) expression, if a matching method was found during compilation. Because all of this only happens at the final stage, these warnings won't show up if that stage never gets reached, like when there's an error during compilation or when -fsyntax-only is in use ๐Ÿ™ˆ
  • -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 try to send a message with it to a receiver of type id or Class ๐Ÿ“จ By default this flag is off, and in that case the compiler skips these warnings if the only differences are between types that share the same size and alignment. Sneaky, but chill ๐Ÿฆ‹
  • -Wundeclared-selector (Objective-C and Objective-C++ only) โ€” warns when you use a @selector(...) expression for a selector that was never declared ๐Ÿšจ 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 option checks right away as soon as it spots a @selector(...) expression, while -Wselector only checks at the final stage of compilation. It also backs up the style rule that methods and selectors need to be declared before they get used ๐Ÿ“
  • -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 how you choose which OpenMP and OpenACC offload targets get code generated for them ๐ŸŽฏ The default behavior, which is the same as -foffload=default, is to generate 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 give it ๐Ÿซถ

Offload targets are written in GCC's internal target-triplet format. If you run the compiler with -v, it'll show you the list of configured offload targets under "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 want options that only apply to certain 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 this:

-foffload-options='-fno-math-errno -ffinite-math-only' \
        -foffload-options=nvptx-none=-latomic
-foffload-options=amdgcn-amdhsa=-march=gfx906
  • -fopenacc โ€” turns on 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 actually 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 :-separated sizes, in the order gang, worker, and vector. You can leave a size out to use a target-specific default value โœจ
  • -fopenmp โ€” turns on handling of OpenMP directives like #pragma omp, [[omp::decl(...)]], [[omp::directive(...)]], and [[omp::sequence(...)]] in C/C++ ๐Ÿ’– In Fortran, it enables !$omp and the conditional compilation sentinel !$. In fixed source form Fortran, those sentinels can also start with c or *.

This option implies -pthread, so it's only supported on targets that have -pthread support ๐Ÿงต -fopenmp also 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 ๐ŸŒธ This works with #pragma omp, [[omp::directive(...)]], [[omp::sequence(...)]], and [[omp::decl(...)]] in C/C++, and with !$omp in Fortran. It also turns on the !$ conditional compilation sentinel in Fortran. In fixed source form Fortran, the sentinels can also start with c or *. Other OpenMP directives get ignored ๐Ÿ™ˆ Unless -fopenmp is also specified, the "loop" region binds to the current task region, no matter what "bind" clause you wrote.
  • -fopenmp-target-simd-clone, -fopenmp-target-simd-clone=device-type โ€” on top of generating SIMD clones for functions marked with "declare simd", GCC also makes clones for functions marked with the OpenMP "declare target" directive that are good for vectorization when this option is on ๐Ÿฆ‹ The device-type can be one of "none", "host", "nohost", or "any". These match the keywords for the "device_type" clause of "declare target", 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 about the output device's aspect (like its width, and so on) ๐Ÿ™„ You can use the options below to control the formatting algorithm, like how many characters go on a line, or 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 shows up on a single line. This is the default for all front ends.

Note: this option also affects the display of the #error and #warning preprocessor directives, and the deprecated function/type/variable attribute. It does not affect the pragma GCC warning and pragma GCC error pragmas โš ๏ธ

  • -fdiagnostics-plain-output โ€” makes diagnostic output look as plain as possible ๐Ÿงผ This is 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 the same as these options:
-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
-fno-diagnostics-show-nesting

If GCC ever changes the default look of its diagnostics in the future, the matching option to turn off the new behavior will be 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 keep getting stuck on 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 put the same source location info (as a prefix) on every physical line that comes out of breaking up a message that's too long to fit on one line ๐Ÿ“
  • -fdiagnostics-color[=WHEN], -fno-diagnostics-color โ€” uses color in diagnostics ๐ŸŽจ WHEN can be 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 in the environment, and auto otherwise. auto makes GCC use color only when standard error is a terminal and it's not running in an emacs shell ๐Ÿง  The forms -fdiagnostics-color and -fno-diagnostics-color are aliases for -fdiagnostics-color=always and -fdiagnostics-color=never, respectively.

The colors get defined by the GCC_COLORS environment variable ๐ŸŒธ Its value is a colon-separated list of capabilities and Select Graphic Rendition (SGR) substrings. SGR commands get interpreted by the terminal or terminal emulator (check the section in your text terminal's documentation for the permitted values and what they mean as character attributes). These substring values are decimal integers and can be concatenated with semicolons. Common values to concatenate 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 modes 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 modes background colors ๐Ÿซง

The default GCC_COLORS is

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

okay so here's your little decoder ring ๐Ÿ’… 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 tweak, bestie โœจ

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

  • -fdiagnostics-urls[=WHEN] โ€” embeds URL escape sequences in diagnostics. For example, when -fdiagnostics-show-option shows the command-line option controlling a diagnostic, it embeds a URL pointing to the docs for that option ๐Ÿ”—
    • WHEN is never, always, or auto. auto makes GCC use URL escape sequences only when standard error is a terminal, and 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, and it can be any of the above WHEN options.
    • GCC can also be configured with --with-diagnostics-urls=auto-if-env so the default is affected by environment variables. Under that setup, GCC defaults to auto if either GCC_URLS or TERM_URLS is present and non-empty in the compiler's environment, or never if neither is.
    • Even with -fdiagnostics-urls=always, the behavior still depends on those environment variables:
      • If GCC_URLS is set to empty or no, URLs are not embedded in diagnostics.
      • If set to st, URLs use ST escape sequences.
      • If set to bel, which is the default, URLs use BEL escape sequences.
      • Any other non-empty value enables the feature.
      • If GCC_URLS is not set, TERM_URLS is used as a fallback.
    • Note: ST is an ANSI escape sequence with the string terminator ESC \. BEL is an ASCII character, CTRL-G, that usually sounds like a beep ๐Ÿ””
    • GCC tries to detect a few terminals that don't implement the URL feature, or that had bugs in versions still in use, where URL escapes are likely to misbehave. That list is currently xfce4-terminal, certain known buggy gnome-terminal versions, the linux console, and mingw. This check can be skipped with -fdiagnostics-urls=always.
bestie, on those glitchy terminals the URL escapes can print garbage all over the screen ๐Ÿ˜ญ that's why GCC tries to dodge them!
  • -fno-diagnostics-show-option โ€” by default, each diagnostic includes text showing the command-line option that directly controls it (if that option is known to the diagnostic machinery). This flag 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 ๐Ÿ™…. The source line is truncated to n characters if the -fmessage-length=n option is given. When the output goes to the terminal, the width is limited to the width given by the COLUMNS environment variable or, if that isn't set, the terminal width ๐Ÿ“
  • -fno-diagnostics-show-labels โ€” by default, when source code is printed (via -fdiagnostics-show-caret), diagnostics can label ranges of source code with pertinent info, like the types of expressions. Here's the vibe:
                       printf ("foo %s bar", long_i + long_j);
                                    ~^       ~~~~~~~~~~~~~~~
                                     |              |
                                     char *         long int

This option suppresses the labels, so in the example above, the vertical bars and the "char *" and "long int" text are gone ๐Ÿ‘‹

  • -fno-diagnostics-show-event-links โ€” by default, when execution paths are printed (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 have 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 this info is present, it gets printed with the diagnostic. This option suppresses that metadata ๐Ÿ›ก๏ธ
  • -fno-diagnostics-show-rules โ€” diagnostic messages can optionally have rules attached, like ones 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 that 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 message:
                   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, plus the "int" in the quoted source, are colored using highlight-a (bold green by default) โœ…
  • the "const char *" in the message and in the quoted source are both colored using highlight-b (bold blue by default) ๐Ÿ’™

The vibe is to draw your eyes to how the different parts of the message and the source relate, using color to group related stuff and distinguish mismatched stuff ๐Ÿ‘€ This extra colorization is enabled by default if color printing is on (per -fdiagnostics-color=), but you can turn it off separately with -fno-diagnostics-show-highlight-colors ๐ŸŽจ

  • -fno-diagnostics-show-line-numbers โ€” by default, when source code is printed (via -fdiagnostics-show-caret), a left margin showing line numbers is printed. This option suppresses that left margin ๐Ÿ”ข
  • -fdiagnostics-minimum-margin-width=width โ€” controls the minimum width of the left margin printed by -fdiagnostics-show-line-numbers. It defaults to 6 ๐Ÿ“
  • -fdiagnostics-show-context[=depth] and -fno-diagnostics-show-context โ€” with this option, the compiler might print the interesting control-flow chain that guards the basic block of the statement with the warning. depth is the maximum depth of that chain. The warning options currently affected are -Warray-bounds, -Wstringop-overflow, -Wstringop-overread, -Wstringop-truncation, and -Wrestrict. More warning options might join the list in future releases ๐Ÿ” The forms -fdiagnostics-show-context and -fno-diagnostics-show-context are aliases for -fdiagnostics-show-context=1 and -fdiagnostics-show-context=0, respectively
  • -fdiagnostics-parseable-fixits โ€” emits fix-it hints in a machine-parseable format that IDEs can gobble up ๐Ÿค– For each fix-it, a line gets printed after the relevant diagnostic, starting with the string fix-it:. For example:
                   fix-it:"test.c":{45:3-45:21}:"gtk_widget_show_all"

the location is a half-open range ๐ŸŽ€ counted in bytes, and byte counting starts at 1 for the initial column (very precise, no notes). In the example below, bytes 3 through 20 of line 45 of "test.c" get swapped out for the new string ๐Ÿฆ‹:

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

The filename and replacement string escape a backslash as "\\", a tab as "\t", a newline as "\n", a double quote as "\"", and non-printable characters as octal, so a vertical tab shows up as "\013" ๐Ÿ’…

An empty replacement string means the range gets deleted ๐Ÿ—‘๏ธ, and an empty range like "45:3-45:3" means the string gets inserted right at that spot โœจ

โœจ -fdiagnostics-generate-patch โœจ

Prints the fix-it hints to stderr in unified diff format, after all the diagnostics have been printed ๐ŸŒธ Here's what that looks like:

                   --- 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 ๐ŸŒˆ, and it follows the same rules as regular diagnostics (see -fdiagnostics-color).

โœจ -fdiagnostics-show-template-tree โœจ

This one is for the C++ frontend ๐Ÿ’œ When diagnostics show mismatching template types, like this:

                     could not convert 'std::map<int, std::vector<double> >()'
                       from 'map<[...],vector<double>>' to 'map<[...],vector<float>>

passing -fdiagnostics-show-template-tree turns on a tree-like structure that shows the common and differing parts of the types, like this:

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

The parts that differ get highlighted with color, which is "double" and "float" in this case ๐Ÿ”ฅ

โœจ -fno-elide-type โœจ

By default, when the C++ frontend prints mismatching template types, the common parts get squished down to [...] so the error message stays short and sweet ๐Ÿ“ For example:

                     could not convert 'std::map<int, std::vector<double> >()'
                       from 'map<[...],vector<double>>' to 'map<[...],vector<float>>

Adding -fno-elide-type turns that squishing off ๐Ÿ™…โ€โ™€๏ธ This flag also affects the output of -fdiagnostics-show-template-tree.

โœจ -fdiagnostics-path-format=KIND โœจ

This one decides how the control-flow event paths get printed for diagnostics that have one attached ๐Ÿฆ‹

KIND can be none, separate-events, or inline-events, and the default is inline-events ๐Ÿ‘‘

  • none โ€” no diagnostic paths get printed at all ๐Ÿ™ˆ
  • separate-events โ€” each event in the diagnostic gets its own "note" diagnostic. For example:
                   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 โ€” the events get printed "inline" right inside the source code ๐Ÿงš It tries to group the events into runs of close-together events and prints them as labelled ranges in the source. For example, those same events might look like this:
                     '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 gets shown by grouping the events by stack frame, with indentation showing how stack 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 option adds extra info when printing the control-flow paths attached to a diagnostic ๐Ÿ”

If you pass it, the stack depth gets printed for each run of events within -fdiagnostics-path-format=inline-events. If you pass it together with -fdiagnostics-path-format=separate-events, then the stack depth and function declaration get tacked onto the end of each event ๐Ÿ“Œ

This one is intended for GCC developers and plugin developers ๐Ÿง‘โ€๐Ÿ’ป who are debugging diagnostics that report interprocedural control flow.

โœจ -fno-show-column โœจ

Don't print column numbers in diagnostics ๐Ÿ™…โ€โ™€๏ธ This might be necessary if your diagnostics are being scanned by a program that doesn't understand column numbers, such as dejagnu.

โœจ -fdiagnostics-column-unit=UNIT โœจ

Picks the units used for the column number ๐Ÿ“ This affects traditional diagnostics (unless you've also passed -fno-show-column).

UNIT can be:

  • display โ€” the default ๐Ÿ’– It counts the number of display columns each character takes up. That can be more than the bytes needed to encode the character (tabs, for example) or fewer (multibyte characters, for example). For instance, GREEK SMALL LETTER PI (U+03C0) takes up one display column but needs two bytes in UTF-8, and SLIGHTLY SMILING FACE (U+1F642) takes up two display columns but needs four bytes in UTF-8.
  • byte โ€” the column number becomes the raw byte count in every case, which is how GCC used to output it before version 11.1.0 ๐Ÿ•ฐ๏ธ

โœจ -fdiagnostics-column-origin=ORIGIN โœจ

Picks the origin for column numbers, meaning the column number given to the very first column ๐ŸŽฏ The default value of 1 matches traditional GCC behavior and the GNU style guide. Some utilities may work better with an origin of 0, and any non-negative value is allowed.

โœจ -fdiagnostics-escape-format=FORMAT โœจ

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 issues with Unicode normalization. Those diagnostics get flagged so GCC escapes any bytes that aren't printable ASCII when it prints their source lines.

This option controls how those bytes get escaped ๐Ÿซถ

FORMAT can be:

  • unicode โ€” the default โœจ Unicode characters that aren't printable ASCII show up as <U+XXXX>, and bytes that don't form a validly UTF-8-encoded Unicode character show up as hexadecimal in the form <XX>.

For example, say a source line contains before, then the Unicode character U+03C0 (GREEK SMALL LETTER PI, which is UTF-8 encoded as 0xCF 0x80), then the stray UTF-8 trailing byte 0xBF, then after. With the default, that diagnostic prints as:

before<U+03C0><BF>after
  • bytes โ€” shows every non-printable-ASCII byte as <XX>, so you can see the underlying encoding of non-ASCII Unicode characters ๐Ÿ”Ž For the example above, this prints:
before<CF><80><BF>after

โœจ -fdiagnostics-text-art-charset=CHARSET โœจ

Some diagnostics can include "text art" diagrams ๐ŸŽจ: visualizations made out of text and meant to be viewed in a monospaced font.

This option picks which characters get used to print those diagrams, if any ๐Ÿ–ผ๏ธ CHARSET can be none, ascii, unicode, or emoji.

  • none โ€” suppresses the printing of these diagrams entirely ๐Ÿ™ˆ
  • ascii โ€” makes sure the diagrams are pure ASCII ("ASCII art") ๐Ÿงธ
  • unicode โ€” allows conservative use of Unicode drawing characters, like box-drawing characters ๐Ÿ“ฆ
  • emoji โ€” adds the option of emoji too, such as emitting 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, in which case the default is ascii ๐ŸŒธ

  • -fno-diagnostics-show-nesting โ€” okay so some diagnostics are nested, like a little tree ๐ŸŒณ of sub-diagnostics, and it's usually when C++ templates get instantiated ๐Ÿ’€. By default GCC makes these look cute with indentation and bullet points โœจ, moves the location info onto its own lines so the structure is easier to read, and cuts out repeated info ๐Ÿงน. Passing -fno-diagnostics-show-nesting turns off all that indenting, reformatting, and eliding and brings back the older "look" of the diagnostics ๐Ÿ•ฐ๏ธ.
  • -fno-diagnostics-show-nesting-locations โ€” when fdiagnostics-show-nesting is on, file names and line and column numbers get their own lines apart from the messages ๐Ÿ“. You can hide that location info completely with -fno-diagnostics-show-nesting-locations. This one is for the GCC devs writing DejaGnu test cases ๐Ÿงช, so it's not really for us, bestie ๐Ÿซถ.
  • -fdiagnostics-show-nesting-levels โ€” when fdiagnostics-show-nesting is on, this also prints numbers showing how deep each nesting level goes ๐Ÿ”ข. It's a debugging thing for GCC devs who are digging into nested diagnostics, but plugin authors might find it useful too ๐Ÿงš.
  • -fdiagnostics-format=FORMAT โ€” pick a different way to print diagnostics ๐ŸŽ€. FORMAT can be text, sarif-stderr, or sarif-file. Using this replaces any extra "output sinks" added by -fdiagnostics-add-output= or set by -fdiagnostics-set-output= ๐Ÿ™…โ€โ™€๏ธ. The default is text ๐Ÿ’–. Both sarif-stderr and sarif-file spit out diagnostics in SARIF Version 2.1.0 format ๐Ÿฆ‹, either to stderr or to a file called source.sarif, respectively.
  • -fdiagnostics-add-output=DIAGNOSTICS-OUTPUT-SPEC โ€” add an extra "output sink" for diagnostics ๐Ÿšฐ. The spec starts with a scheme, optionally followed by : and one or more KEY=VALUE pairs, looking like this:
<SCHEME>
<SCHEME>:<KEY>=<VALUE>
<SCHEME>:<KEY>=<VALUE>,<KEY2>=<VALUE2>

etc. โœจ

Heads up ๐Ÿšจ: schemes, keys, or values with a name starting with "experimental" might change or get removed without notice ๐Ÿ™ˆ. Keys can belong to one specific scheme or be GCC-wide ๐ŸŒˆ. Here are the schemes you can use:

๐Ÿ–ฅ๏ธ text

Emits diagnostics to stderr using GCC's classic text output format ๐Ÿ“Ÿ. These keys are supported for text:

  • color=[yes|no] โ€” overrides the colour settings from -fdiagnostics-color for this text output ๐ŸŽจ.
  • show-nesting=[yes|no] โ€” turns on a mode that shows the hierarchy within diagnostics, same as -fdiagnostics-show-nesting ๐ŸŒณ. Defaults to "yes".
  • show-nesting-locations=[yes|no] โ€” if show-nesting=yes, locations are shown by default ๐Ÿ“. Set this to "no" to hide them. Again, this is for GCC devs writing DejaGnu test cases ๐Ÿงช.
  • show-nesting-levels=[yes|no] โ€” a debugging option that works with show-nesting=yes ๐Ÿ”. Set it to "yes" to print explicit nesting levels in the output. Also GCC-dev territory ๐Ÿ‘ฉโ€๐Ÿ’ป.

๐Ÿ“ฆ sarif

Emits diagnostics to a file in SARIF format ๐Ÿ—‚๏ธ. These keys are supported for sarif:

  • file=FILENAME โ€” the name of the file the SARIF output gets written to, which can include a leading absolute or relative path ๐Ÿ“. If you don't specify it, it defaults to source.sarif ๐Ÿ’….
  • serialization=[json] โ€” the serialization format for writing the SARIF out ๐Ÿงพ. Right now it can only be "json", but it's there as an extension point for trying out other serializations ๐Ÿ”ง.
  • version=[2.1|2.2-prerelease] โ€” the SARIF version for the output ๐Ÿท๏ธ. If you don't pick one, it defaults to 2.1. "2.2-prerelease" uses an unofficial draft of the future SARIF 2.2 spec, so it should only be used for experimenting in this release ๐Ÿงช.

There's also this key meant for GCC devs, not end-users, and it can change or disappear without notice ๐Ÿ‘€:

  • state-graphs=[yes|no] โ€” a debugging feature that defaults to "no" ๐Ÿ›. If you set "yes", GCC tries to capture detailed state info from -fanalyzer in the generated SARIF ๐Ÿ”ฌ.

๐ŸŒ experimental-html

Emits diagnostics to a file in HTML format ๐Ÿ’ป. This scheme is experimental and may go away in future GCC releases ๐Ÿซฃ. The keys and output details can change too. These keys are supported for experimental-html:

  • css=[yes|no] โ€” adds an embedded <style> to the generated HTML ๐Ÿ’…. Defaults to yes.
  • file=FILENAME โ€” the name of the file the HTML output gets written to, which can include a leading absolute or relative path ๐Ÿ“. If you don't specify it, it defaults to source.html ๐ŸŒธ.
  • javascript=[yes|no] โ€” adds an embedded <script> to the generated HTML, giving you a barebones UI for viewing results ๐Ÿ–ฑ๏ธ. Defaults to yes.

These keys are also for GCC devs and not end-users, and they can change or be removed without notice ๐Ÿ™ƒ:

  • show-state-diagrams=[yes|no] โ€” a debugging feature that defaults to "no" ๐Ÿž. If you set "yes", GCC tries to use dot to make SVG diagrams in the HTML, showing the state at each event in a diagnostic path ๐Ÿ—บ๏ธ. You view them by pressing "j" and "k" to step forward and backward through events โฌ†๏ธโฌ‡๏ธ. Turning this on will slow down HTML generation ๐ŸŒ.
  • show-graph-dot-src=[yes|no] โ€” another debugging feature that defaults to "no" ๐Ÿ”ฌ. If you set show-graph-dot-src=yes and show-state-diagrams=yes, the state diagrams also show the .dot source input to GraphViz used for the diagram ๐Ÿ“œ.
  • show-graph-sarif=[yes|no] โ€” a debugging feature that defaults to "no" ๐Ÿง . If you set show-graph-sarif=yes and show-state-diagrams=yes, the state diagrams also show a SARIF representation of the state ๐Ÿงฉ.

Besides the scheme-specific keys, this GCC-related key works on sinks of any scheme ๐ŸŒˆ:

  • cfgs=[yes|no] โ€” if you set cfgs=yes for a sink, GCC tries to send info about the control flow graphs for the functions it's compiling ๐Ÿ•ธ๏ธ. Text sinks ignore this info. SARIF sinks add the graphs within theRun.graphs ๐Ÿ“Š. HTML sinks generate SVG showing the graphs ๐Ÿ–ผ๏ธ. The exact form of the info can change without notice โš ๏ธ.

Here's a cute 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

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

And here's the EBNF, for the nerds ๐Ÿค“:

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

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

<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 sink ๐Ÿ”„, it replaces all the existing output sinks ๐Ÿ’ฅ. That includes sinks from -fdiagnostics-format=, -fdiagnostics-add-output=, or a previous -fdiagnostics-set-output= call ๐Ÿงน.
  • -fno-diagnostics-json-formatting โ€” by default, when JSON is emitted for diagnostics (via -fdiagnostics-format=sarif-stderr or -fdiagnostics-format=sarif-file), GCC adds newlines and indentation so the JSON structure is easier to see ๐Ÿ‘€. Use -fno-diagnostics-json-formatting to suppress that whitespace ๐Ÿซง. It must be passed before the option it's supposed to affect โš ๏ธ. This is meant for compatibility with tools that don't expect newlines in the output, like the output from older GCC releases ๐Ÿ•ฐ๏ธ.

โœจ Options to Request or Suppress Warnings โœจ

Warnings are diagnostic messages that report constructions that aren't inherently wrong but are risky or suggest there might have been an error โš ๏ธ. The language-independent options below don't turn on specific warnings ๐Ÿ™…โ€โ™€๏ธ. Instead, they control the kinds of diagnostics GCC produces ๐ŸŽ›๏ธ.

  • -fsyntax-only โ€” checks the code for syntax errors and doesn't do anything beyond that ๐Ÿ”.
  • -fmax-errors=n โ€” limits the maximum number of error messages to n ๐Ÿงฑ. Once it hits that number, GCC bails out instead of trying to keep processing the source code ๐Ÿƒโ€โ™€๏ธ. If n is 0 (the default), there's no limit on how many error messages get produced โ™พ๏ธ. If -Wfatal-errors is also specified, -Wfatal-errors takes precedence over this option ๐Ÿ‘‘.
  • -w, --no-warnings โ€” inhibits all warning messages ๐Ÿ”‡.
  • -Werror โ€” turns all warnings into errors ๐Ÿ’ฅ.
  • -Werror= โ€” turns the specified warning into an error ๐ŸŽฏ. You append the warning's specifier, so 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= like described above ๐Ÿ”. (Printing 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 does not imply anything ๐Ÿ™…โ€โ™€๏ธ.
  • -Wfatal-errors โ€” okay bestie, this one is a whole mood ๐Ÿ’€ It makes the compiler stop dead at the very first error it finds, instead of pushing on and spamming you with more error messages.

You can ask for loads of specific warnings with options that start with -W, like -Wunused-variable to get warnings about declarations of variables that never get used ๐Ÿ‘€ Every one of these also has a negative form starting with -Wno- that turns the warning off, so -Wno-unused-variable is the opposite. This manual only lists one of the two forms, whichever one isn't the default ๐ŸŒธ For more language-specific stuff, check out C++ Dialect Options and Objective-C and Objective-C++ Dialect Options. Extra warnings can also come from turning on the static analyzer;

Some options, like -Wall and -Wextra, switch on other options, like -Wunused, which might switch on even more, like -Wunused-variable. Positive and negative forms combine so that more specific options beat less specific ones, no matter where they sit on the command line. For options that are equally specific, the last one wins โœจ Options turned on or off with pragmas act as if they were at the very end of the command line ๐Ÿงš

When you request an unrecognized warning (e.g., -Wunknown-warning), GCC throws an error saying the option isn't recognized. The -Wno- form is chiller, though: no diagnostic gets produced for -Wno-unknown-warning unless other diagnostics are being produced anyway. That lets you use new -Wno- options with old compilers, but if something goes sideways, 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 won't be issued at all unless optimization is on. Optimization in general makes warnings about control and data-flow problems better, but in some cases it can also cause false positives ๐Ÿซถ

  • -Wpedantic, -pedantic, --pedantic โ€” issue all the warnings strict ISO C and ISO C++ demand, diagnose every program that uses forbidden extensions, and some other programs that don't follow ISO C and ISO C++ either. This follows whichever version of the ISO C or C++ standard you pick with any -std option ๐Ÿ“š

Valid ISO C and ISO C++ programs should compile properly with or without this option (though a rare few need -ansi or a -std option naming the version of the standard). Without this option, 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 warnings for use of the alternate keywords whose names begin and end with __. That alternate format can also 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 can be disabled separately by their specific option, e.g. -Wpedantic -Wno-pointer-sign.

Where the standard given with -std is a GNU extended dialect of C, such as gnu90 or gnu99, there's a corresponding base standard: the version of ISO C that the GNU extended dialect is based on. Warnings from -Wpedantic are given where the base standard requires them. (It wouldn't make sense for these warnings to only cover features outside the specified GNU C dialect, since by definition the GNU dialects include every feature the compiler supports with the given option, so there'd be nothing to warn about ๐Ÿคท)

  • -pedantic-errors, --pedantic-errors โ€” give an error whenever the base standard (see -Wpedantic) requires a diagnostic, in some cases where there's undefined behavior at compile time, and in some other cases that don't prevent compilation of programs that are valid according to the standard. ๐Ÿšจ

This is not equivalent to -Werror=pedantic: the latter is unlikely to be useful, because it only turns errors of the diagnostics controlled by -Wpedantic into errors. This option also affects required diagnostics that are always enabled or controlled by options other than -Wpedantic.

If you want the required diagnostics that are warnings by default to be errors instead, but don't also want to enable the -Wpedantic diagnostics, you can specify -pedantic-errors -Wno-pedantic (or -pedantic-errors -Wno-error=pedantic to enable them but only as warnings).

Some required diagnostics are errors by default, but can be reduced to warnings using -fpermissive or their specific warning option, e.g. -Wno-error=narrowing.

Some diagnostics for non-ISO practices are controlled by specific warning options other than -Wpedantic, but are also made errors by -pedantic-errors. For instance: -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 โ€” downgrades some required diagnostics about nonconformant code from errors to warnings, so it lets some nonconforming code compile. ๐Ÿ˜Œ Some C++ diagnostics are controlled only by this flag, but it also downgrades some C and C++ diagnostics that have their own flag: -Wabbreviated-auto-in-template-arg (C++ and Objective-C++ only), -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)

The -fpermissive option is the default for the historic C language modes (-std=c89, -std=gnu89, -std=c90, -std=gnu90) ๐Ÿฆ‹

  • -Wall, --all-warnings โ€” this enables all the warnings about constructions that some users consider questionable and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros. It also enables some language-specific warnings described in C++ Dialect Options and Objective-C and Objective-C++ Dialect Options ๐ŸŽ€

-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

Note that some warning flags are not implied by -Wall. Some of them warn about constructions that users generally don't consider questionable, but which you might occasionally want to check for. Others warn about constructions that are necessary or hard to avoid in some cases, and there's no simple way to modify the code to suppress the warning. Some of them are enabled by -Wextra, but many must be enabled individually ๐Ÿง

  • -Wextra, --extra-warnings โ€” enables some extra warning flags that aren't enabled by -Wall. (This option used to be called -W. The older name is still supported, but the newer name is more descriptive ๐Ÿ’œ)

okay so the warning squad is still coming in hot ๐Ÿ”ฅ here's the rest of the -W lineup, and yes, I'm obsessed ๐Ÿ’…

  • -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) ๐Ÿ’€
  • -Wmultiple-parameter-fwd-decl-lists (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 (C/ObjC only) ๐Ÿ˜ญ
  • -Wunused-parameter (only with -Wunused or -Wall) ๐Ÿ“
  • -Wunused-but-set-parameter (only with -Wunused or -Wall) ๐ŸŒˆ

ngl -Wextra gets a bonus round ๐Ÿ‘‘ it also flags these cases:

  • A pointer is compared against integer zero with <, <=, >, or >= ๐Ÿ™ˆ
  • (C++ only) An enumerator and a non-enumerator both appear 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 is not initialized in the copy constructor of a derived class โœจ

๐ŸŽ€ -Wabi (C, Objective-C, C++ and Objective-C++ only)

okay so -Wabi is the one that yells when your code gets hit by ABI changes ๐Ÿ˜ญ That includes code that might not be compatible with the vendor-neutral C++ ABI, plus the psABI for your particular target. The psABI warnings have their own switch, -Wpsabi, and -Wabi implies it ๐Ÿซถ

Since G++ now defaults to updating the ABI with each major release, -Wabi usually only warns about C++ ABI compatibility problems when a check got added later in a release series for an ABI issue found after the initial release ๐Ÿ’… But if you pick an older ABI version with -fabi-version=n, -Wabi gets way more chatty ๐Ÿ™ˆ

You can also give -Wabi an explicit version number, like -Wabi=2, to check C++ ABI compatibility against a particular -fabi-version level. So -Wabi=2 warns about changes relative to -fabi-version=2 ๐Ÿฆ‹

If you give an explicit version number and don't set -fabi-compat-version, that number is used for compatibility aliases. If you skip the number on this option but DO set -fabi-compat-version, that version is used for C++ ABI warnings instead ๐ŸŒธ

Real talk: they tried to catch every case ๐Ÿฅบ but there are probably some sneaky ones that slip through even when G++ is generating incompatible code. And sometimes you'll get warnings even though the generated code is totally compatible. Not ideal, but that's life ๐Ÿ’€

If you're worried that G++ output might not be binary compatible with code from other compilers, bestie, you should 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 was 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 does not allow for overloading of functions taking vectors of different sizes ๐Ÿ™ƒ

The mangling was changed in -fabi-version=4 โœ…

  • __attribute ((const)) and noreturn were mangled as type qualifiers, and decltype of a plain declaration was folded away ๐Ÿ’€

These mangling issues were fixed in -fabi-version=5 โœ…

  • Scoped enumerators passed as arguments to a variadic function are promoted like unscoped enumerators, causing va_arg to complain. On most targets this does not actually affect the parameter passing ABI, as there is no way to pass an argument smaller than int ๐Ÿค“

Also, the ABI changed the mangling of template argument packs, const_cast, static_cast, prefix increment/decrement, and a class scope function used as a template argument.

These issues were corrected in -fabi-version=6 โœ…

  • Lambdas in default argument scope were mangled incorrectly, and the ABI changed the mangling of nullptr_t ๐Ÿ™ˆ

These issues were corrected in -fabi-version=7 โœ…

  • When mangling a function type with function-cv-qualifiers, the un-qualified function type was incorrectly treated as a substitution candidate ๐Ÿคฏ

This was fixed in -fabi-version=8, the default for GCC 5.1 โœ…

  • decltype(nullptr) incorrectly had an alignment of 1, leading to unaligned accesses. Note that this did not affect the ABI of a function with a nullptr_t parameter, as parameters have a minimum alignment ๐Ÿซ 

This was fixed in -fabi-version=9, the default for GCC 5.2 โœ…

  • Target-specific attributes that affect the identity of a type, such as ia32 calling conventions on a function type (stdcall, regparm, etc.), did not affect the mangled name, leading to name collisions when function pointers were used as template arguments ๐Ÿ’ฅ

This was fixed in -fabi-version=10, the default for GCC 6.1 โœ…


๐Ÿฆ‹ -Wpsabi (C, Objective-C, C++ and Objective-C++ only)

-Wpsabi enables warnings about processor-specific ABI changes, like changes in alignment requirements or how function arguments get passed ๐Ÿงš On several targets, including AArch64, ARM, x86, MIPS, RS6000/PowerPC, and S/390, these details have changed between different GCC versions and/or different versions of the C or C++ language standards, in ways that affect binary compatibility of compiled code ๐Ÿ˜ตโ€๐Ÿ’ซ With -Wpsabi, GCC can detect potentially incompatible usages and warn you about them ๐Ÿ’–

-Wpsabi is enabled by default, and is also implied by -Wabi โœจ


๐Ÿ’œ -Wno-changes-meaning (C++ and Objective-C++ only)

C++ requires that unqualified uses of a name within a class have the same meaning in the complete scope of the class, so declaring the name after using 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 the two declarations have the same type, while the B2 case is an error. Both diagnostics can be disabled with -Wno-changes-meaning. Alternately, the error case can be reduced to a warning with -Wno-error=changes-meaning or -fpermissive ๐Ÿ”ง

Both diagnostics are also suppressed by -fms-extensions ๐ŸŒธ


๐Ÿ“ -Wchar-subscripts

This warns if an array subscript has type char ๐Ÿ˜ญ It's a super common mistake because people forget that char is signed on some machines. This warning is enabled by -Wall โœ…


๐ŸŒˆ -Wno-coverage-mismatch

This warns if feedback profiles don't match when you use the -fprofile-use option. If a source file gets changed between compiling with -fprofile-generate and with -fprofile-use, the files with the profile feedback can fail to match the source file, and then GCC can't use the profile feedback information ๐Ÿฅบ By default, this warning is enabled and is treated as an error. -Wno-coverage-mismatch can disable the warning, or -Wno-error=coverage-mismatch can disable the error ๐Ÿ™ƒ

Disabling the error for this warning can result in poorly optimized code. It's only useful for very minor changes, like bug fixes to an existing code-base. Completely disabling the warning is not recommended ๐Ÿšซ

๐ŸŽ€ -Wno-coverage-too-many-conditions

This warns if -fcondition-coverage is used and an expression has too many terms, so GCC gives up on coverage. Coverage gets given up when there are more terms in the conditional than there are bits in a gcov_type_unsigned ๐Ÿคฏ This warning is enabled by default โœ…


๐Ÿงš -Wno-coverage-too-many-paths

This warns if -fpath-coverage is used and a function has too many paths, so GCC gives up on coverage. Giving up is controlled by -fpath-coverage-limit. This warning is enabled by default โœ…


๐Ÿ’– -Wno-coverage-invalid-line-number

This warns in case a function ends earlier than it begins because of invalid linenum macros ๐Ÿซ  The warning is only emitted with --coverage enabled.

By default, this warning is enabled and is treated as an error. -Wno-coverage-invalid-line-number can disable the warning, or -Wno-error=coverage-invalid-line-number can disable the error ๐Ÿ’…


๐ŸŒธ -Wno-cpp (C, Objective-C, C++, Objective-C++ and Fortran only)

This suppresses warning messages emitted by #warning directives ๐Ÿคซ


๐Ÿ‘‘ -Wdouble-promotion (C, C++, Objective-C and Objective-C++ only)

Okay this one is so sneaky ๐Ÿ˜ญ It gives a warning when a value of type float is 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 computations with double values is much more expensive because of the overhead required for software emulation ๐Ÿข

It's super easy to accidentally do computations with double, because floating-point literals are implicitly of type double. For example, in:

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

the compiler performs the entire computation with double because the floating-point literal is a double ๐Ÿ™ˆ


๐Ÿซถ -Wduplicate-decl-specifier (C and Objective-C only)

This warns if a declaration has a duplicate const, volatile, restrict or _Atomic specifier ๐Ÿ“ This warning is enabled by -Wall โœจ

  • -Wformat, -Wformat=n โ€” okay so this one is the format police ๐Ÿ‘ฎโ€โ™€๏ธโœจ It checks your calls to printf and scanf (and their crew) to make sure the arguments you pass actually match the types the format string asks for, and that the conversions in the format string make sense. That includes the standard functions plus anything tagged with format attributes, across the printf, scanf, strftime and strfmon (an X/Open extension, not in the C standard) families, or other target-specific families. Which functions get checked without a format attribute depends on the standard version you picked, and those checks get turned off by -ffreestanding or -fno-builtin. ๐Ÿซถ

The formats get checked against the format features supported by GNU libc version 2.2. That covers all ISO C90 and C99 features, plus stuff from the Single Unix Specification and some BSD and GNU extensions. Other library implementations might not support all of these, and GCC won't warn you about features that go beyond a particular library's limits. ๐Ÿคทโ€โ™€๏ธ BUT if you use -Wpedantic together with -Wformat, you'll get warnings about format features that aren't in the selected standard version (just not for strfmon formats, since those aren't in any version of the C standard).

  • -Wformat=1, -Wformat โ€” -Wformat is the same thing 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. Some bits of this level can be switched off with -Wno-format-contains-nul, -Wno-format-diag, -Wno-format-extra-args, and -Wno-format-zero-length. And yes, -Wformat is enabled by -Wall ๐ŸŒธ
  • -Wformat=2 โ€” level up! โญ This turns on -Wformat plus additional format checks. Right now it's the same as -Wformat -Wformat-nonliteral -Wformat-security -Wformat-y2k. Iconic behaviour, no notes.
  • -Wno-format-contains-nul โ€” if you've got -Wformat on, this tells GCC to chill and not warn about format strings that contain NUL bytes. ๐Ÿ™ˆ
  • -Wno-format-diag โ€” if -Wformat is on, this stops GCC from warning about format strings that aren't suitable for GCC's own diagnostics. ๐Ÿฆ‹
  • -Wno-format-extra-args โ€” if -Wformat is on, GCC won't yell about excess arguments to a printf or scanf format function. The C standard says those extra arguments just get ignored, so it's not really a crime ๐Ÿ™ƒ

Where the unused arguments sit between used arguments that use $ operand number specifications, GCC will normally still warn you anyway. The reason is that the implementation can't know what type to hand to va_arg to skip the unused ones. HOWEVER, for scanf formats, this option does suppress the warning if the unused arguments are all pointers, because the Single Unix Specification says those unused pointer arguments are allowed. โœจ

  • -Wformat-overflow, -Wformat-overflow=level โ€” okay this one is giving buffer-safety vibes ๐Ÿงš It warns about calls to formatted I/O functions like sprintf and vsprintf that might overflow the destination buffer. When GCC can't work out exactly how many bytes a format directive writes at compile time, it guesses using heuristics that depend on the level and on optimization. Turning on optimization usually makes the warning more accurate, but it can also cause false positives. ๐Ÿ˜ญ
    • -Wformat-overflow=1, -Wformat-overflow โ€” level 1 is enabled by -Wformat and is the cautious one. It only warns about calls that will 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 by the directive's precision or by a finite set of string literals, are assumed to take whatever value in that range produces the most output bytes. 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 dodge the warning, though it might not be enough to dodge the actual overflow. ๐Ÿ‘€
void f (int a, int b)
{
  char buf [13];
  sprintf (buf, "a = %i, b = %i\n", a, b);
}
  • -Wformat-overflow=2 โ€” level 2 warns about calls that might overflow the destination buffer given an argument of sufficient length or magnitude. ๐Ÿ”ฅ At level 2, unknown numeric arguments are assumed to have the minimum representable value for signed types with a precision greater than 1, and the maximum representable value otherwise. Unknown string arguments that can't be assumed to be bounded by the directive's precision, by a finite set of string literals they might evaluate to, or by the character array they might point to, are assumed to be 1 character long. At this level, the example above gets diagnosed again, but this time because with a equal to a 32-bit INT_MIN, the first %i directive will write some of its digits past the end of the destination buffer. To make the call safe no matter what the two variables are, the destination buffer needs to be at least 34 bytes. GCC even includes the minimum buffer size in an informational note after the warning. Helpful, bestie ๐Ÿ’–

An alternative to making the buffer bigger is to constrain the formatted values. You can bound the maximum length of string arguments by specifying the precision in the format directive. And when numeric arguments can be assumed to be bounded by less than the precision of their type, picking an appropriate length modifier for the specifier will shrink the required buffer size. For example, if a and b in the example above can be assumed to be within the precision of the short int type, then using either the %hi format directive or casting the argument to short brings the maximum required buffer 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 -Wformat is specified, GCC won't warn about zero-length formats. The C standard says zero-length formats are allowed, so this one is valid, no cap ๐Ÿงธ
  • -Wformat-nonliteral โ€” if -Wformat is specified, GCC will also warn if the format string isn't a string literal, since that means it can't be checked. The exception is when the format function takes its format arguments as a va_list. ๐ŸŒˆ
  • -Wformat-security โ€” if -Wformat is specified, this also warns about uses of format functions that could be security problems. Right now it warns about calls to printf and scanf functions where the format string isn't a string literal and there are no format arguments, like printf (foo);. That can be a security hole if the format string came from untrusted input and contains %n. ๐Ÿšจ (This is currently a subset of what -Wformat-nonliteral warns about, but future warnings might be added to -Wformat-security that aren't included in -Wformat-nonliteral.)
  • -Wformat-signedness โ€” if -Wformat is specified, this also warns if the format string needs an unsigned argument and you pass a signed one, and vice versa. ๐Ÿฆข
  • -Wformat-truncation, -Wformat-truncation=level โ€” this one is the truncation watcher ๐Ÿ‘‘ It warns about calls to formatted I/O functions like snprintf and vsnprintf that might cut off the output. Same deal as -Wformat-overflow: if the exact byte count can't be known at compile time, GCC estimates it with heuristics that depend on the level and on optimization. Optimization usually helps accuracy, but might also cause false positives. Unless noted otherwise, it uses the same logic as -Wformat-overflow.
    • -Wformat-truncation=1, -Wformat-truncation โ€” level 1 is enabled by -Wformat and is the careful version. It only warns about calls to bounded functions whose return value is unused and that will most likely result in truncated output. ๐Ÿ’…
    • -Wformat-truncation=2 โ€” level 2 also warns about calls to bounded functions whose return value is used and that might truncate the output, given an argument of sufficient length or magnitude. ๐ŸŒธ
  • -Wformat-y2k โ€” if -Wformat is specified, this also warns about strftime formats that may only yield a two-digit year. Y2K energy, and not the good kind ๐Ÿ’€
  • -Wnonnull โ€” warns about passing a null pointer for arguments that the nonnull function attribute says must be non-null. ๐Ÿ™…โ€โ™€๏ธ -Wnonnull is 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. ๐Ÿคจ -Wnonnull-compare is included in -Wall, and it can be disabled with -Wno-nonnull-compare.
  • -Wnull-dereference โ€” warns if the compiler detects paths that trigger erroneous or undefined behavior because a null pointer gets dereferenced. โš ๏ธ This option only works when -fdelete-null-pointer-checks is active, which optimizations turn on for most targets. How precise the warnings are depends on the optimization options you use.
  • -Wno-musttail-local-addr โ€” don't warn 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 this is the "I know what I'm doing" switch ๐Ÿฆ‹
  • -Wmaybe-musttail-local-addr โ€” warns when the address of a local variable can escape to a musttail call, unless the variable already goes out of scope before the musttail call. Main-character warning for your pointers ๐ŸŽ€
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)

okay so this one is all about the Named Return Value Optimization (aka NRVO) ๐Ÿ’… It warns when the compiler doesn't skip copying a local variable into the return value, even though [class.copy.elision] says it's allowed to. The compiler could do it, it just didn't, and this flag lets you know. ๐Ÿซถ

Here's the example. The compiler can't skip the copy from both v1 and v2, so it skips 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, and it's giving main character energy because it works at all optimization levels ๐Ÿ”ฅ Heads up though, catching infinite recursion between two or more functions needs optimization turned on. -Winfinite-recursion is included in -Wall. ๐Ÿ‘‘

Compare this with -Wanalyzer-infinite-recursion, which gives a similar diagnostic but is implemented in a different way (as part of -fanalyzer). ๐Ÿงš

๐Ÿฆ‹ -Winit-self (C, C++, Objective-C and Objective-C++ only)

This one warns about uninitialized variables that get initialized with themselves ๐Ÿ™ˆ Note that this option can only be used with the -Wuninitialized option.

GCC warns about "i" being uninitialized in the following snippet only when -Winit-self has been specified:

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

This warning is enabled by -Wall in C++. ๐Ÿ’œ

๐Ÿ’… -Wno-implicit-int (C and Objective-C only)

This option controls warnings when a declaration doesn't specify a type. It's enabled by default, as an error, in C99 and later dialects of C, and also by -Wall. You can downgrade the error to a warning with -fpermissive (along with certain other errors), or for this error alone, with -Wno-error=implicit-int. ๐Ÿซถ

This warning gets upgraded to an error by -pedantic-errors. ๐Ÿ˜ญ

๐ŸŒˆ -Wno-implicit-function-declaration (C and Objective-C only)

This option controls warnings when a function is used before it's declared. It's enabled by default, as an error, in C99 and later dialects of C, and also by -Wall. You can downgrade it to a warning with -fpermissive (along with certain other errors), or for this error alone, with -Wno-error=implicit-function-declaration. ๐Ÿ“

Yep, -pedantic-errors upgrades this one to an error too. ๐Ÿ’€

๐Ÿ“ -Wimplicit (C and Objective-C only)

Same as -Wimplicit-int and -Wimplicit-function-declaration. It's enabled by -Wall. โœจ

๐Ÿ”ฅ -Whardened

This one warns when -fhardened didn't enable an option from its set (see -fhardened for the full lineup). For example, if you use -fhardened and -fstack-protector at the same time on the command line, -Whardened will warn, because -fstack-protector-strong won't be enabled by -fhardened. ๐Ÿ›ก๏ธ

It's enabled by default, and it only has an effect when -fhardened is enabled. ๐ŸŒธ

๐Ÿงš -Wimplicit-fallthrough

-Wimplicit-fallthrough is the same as -Wimplicit-fallthrough=3, and -Wno-implicit-fallthrough is the same as -Wimplicit-fallthrough=0. ๐Ÿฆ‹

๐Ÿซง -Wimplicit-fallthrough=n

okay so this warns when a switch case falls through ๐Ÿ’– For example:

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

It does not warn when the last statement of a case can't fall through, like when there's a return statement or a call to a function declared with the noreturn attribute. ๐Ÿ™Œ

This also takes control flow statements into account, such as ifs, and it only warns when it's appropriate. E.g.

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

Sometimes a case fall through is totally intentional, so GCC gives you an attribute, __attribute__ ((fallthrough)), to be used along with a null statement to suppress the warning that would normally happen:

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

In C++17 and C23 there's a standard way to do this 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, you can also add a fallthrough comment to silence the warning. The whole body of the C or C++ style comment has to match the regular expressions listed below. The option argument n decides which kind of comments are accepted:

  • -Wimplicit-fallthrough=0 disables the warning altogether. ๐Ÿšซ
  • -Wimplicit-fallthrough=1 matches ".*" regular expression, so any comment counts as a 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, so only attributes disable the warning. ๐Ÿ™…

The comment has to be followed, after optional whitespace and other comments, by a case or default keyword, or by a user label that comes right before 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)

This 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 a function's return type has a type qualifier such as const. In ISO C, that qualifier has no effect, since the value returned by a function isn't an lvalue. In C++, the warning is only emitted for scalar types or void. ISO C prohibits qualified void return types on function definitions, so those always get 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. It's different from the -Wattributes option, because it warns whenever the compiler decides to drop an attribute, not when the attribute is unknown, used in the wrong place, and so on. 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 it's also enabled by either -Wall or -Wpedantic. ๐Ÿ’–

Upgraded to an error by -pedantic-errors. ๐Ÿ’€

๐Ÿง -Wmisleading-indentation (C and C++ only)

Warns when the indentation of the code doesn't match the block structure. Specifically, a warning is issued for if, else, while, and for clauses with a guarded statement that doesn't 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".  */

With mixed tabs and spaces, the warning uses the -ftabstop= option to figure out whether the statements line up (it defaults 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);

Okay so the warning is not issued after a #line directive ๐Ÿ™ˆ That usually means the code was autogenerated, and nobody can assume anything about the layout of the file the directive points to. Respect the vibe.

This warning is enabled by -Wall in C and C++ โœจ


  • -Wmissing-attributes โ€” bestie, this one flags a function declaration that's missing an attribute a related function was declared with, when that absence could hurt the correctness or efficiency of the generated code ๐Ÿ’… The classic example is an alias that uses attributes to ask for less strict stuff than its target. That's basically a potential optimization opportunity. The flip side is -Wattribute-alias=2, which warns when the alias is more restrictive than the target, and that one can lead to incorrect code generation ๐Ÿ˜ญ The attributes it keeps an eye on are "alloc_align", "alloc_size", "cold", "const", "hot", "leaf", "malloc", "nonnull", "noreturn", "nothrow", "pure", "returns_nonnull", and "returns_twice".

In C++, it warns when an explicit specialization of a primary template is declared without one of these attributes: "alloc_align", "alloc_size", "assume_aligned", "format", "format_arg", "malloc", or "nonnull". The attributes "deprecated", "error", and "warning" suppress the warning.. ๐Ÿ™ƒ

You can use the "copy" attribute to slap the same set of attributes onto a declaration as another declaration has, without listing them all out. It works on declarations of functions, variables, or types. Peep Common Attributes too ๐ŸŽ€

-Wmissing-attributes is enabled by -Wall.

Like, since the primary function template below uses both "malloc" and "alloc_size", the explicit specialization gets flagged because it's missing one of them ๐Ÿ‘€

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 when an aggregate or union initializer isn't fully bracketed ๐Ÿงš In this example, a is not fully bracketed, but b is, and that's the whole difference ๐Ÿ’–

int a[2][2] = { 0, 1, 2, 3 }; int b[2][2] = { { 0, 1 }, { 2, 3 } };

This warning is enabled by -Wall.

  • -Wmissing-include-dirs (C, C++, Objective-C, Objective-C++ and Fortran only) โ€” warns if a user-supplied include directory doesn't exist ๐Ÿ“‚ It's off by default for C, C++, Objective-C and Objective-C++. For Fortran, it's partially on by default, but only for -I and -J ๐ŸŒธ
  • -Wno-missing-profile โ€” this one is about feedback profiles going missing when you use -fprofile-use โœจ It calls out cases where a new function or a new file got added between -fprofile-generate and -fprofile-use without regenerating the profiles. The profile feedback data files then have no info for that new function or file. Also, if profile count data (.gcda) files get deleted, GCC can't use any profile feedback info at all. In all these cases you get a warning telling you a profile generation step is due ๐Ÿฅบ Ignoring it can leave you with poorly optimized code.

-Wno-missing-profile can turn the warning off, but that's not recommended ๐Ÿšซ Only do it when having non-existent profile data is actually justified.

  • -Wmismatched-dealloc โ€” warns on calls to deallocation functions that get pointer arguments returned from allocation functions the former isn't a suitable deallocator for ๐Ÿ”ฅ You can pair up matching allocators and deallocators with the malloc attribute. Unless -fno-builtin is in play, the standard "calloc", "malloc", "realloc", and "free" are implicitly paired up, same goes for C++ "operator new" and "operator delete" ๐Ÿซถ In the example, "mydealloc" is the deallocator for pointers 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 }

In C++, the related option -Wmismatched-new-delete catches mismatches involving either "operator new" or "operator delete" ๐Ÿ’œ

-Wmismatched-dealloc is included in -Wall.

  • -Wmultistatement-macros โ€” warns about sketchy multiple statement macros that look guarded by something like "if", "else", "for", "switch", or "while", where only the first statement actually ends up guarded once the macro expands ๐Ÿ’€

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

This will increment "y" unconditionally, not just when "c" holds ๐Ÿ˜ฑ It 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 get left out in certain spots ๐Ÿ“ Like when there's an assignment where a truth value is expected, or when operators are nested in a way people often get mixed up about.

It also warns if you write something like x<=y<=z. That's equivalent to (x<=y ? 1 : 0) <= z, which is a totally different reading from normal math notation ๐Ÿคฏ

It warns for dangerous uses of the GNU extension to ?: with the middle operand omitted. 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, so this is a major vibe check ๐Ÿšฉ

For C++, this also warns about some unnecessary parentheses in declarations, which can mean someone was trying to make 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 warns when a value is moved to itself with "std::move" ๐Ÿฆ‹ That kind of "std::move" typically does nothing at all.

struct T { ... }; void fn() { T t; ... t = std::move (t); }

This warning is enabled by -Wall.

  • -Wsequence-point โ€” warns about code that might have undefined semantics because it breaks the sequence point rules in the C and C++ standards ๐ŸŒˆ Okay this one is a lot, so bear with me ๐Ÿซ 

The standards define the order expressions get evaluated in using sequence points, which are a partial ordering between parts of the program: stuff executed before the sequence point, and stuff executed after it. They happen after a full expression is evaluated (one that isn't part of a larger expression), after the first operand of a "&&", "||", "? :" or "," (comma) operator is evaluated, before a function is called (but after its arguments and the expression naming the called function are evaluated), and in some other places too. Apart from what the sequence point rules say, the order subexpressions get evaluated in isn't specified. These rules only give a partial order, not a total one. For example, if two functions get called in one expression with no sequence point between them, the order of those calls isn't specified. However, the standards committee has ruled that function calls do not overlap ๐Ÿ™Œ

It isn't specified when, between sequence points, changes to object values take effect. Programs that depend on that 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, results on any given implementation are completely unpredictable ๐Ÿ˜ต

Examples of undefined behavior are "a = a++;", "a[n] = b[n++]" and "a[i++] = i;". Some trickier cases don't get diagnosed by this option, and it might occasionally give a false positive. Still, it's been found pretty effective at catching this kind of problem in programs โœจ

The C++17 standard defines the order of evaluation of operands in more cases. In particular, it requires the right-hand side of an assignment to be evaluated before the left-hand side, so the examples above are no longer undefined. But this option will still warn about them, so people avoid writing code that's undefined in C and 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 ๐ŸŒท Okay, you do you ๐Ÿซถ
  • -Wreturn-mismatch โ€” okay bestie, this one is all about return statements ๐Ÿ’…. It warns when a function that isn't void hits a return with no expression ๐Ÿ˜ญ. It also warns when a void function tries to return an expression, unless that expression is also void ๐Ÿ™ˆ. As a GNU extension, that last case gets a free pass with no warning unless you've got -Wpedantic turned on ๐Ÿงš.
๐Ÿ’€ Heads up: using the return value of a non-void function (other than main) that runs off the end by reaching its closing curly brace is undefined behavior. Don't do it, babe ๐Ÿซถ.

This warning is C-only and enabled by default โœจ. In C99 and later dialects, it gets promoted to an error ๐Ÿšซ. You can soften it back into a regular warning with -fpermissive (which also softens other warnings) ๐Ÿฆ‹, or just for this one with -Wno-error=return-mismatch ๐ŸŒธ.

  • -Wreturn-type โ€” this one checks a few things ๐Ÿ”. It warns about a function defined with a return type that defaults to int (unless -Wimplicit-int is active, which takes priority ๐Ÿ‘‘). It also warns if execution might reach the end of the function body, or if the function doesn't contain any return statement at all ๐Ÿ’€.
๐Ÿ’€ Same UB energy: using the return value of a non-void function (other than main) that runs off the end by reaching its closing curly brace is undefined behavior.

Unlike in C, in C++, flowing off the end of a non-void function other than main is undefined behavior even when the function's value isn't used ๐Ÿ˜ฌ. This warning is enabled by default in C++ and by -Wall otherwise ๐ŸŒˆ.

  • -Wno-shift-count-negative โ€” controls the warnings for a negative shift count ๐Ÿ™ˆ. Heads up, this one is enabled by default.
  • -Wno-shift-count-overflow โ€” controls the warnings for when a shift count is greater than or equal to the bit width of the type ๐Ÿงฎ. Enabled by default too.
  • -Wshift-negative-value โ€” warns if you left shift a negative value ๐ŸŒ€. It's enabled by -Wextra in C99 (and newer) and 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 level you get from plain -Wshift-overflow, and it's enabled by default in C99 and C++11 modes (and newer) ๐Ÿ’–. It doesn't warn about left-shifting 1 into the sign bit ๐Ÿ™ƒ. (In C, though, that overflow still gets rejected in contexts where an integer constant expression is required.) No warning shows up in C++20 mode (and newer), since signed left shifts always wrap there ๐Ÿฆ‹.
    • -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 has an index of enumerated type and is missing a case for one or more of that enum's named codes ๐Ÿ“. A default label stops this warning from firing ๐Ÿ›‘. case labels that don't match any enumerator also trigger warnings with this option, unless the enum is marked with the flag_enum attribute ๐Ÿงš. This is enabled by -Wall โœจ.
  • -Wswitch-default โ€” warns whenever a switch doesn't have a default case ๐Ÿซ .
  • -Wswitch-enum โ€” warns about the same missing-case situation as -Wswitch ๐Ÿ’…. The only difference is that this one still warns about an omitted enumeration code even if there's a default label ๐Ÿ˜ค. Like -Wswitch, case labels that don't correspond to enumerators also get warnings, unless the enum is marked with the flag_enum attribute ๐Ÿ’œ.
  • -Wno-switch-bool โ€” don't warn when a switch has an index of boolean type and the case values are outside the range of a boolean ๐Ÿ™…. 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 โ€” controls warnings when a switch case has a value outside the range of its type ๐Ÿ“. Enabled by default for C and C++ programs โœจ.
  • -Wno-switch-unreachable โ€” don't warn when a switch has statements between the controlling expression and the first case label, since those statements will never be executed ๐Ÿƒโ€โ™€๏ธ. For example:
switch (cond)
  {
   i = 15;
  ...
   case 5:
  ...
  }

-Wswitch-unreachable doesn't warn if the statement between the controlling expression and the first case label is just a declaration ๐Ÿ™Œ. For example:

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

This warning is enabled by default for C and C++ programs ๐Ÿ’–.

  • -Wsync-nand (C and C++ only) โ€” warns when the __sync_fetch_and_nand and __sync_nand_and_fetch built-in functions get used ๐Ÿšจ. Heads up, these functions changed semantics in GCC 4.4 ๐Ÿ˜ฌ.
  • -Wtrivial-auto-var-init โ€” warns when -ftrivial-auto-var-init can't initialize an automatic variable ๐Ÿซฅ. A super common case is an automatic variable declared between the controlling expression and the first case label of a switch ๐ŸŒ€.
  • -Wunused-but-set-parameter โ€” this is the same as -Wunused-but-set-parameter=3, and -Wno-unused-but-set-parameter is the same as -Wunused-but-set-parameter=0 ๐ŸŽ€.
  • -Wunused-but-set-parameter=n โ€” warns whenever a function parameter gets assigned to but is otherwise unused (aside from its declaration) ๐Ÿ‘€. To shush it, use the unused attribute ๐Ÿคซ.
    • -Wunused-but-set-parameter=0 disables the warning ๐Ÿ™….
    • -Wunused-but-set-parameter=1 means all uses except initialization and a left hand side of assignment that isn't used further will disable the warning ๐Ÿ’….
    • -Wunused-but-set-parameter=2 means that, additionally, uses of the parameter in ++ and -- operators don't count as uses ๐Ÿ™ƒ.
    • -Wunused-but-set-parameter=3 means that, additionally, uses in parm @= rhs outside of rhs don't count as uses ๐Ÿงพ. See -Wunused-but-set-variable=n for examples ๐Ÿ“š.

This -Wunused-but-set-parameter=3 warning is also enabled by -Wunused together with -Wextra โœจ.

  • -Wunused-but-set-variable โ€” this is the same as -Wunused-but-set-variable=3, and -Wno-unused-but-set-variable is the same as -Wunused-but-set-variable=0 ๐Ÿง .
  • -Wunused-but-set-variable=n โ€” warns whenever a local variable gets assigned to but is otherwise unused (aside from its declaration) ๐Ÿซฃ. This -Wunused-but-set-variable=3 warning is enabled by -Wall โœจ. To suppress it, use the unused attribute ๐Ÿคซ.
    • -Wunused-but-set-variable=0 disables the warning ๐Ÿ™….
    • -Wunused-but-set-variable=1 means all uses except initialization and a left hand side of assignment that isn't used further will disable the warning ๐Ÿ’….
    • -Wunused-but-set-variable=2 means that, additionally, uses of the variable in ++ and -- operators don't count as uses ๐Ÿ™ƒ.
    • -Wunused-but-set-variable=3 means that, additionally, uses in parm @= rhs outside of rhs don't count as uses ๐Ÿงพ.

This -Wunused-but-set-variable=3 warning is also enabled by -Wunused, which is enabled by -Wall ๐Ÿ’–. Check out this example:

void foo (void)
{
  int a = 1; // B<-Wunused-variable> warning
  int b = 0; // Warning for <n> >= 1
  b = 1; b = 2;
  int c = 0; // Warning for <n> >= 2
  ++c; c--; --c; c++;
  int d = 0; // Warning for <n> >= 3
  d += 4;
  int e = 0; // No warning, cast to void
  (void) e;
  int f = 0; // No warning, f used
  int g = f = 5;
  (void) g;
  int h = 0; // No warning, preincrement used
  int i = ++h;
  (void) i;
  int j = 0; // No warning, postdecrement used
  int k = j--;
  (void) k;
  int l = 0; // No warning, l used
  int m = l |= 2;
  (void) m;
}
  • -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 not used ๐Ÿท๏ธ. Enabled by -Wall. To suppress it, use the unused attribute ๐Ÿคซ.
  • -Wunused-local-typedefs (C, Objective-C, C++ and Objective-C++ only) โ€” warns when a typedef defined locally inside a function isn't used ๐Ÿง. Enabled by -Wall.
  • -Wunused-parameter โ€” warns whenever a function parameter is unused (aside from its declaration) ๐Ÿ‘ป. This option isn't enabled by -Wunused unless -Wextra is also specified ๐Ÿงƒ. To suppress it, use the unused attribute ๐Ÿคซ.
  • -Wno-unused-result โ€” don't warn if a caller of a function marked with the warn_unused_result attribute doesn't use 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) ๐Ÿ’œ. To suppress it, use 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 included header ๐Ÿ™ˆ. It's enabled by either -Wunused-variable or -Wunused for C, but not for C++ ๐ŸŒˆ. In C this declares variable storage, but in C++ it isn't an error, since const variables take the place of #defines ๐Ÿ’….
  • -Wunused-const-variable=2: okay so this level also warns about unused constant static variables in headers ๐Ÿ“š, just not system headers ๐Ÿ™…โ€โ™€๏ธ. It's the same as the short form -Wunused-const-variable. You have to ask for it explicitly in both C and C++ ๐Ÿ’… because cleaning up every header you've included can be genuinely hard ๐Ÿ˜ญ.
  • -Wunused-value: ngl, this one is iconic ๐Ÿ”ฅ. It warns whenever a statement computes a result that's explicitly not used ๐Ÿ’€. To shut it up, cast the unused expression to "void". That covers an expression-statement or the left-hand side of a comma expression that has no side effects. So x[i,j] gets flagged, but x[(void)i,j] is totally fine ๐Ÿ™ˆ.
    • This warning is enabled by -Wall โœจ.
  • -Wunused: all the above -Wunused options combined, except the ones that have 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 only enables -Wunused-const-variable=1 instead of -Wunused-const-variable, and only for C, not C++ ๐Ÿ™…โ€โ™€๏ธ.
  • -Wuse-after-free (C, Objective-C, C++ and Objective-C++ only) and -Wuse-after-free=n: this one warns about uses of pointers to dynamically allocated objects that were made indeterminate by a call to a deallocation function ๐Ÿฆ‹. It's enabled at all optimization levels, but the results can be different 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 a successful call to "realloc", no matter whether that call actually reallocated memory ๐ŸŒธ. This includes double-"free" calls, plus uses in arithmetic and relational expressions. Uses of indeterminate pointers in equality (or inequality) expressions are undefined, but they aren't diagnosed at this level ๐Ÿซถ.
    • -Wuse-after-free=2: at level 2, on top of unconditional uses, it also diagnoses conditional uses of pointers made indeterminate by a deallocation call ๐Ÿ‘‘. As at level 2, uses in equality (or inequality) expressions aren't diagnosed. Here's a sneaky example where the second "free" gets flagged 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 ๐Ÿ’…. Every use of an indeterminate pointer is undefined, but equality tests sometimes pop up after "realloc" calls as an attempt to figure out whether the object got moved to a new address ๐Ÿง. They get their own level so legacy code can gradually move to safer alternatives ๐ŸŒˆ. Here's the equality test that gets flagged 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 way 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 ๐Ÿ™ˆ. It does not occur when a class object is converted to a non-reference type, because that's a legit way to make a temporary โœจ:
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 non-static "const" member shows up in a class without constructors ๐ŸŽ€.
    • Passing a pointer (or, in C++, a reference) to an uninitialized object into a "const"-qualified argument of a built-in function known to read the object also gets diagnosed here ๐Ÿ”. (-Wmaybe-uninitialized handles 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 cover individual uninitialized elements of structure, union or array variables, as well as variables that are uninitialized as a whole ๐Ÿงธ. They do not occur for variables or elements declared "volatile". Since these warnings depend on optimization, exactly which variables or elements get warned about depends on the precise optimization options and the version of GCC you're using ๐ŸŒ€.
    • 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 might delete those computations before the warnings get printed ๐Ÿ—‘๏ธ.
    • In C++, this warning also catches using uninitialized objects in member-initializer-lists. GCC warns about "b" being uninitialized in this snippet ๐Ÿ‘‡:
struct A {
  int a;
  int b;
  A() : a(b) { }
};
  • -Wno-invalid-memory-model: this option controls warnings for invocations of __atomic Builtins, __sync Builtins, and the C11 atomic generic functions with a memory consistency argument that is either invalid for the operation or outside the range of values in the memory_order enumeration ๐Ÿงต. For example, __atomic_store and __atomic_store_n are only defined for the relaxed, release, and sequentially consistent memory orders, so this 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's initialized, but some other paths where it isn't, the compiler warns if it can't prove those uninitialized paths never run at runtime ๐ŸŒ™.
    • Passing a pointer (or, in C++, a reference) to an uninitialized object into a "const"-qualified function argument is also diagnosed by this warning ๐Ÿซ . (-Wuninitialized is issued for built-in functions known to read the object.) Annotating the function with attribute access (none) says the argument isn't used to access the object, and that avoids the warning ๐Ÿ™Œ.
    • These warnings are only possible in optimizing compilation, because otherwise GCC doesn't track the state of variables ๐Ÿ› ๏ธ. On the flip side, -Wmaybe-uninitialized is known to miss plenty of situations (false negatives) because optimizations like constant propagation take advantage of the undefinedness of uninitialized uses ๐Ÿคทโ€โ™€๏ธ.
    • These warnings are optional because GCC may not be able to tell when code is correct even though it looks like an error ๐Ÿฅบ. Here's one example of how that happens:
{
  int x;
  switch (y)
    {
    case 1: x = 1;
      break;
    case 2: x = 4;
      break;
    case 3: x = 5;
    }
  foo (x);
}
  • If the value of "y" is always 1, 2 or 3, then "x" is always initialized, but GCC doesn't know that ๐Ÿฅน. To suppress the warning, provide a default case with assert(0) or similar code ๐Ÿ’….
  • This option also warns when a non-volatile automatic variable might be changed by a call to "longjmp". The compiler only sees the calls to "setjmp". It can't know where "longjmp" will be called, since a signal handler could call it at any point in the code ๐Ÿ˜ต. So you might get a warning even when there's actually no problem, because "longjmp" can't really be called at the place that would cause one.
  • Some spurious warnings can be avoided if you declare all the functions you use that never return 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 this command-line option is used, warnings are even issued for unknown pragmas in system header files. That's not the case if the warnings are only enabled by the -Wall command-line option ๐Ÿšซ.
  • -Wno-pragmas: don't warn about misuses of pragmas, like incorrect parameters, invalid syntax, or conflicts between pragmas ๐Ÿ™ˆ. See also -Wunknown-pragmas ๐Ÿ”—.
  • -Wno-pragma-once-outside-header: don't warn when #pragma once is used in a file that isn't a header file, such as a main file ๐ŸŒธ.
  • -Wno-prio-ctor-dtor: don't warn if a priority from 0 to 100 is used for a constructor or destructor ๐ŸŽ€. The constructor and destructor attributes let you assign a priority to control the order of execution before main is called or after it returns ๐Ÿ—๏ธ. The priority values must be greater than 100, because the compiler reserves priority values between 0--100 for the implementation ๐Ÿ”’.
  • -Wstrict-aliasing: this one is only active when -fstrict-aliasing is active ๐ŸŒˆ. It warns about code that might break the strict aliasing rules the compiler uses for optimization โœจ. It doesn't catch every case, but it does try to catch the more common pitfalls ๐Ÿซถ. It's included in -Wall, and it's equivalent to -Wstrict-aliasing=3 ๐Ÿ’–.
  • -Wstrict-aliasing=n โ€” ๐Ÿ’– okay bestie, this one only works when -fstrict-aliasing is turned on. It checks for code that might break the strict aliasing rules the compiler uses for optimization โœจ. Higher levels are more accurate (fewer false positives), but they also take more effort, kinda like how -O works ๐Ÿงš.
    • -Wstrict-aliasing is the same thing as -Wstrict-aliasing=3 ๐ŸŽ€.
    • Level 1 is the most aggressive, quick, and least accurate option ๐Ÿ’…. It's useful when the higher levels don't warn but -fstrict-aliasing still breaks your code, since it has very few false negatives ๐Ÿ˜ญ. The catch is it has many false positives. It warns for every pointer conversion between possibly incompatible types, even if the pointer is never dereferenced. It only runs in the front end ๐Ÿ™ˆ.
    • Level 2 is aggressive and quick but not super precise ๐ŸŒธ. It can still have a lot of false positives (just not as many as level 1), and few false negatives, though maybe more than level 1. Unlike level 1, it only warns when an address is taken. It also warns about incomplete types. It runs in the front end only ๐Ÿซถ.
    • Level 3 is the default for -Wstrict-aliasing ๐Ÿ‘‘. It should have very few false positives and very 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 multiple statement cases using flow-sensitive points-to information ๐Ÿง . It only warns when the converted pointer is dereferenced, and it does not warn about incomplete types โœจ.
  • -Wstrict-overflow, -Wstrict-overflow=n โ€” ๐ŸŒˆ this one is only active when signed overflow is undefined. It warns when the compiler optimizes based on the assumption that signed overflow never happens. Heads up, it doesn't catch every spot where your code might overflow ๐Ÿ™ˆ. It only warns where the compiler actually does some optimization, so this warning depends on your optimization level ๐Ÿ’….
    • An optimization that assumes signed overflow doesn't happen is totally safe if the values never actually overflow ๐Ÿ’€. That's why this warning can easily give a false positive, meaning 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 are issued for undefined signed overflow when 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 ๐Ÿงš. Plain -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" gets simplified to "x > 0" ๐Ÿ’….
    • -Wstrict-overflow=4 also warns about other simplifications that aren't covered above ๐Ÿ”ฅ. For example: "(x * 10) / 5" gets simplified to "x * 2" ๐Ÿ‘‘.
    • -Wstrict-overflow=5 also warns about cases where the compiler reduces the magnitude of a constant in a comparison ๐Ÿ˜ญ. For example: "x + 2 > y" gets simplified to "x + 1 >= y". This is only reported at the highest warning level ๐Ÿ’€.
โš ๏ธ This simplification applies to so many comparisons that this level gives a very large number of false positives ๐Ÿ™ˆ.
  • -Wstring-compare โ€” ๐Ÿ“ this one warns for calls to "strcmp" and "strncmp" whose result is determined to be zero or non-zero in equality tests, because one argument's length is bigger than the size of the array the other argument is stored in (or the bound, for "strncmp") ๐ŸŒˆ. Those calls could be mistakes ๐Ÿ™ˆ. In the example below, the "strcmp" call gets flagged, since 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 โ€” ๐Ÿšจ this one warns about code that can be figured out at compile time 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 ๐Ÿงš. It only matters for string functions that work on character arrays. Raw memory functions like "memcpy" always use type-zero Object Size Checking ๐Ÿซถ.
    • It also warns for calls that specify a size bigger than the largest possible object, or at most "SIZE_MAX / 2" bytes ๐Ÿ’œ.
    • This option gives the best results with optimization turned on ๐Ÿ”ฅ. Even without optimization, it can catch 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". Those can do run-time checking when the access can't be proven safe at compile time ๐Ÿฆ‹.
    • In the example below, this option warns about the "strcpy" call because it copies at least 5 characters (the string "blue" plus 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
}

This option isn't only for string or memory functions, either ๐Ÿ‘‘. In the example below, a warning is issued because a 1-element array gets 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
}

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

  • -Wstringop-overflow, -Wstringop-overflow=1 โ€” this one uses type-zero Object Size Checking to figure out the sizes of destination objects ๐ŸŒˆ. At this setting, it doesn't warn about writes past the end of subobjects of larger objects accessed through pointers, unless the size of the largest surrounding object is known ๐Ÿง . When the destination could be one of several objects, it's assumed to be the largest one of them ๐Ÿซถ. On Linux systems, when optimization is enabled at this setting, the option warns about the same code that gets flagged when the _FORTIFY_SOURCE macro is defined to a non-zero value ๐Ÿฆ‹.
  • -Wstringop-overflow=2 โ€” this one uses type-one Object Size Checking to figure out the sizes of destination objects โœจ. At this setting, it warns about overflows when writing to members of the largest complete objects whose exact size is known ๐ŸŒธ. It doesn't warn about excessive writes to the same members of unknown objects referenced by pointers, since those pointers might point to arrays with unknown numbers of elements ๐Ÿ™ˆ. This is the default setting for this option ๐Ÿ‘‘.
  • -Wstringop-overflow=3 โ€” this one uses type-two Object Size Checking to figure out the sizes of destination objects ๐Ÿ’…. At this setting, it warns about overflowing the smallest object or data member ๐Ÿ˜ญ. This is the most restrictive setting that might warn about safe code too ๐Ÿ’€.
  • -Wstringop-overflow=4 โ€” this one uses type-three Object Size Checking to figure out the sizes of destination objects ๐Ÿ”ฅ. At this setting, it warns about overflowing any data members. When the destination is one of several objects, it uses the size of the largest one to decide whether to warn ๐Ÿงš. Like -Wstringop-overflow=3, this setting may result in warnings for benign code ๐ŸŽ€.
  • -Wno-stringop-overread โ€” ๐ŸŒŠ this one warns for calls to string manipulation functions like "memchr" or "strcpy" that are determined to read past the end of the source sequence ๐Ÿซถ. -Wstringop-overread is enabled by default โœจ.
  • -Wno-stringop-truncation โ€” ๐Ÿ™…โ€โ™€๏ธ this one stops the warning for calls to bounded string manipulation functions like "strncat", "strncpy", and "stpncpy" that might truncate the copied string or leave the destination unchanged ๐Ÿ’–. In the example below, the "strncat" call sets a bound that's less than the length of the source string. That means the copy gets truncated, so the call is flagged ๐Ÿ˜ญ. To avoid the warning, use "bufsize - strlen (buf) - 1)" as the bound ๐Ÿ’….
void append (char *buf, size_t bufsize)
{
  strncat (buf, ".txt", 3);
}

In another example, the "strncpy" call copies only the characters before the terminating NUL into "d", without adding the NUL to the end ๐Ÿฆ‹. Assuming the result of "strncpy" is always a NUL-terminated string is a super common mistake, so this call gets flagged ๐Ÿ™ˆ. If the result isn't expected to be NUL-terminated, call "memcpy" instead to avoid the warning ๐ŸŒธ.

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

Okay so this is the sketchy one ๐Ÿ‘€ Next up is the safe version. Here the strncpy call uses the size of the destination buffer as its bound, and if the source string is equal to or longer than that size, the copy won't be NUL-terminated. So GCC diagnoses it too ๐Ÿšฉ To dodge the warning, use sizeof buf - 1 as the bound and set the last element of the buffer to NUL. Literally one tiny fix, no cap ๐Ÿ’…

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

Now, sometimes a character array is supposed to hold a run of bytes with no terminating NUL, and that's valid! For those, you can slap the nonstring attribute on the array so this warning chills out ๐ŸงŠ BUT bestie, these arrays are not it for functions that expect NUL-terminated strings. To catch accidental misuse, GCC warns on them unless it can prove the use is safe ๐Ÿซถ

  • -Wstrict-flex-arrays (C and C++ only) โ€” this one checks for sus usage of flexible array members. It goes by the level in the strict_flex_array (level) attribute on the trailing array field of a struct, if that's available. If not, it goes by -fstrict-flex-arrays=level instead. It's only effective when level is greater than 0 โœจ
    • At level 1, you get warnings for a trailing array reference in a struct 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 warnings for a trailing one-element array reference in a struct if it's used as a flexible array member ๐Ÿฆ‹
    • At level 3, you get everything from level 2, plus warnings for a trailing zero-length array reference in a struct if it's used as a flexible array member ๐Ÿ‘‘
    • This option hits harder with -ftree-vrp on (the default for -O2 and above), but some warnings can still show up even without optimization ๐Ÿ’–
  • -Wsuggest-attribute=attribute-name โ€” warns when adding an attribute might be a glow-up ๐Ÿ’… The attribute names supported right now are:
    • -Wsuggest-attribute=pure
    • -Wsuggest-attribute=const
    • -Wsuggest-attribute=noreturn
    • -Wmissing-noreturn
    • -Wsuggest-attribute=malloc
    • -Wsuggest-attribute=returns_nonnull

These all warn about functions that might be candidates for the pure, const, noreturn, malloc, or returns_nonnull attributes. GCC only warns for functions visible in other compilation units, or (for pure and const) if it can't prove the function returns normally. A function returns normally if it doesn't have an infinite loop and doesn't return abnormally by throwing, calling abort, or trapping ๐Ÿง This analysis needs -fipa-pure-const, which is on by default at -O and higher. Higher optimization levels make the analysis more accurate โœจ

  • -Wsuggest-attribute=format and -Wmissing-format-attribute โ€” these warn about function pointers that might be candidates for format attributes. Heads up, these are only possible candidates, not guaranteed ones ๐Ÿ™ˆ GCC assumes that function pointers with format attributes, used in an assignment, initialization, parameter passing, or a return statement, should have a matching format attribute in the resulting type. So 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 format candidates, and again, that's just a maybe. GCC guesses format attributes might fit any function that calls something like vprintf or vscanf, but that's not always true, and some functions that do deserve format attributes might 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 usually only warns about functions that always lead to a call to another cold function, like wrappers around 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 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. This includes the built-in forms of aligned_alloc, alloca, calloc, malloc, and realloc. Behavior with a zero size differs between implementations (and realloc with zero has been deprecated), so relying on it can cause subtle portability bugs. Bestie, just avoid it ๐Ÿšซ
  • -Wcalloc-transposed-args โ€” warns about calls to allocation functions decorated with the alloc_size attribute that have two arguments, where the earlier size argument uses the sizeof operator and the later one doesn't. 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 using sizeof in the earlier argument but not the latter is on purpose, you can suppress the warning with 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 these warnings by specifying a byte-size of SIZE_MAX or more, or by using -Wno-alloc-size-larger-than.
  • -Wno-alloc-size-larger-than โ€” disables the -Walloc-size-larger-than= warnings. It's equivalent to -Walloc-size-larger-than=SIZE_MAX or larger ๐ŸŒธ
  • -Walloca โ€” warns on all uses of alloca in the source ๐Ÿ™ˆ
  • -Wauto-profile โ€” outputs warnings about auto-profile inconsistencies ๐Ÿ”
  • -Wcannot-profile โ€” warns when profiling instrumentation was requested but couldn't be applied to a certain function ๐Ÿฅ€
  • -Walloca-larger-than=byte-size โ€” warns on calls to alloca with an integer argument whose value is either zero, or isn't bounded by a controlling predicate that limits it to at most byte-size. It also warns for alloca calls where the bound value is unknown. Arguments of non-integer types count as unbounded, even if they look like they're in the expected range ๐Ÿง

Here's a bounded case of alloca:

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

Passing -Walloca-larger-than=1000 won't warn here, since the alloca call is known to be at most 1000 bytes. But if you passed -Walloca-larger-than=500, the compiler would warn โš ๏ธ

Unbounded uses are alloca calls with no controlling predicate constraining the integer argument. For example:

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

With -Walloca-larger-than=500, the above would trigger a warning, because there's no bounds checking ๐Ÿ’€

Heads up, even seemingly correct code with signed integers can cause a warning:

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

In the example above, n could be negative, which would cause a larger-than-expected argument to be implicitly cast into the alloca call ๐Ÿ˜ญ

This option also warns when alloca is used in a loop ๐Ÿ”

-Walloca-larger-than=PTRDIFF_MAX is enabled by default, but it's usually only effective when -ftree-vrp is active (the default for -O2 and above). See also -Wvla-larger-than=byte-size ๐Ÿฆ‹

  • -Wno-alloca-larger-than โ€” disables the -Walloca-larger-than= warnings. It's equivalent to -Walloca-larger-than=SIZE_MAX or larger โœจ
  • -Warith-conversion โ€” warns about implicit conversions from arithmetic operations, even when converting the operands to the same type can't change their values. This affects 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 and -Warray-bounds=n โ€” warn about out-of-bounds subscripts or offsets into arrays. This warning is enabled by -Wall ๐Ÿงš It's more effective when -ftree-vrp is active (the default for -O2 and above), but a subset of instances are issued even without optimization ๐Ÿ’œ

Okay so by default, the trailing array at the end of a struct gets treated as a flexible array member by -Warray-bounds or -Warray-bounds=n ๐Ÿ’…. That's true whether it's written as a C99 flexible array member ([]), 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. Lowkey wild, but it's the vibe โœจ

You can add -fstrict-flex-arrays or -fstrict-flex-arrays=level to control how this option treats a trailing struct array as a flexible array member ๐ŸŽ€

  • level<=1 โ€” no change to the default behavior ๐ŸŒธ
  • level=2 โ€” extra warnings get issued for out-of-bounds subscripts or offsets into one-element arrays ๐Ÿ”ฅ
  • level=3 โ€” in addition to level=2, extra warnings get issued 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 turned on by -Wall, so you have to ask for them explicitly ๐Ÿซถ
  • -Warray-bounds=2 โ€” this level also warns about the intermediate results of pointer arithmetic that might come out as out-of-bounds values ๐Ÿ™ˆ. It can give you a bigger pile of false positives, so it's deactivated by default ๐Ÿ’€

-Wunterminated-string-initialization (C and Objective-C only)

Okay, this one is about character arrays that get initialized as unterminated character sequences using a string literal ๐Ÿ˜ญ. It warns about that unless the declaration being initialized has the nonstring attribute. Here's the tea:

char arr[3] = "foo"; /* Warning.  */
char arr2[3] __attribute__((nonstring)) = "bar"; /* No warning.  */

This warning is enabled by -Wextra ๐ŸŒˆ. If -Wc++-compat is enabled, the warning has slightly different wording, and it warns even if the declaration has the nonstring attribute. Why? Because in C++, those initializations are an error ๐Ÿ’…


-Warray-compare

This one warns about equality and relational comparisons between two operands of array type ๐Ÿ‘€. Fun fact (not fun): that comparison got deprecated in C++20 ๐Ÿฅ€. Example:

int arr1[5];
int arr2[5];
bool same = arr1 == arr2;

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


-Warray-parameter and -Warray-parameter=n

Warn about redeclarations of functions that involve parameters of array or pointer types with inconsistent kinds or forms ๐Ÿงš. It also turns on detecting out-of-bounds accesses to those parameters through 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 ๐Ÿ“. Giving it arguments that are too small, or accessing more than that maximum, 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.

For example, the warning triggers on the second declaration of f, because the first one, with the keyword static, says the array argument must have at least four elements, while the second one allows an array of any size to be passed in ๐Ÿ˜ค

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 and -Wno-attribute-alias

This warns about declarations that use the "alias" and similar attributes whose target is incompatible with the type of the alias ๐ŸŽ€

  • -Wattribute-alias=1 โ€” this is the default level ๐Ÿ’…. It diagnoses incompatibilities between the type of the alias declaration and the type of its target. These are usually signs of bugs ๐Ÿ›
  • -Wattribute-alias=2 โ€” at this level, -Wattribute-alias also diagnoses cases where the attributes of the alias declaration are more restrictive than the attributes on its target ๐Ÿง. These mismatches can potentially lead to incorrect code generation ๐Ÿ’€. Other times they're totally benign, and you can fix them just by adding the missing attribute to the target ๐Ÿฉน. For comparison, check out -Wmissing-attributes, which controls diagnostics when the alias declaration is less restrictive than the target, rather than more restrictive ๐Ÿ‘€

Attributes considered at this level 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, which is the default ๐Ÿ’–. You can turn these warnings off with either -Wno-attribute-alias or -Wattribute-alias=0 ๐Ÿ™…โ€โ™€๏ธ


-Wbidi-chars=[none|unpaired|any|ucn]

Okay, this one is super important ๐Ÿ˜ญ. It warns about possibly misleading UTF-8 bidirectional control characters in comments, string literals, character constants, and identifiers ๐ŸŒˆ. These characters can flip left-to-right writing direction into right-to-left (and vice versa), which can cause confusion between the logical order and the visual order ๐ŸŒ€.

This can be genuinely dangerous ๐Ÿ’€. For instance, a piece of code might look like it isn't commented out, when it actually is. Or the other way around!

GCC supports three levels of this warning ๐Ÿ”ฅ

  • -Wbidi-chars=unpaired โ€” this is the default, and it 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 does not warn about UCNs (universal character names). You can totally turn on that checking, though, 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, and ObjC++, unless -Wno-bidi-chars or -Wbidi-chars=none is found ๐Ÿง

-Wbool-compare

This warns about a boolean expression being compared with an integer value that's different from "true"/"false" ๐Ÿค”. For instance, this comparison is always false:

int n = 5;
...
if ((n > 1) == 2) { ... }

This warning is enabled by -Wall ๐Ÿ’…


-Wbool-operation

This warns about suspicious operations on expressions of a boolean type ๐Ÿ‘€. For example, bitwise negation of a boolean is very likely a bug in your program ๐Ÿ›. For C, this warning also warns about incrementing or decrementing a boolean, which rarely makes sense ๐Ÿ™ƒ. (In C++, decrementing a boolean is always invalid. Incrementing a boolean is invalid in C++17, and deprecated otherwise ๐Ÿฅ€.)

This warning is enabled by -Wall โœจ


-Wduplicated-branches

This warns when an if-else has identical branches ๐Ÿ‘ฏ. It detects cases like this one:

if (p != NULL)
  return 0;
else
  return 0;

It doesn't warn when both branches contain just a null statement ๐Ÿ™ˆ. This warning also warns for conditional operators, like this one:

int i = x ? *p : *p;

-Wduplicated-cond

This warns about duplicated conditions in an if-else-if chain ๐Ÿ”. For instance, it'll warn for code like this:

if (p->q != NULL) { ... }
else if (p->q != NULL) { ... }

-Wframe-address

This warns when __builtin_frame_address or __builtin_return_address is called with an argument greater than 0 ๐Ÿšจ. Those calls may return indeterminate values or crash the program ๐Ÿ’€. This warning is included in -Wall ๐Ÿ’–


-Wno-discarded-qualifiers (C and Objective-C only)

Okay so this one turns off a warning ๐Ÿ™…. By default, the compiler warns if the type qualifiers on pointers are being discarded ๐Ÿ‘€. Typically, that's when a "const char *" variable gets passed to a function that takes a "char *" parameter. Use this option if you want to suppress that warning ๐Ÿซถ


-Wno-discarded-array-qualifiers (C and Objective-C only)

Same energy, but for arrays that are pointer targets ๐Ÿ’…. Do not warn if the type qualifiers on those arrays are being discarded. Typically, the compiler warns if a "const int (*)[]" variable is passed to a function that takes an "int (*)[]" parameter. This option suppresses that warning ๐ŸŒธ


-Wno-incompatible-pointer-types (C and Objective-C only)

This tells GCC not to warn when there's a conversion between pointers that have incompatible types ๐Ÿ™ˆ. It covers cases not handled by -Wno-pointer-sign, which is the one for pointer argument passing or assignment with different signedness ๐Ÿง

By default, in C99 and later dialects of C, GCC treats this issue as an error ๐Ÿ˜ญ. You can downgrade it to a warning with -fpermissive (along with certain other errors), or for this error alone, with -Wno-error=incompatible-pointer-types ๐Ÿฉน. This warning gets upgraded to an error by -pedantic-errors ๐Ÿ”ฅ


-Wno-int-conversion (C and Objective-C only)

This tells GCC not to 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 issue as an error ๐Ÿ’€. You can downgrade it to a warning with -fpermissive (along with certain other errors), or for this error alone, with -Wno-error=int-conversion ๐ŸŒˆ

Heads up, bestie: this next one gets bumped up to an error if you use -pedantic-errors ๐Ÿšจ

  • -Wzero-as-null-pointer-constant โ€” okay so this one yells whenever you use a literal 0 as a null pointer constant ๐Ÿ™ˆ
  • -Wzero-length-bounds โ€” ngl this one is giving "watch your array bounds" energy โœจ It warns about accesses to elements of zero-length array members that might overlap other members of the same object. Declaring interior zero-length arrays is discouraged, because accessing them is undefined.
โš ๏ธ Undefined behaviour alert: touching those interior zero-length arrays is undefined, so please don't go poking at them ๐Ÿ˜ญ

Check out this example. The first two stores in function bad get flagged because those array elements overlap the next members b and c. The third store gets 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
}

Also, -Wzero-length-bounds is enabled by -Warray-bounds, so it tags along for the ride ๐ŸŽ€

  • -Wno-div-by-zero โ€” bestie, this turns off the warning for 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 โ€” this one tells GCC to print warnings for constructs found in system header files ๐Ÿ“š Normally those warnings are suppressed, on the assumption that they usually aren't real problems and would just make the compiler output harder to read. With this flag, GCC emits them as if they happened in user code ๐Ÿ’… Heads up though: using -Wall alongside this option does not warn about unknown pragmas in system headers. For that, you also need -Wunknown-pragmas ๐Ÿซถ
  • -Wtautological-compare โ€” this one catches self-comparisons that always evaluate to true or false ๐Ÿ’€ It detects mistakes like this:
int i = 1;
...
if (i > i) { ... }

It also warns about bitwise comparisons that always come out true or false. For example:

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

That one will always be false, sis ๐Ÿ˜ญ This warning is enabled by -Wall โœจ

  • -Wtrailing-whitespace, -Wtrailing-whitespace=kind โ€” this one warns about trailing whitespace at the end of lines, including inside comments, but skips trailing whitespace in raw string literals ๐Ÿงน -Wtrailing-whitespace is the same as -Wtrailing-whitespace=blanks, and it only warns about trailing spaces and horizontal tabs. -Wtrailing-whitespace=any also catches trailing form feed or vertical tab characters. -Wno-trailing-whitespace or -Wtrailing-whitespace=none turns it off, which is the default. This is a coding style warning, so it's more of a vibe thing than a bug thing ๐Ÿ’–
  • -Wleading-whitespace=kind โ€” this one checks for style issues in leading whitespace, but it doesn't care about how much you indent ๐ŸŒธ Some projects only use spaces, some only use tabs, and some use zero or more tabs (in multiples of -ftabstop=n) followed by zero or fewer than n spaces. No warning shows up on lines that are only whitespace (although a -Wtrailing-whitespace= warning might still pop up), and nothing gets flagged inside raw string literals. It does check leading whitespace inside multi-line comments, though ๐Ÿ‘€
    • -Wleading-whitespace=spaces warns about leading whitespace that isn't spaces, for projects that want to indent with just spaces ๐ŸŽ€
    • -Wleading-whitespace=tabs warns about leading whitespace that isn't horizontal tabs, for projects that want to indent with just horizontal tabs ๐Ÿงš
    • -Wleading-whitespace=blanks warns about leading whitespace that isn't spaces or horizontal tabs. It also flags a horizontal tab after a space in the leading whitespace, or 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 ๐Ÿ’œ
  • -Wtrampolines โ€” this one warns about trampolines generated for pointers to nested functions ๐Ÿฆ‹ A trampoline is a little piece of data or code that gets created at run time on the stack when the address of a nested function is taken, and it's used to call that nested function indirectly. On some targets it's only data, so it needs no special treatment. On most targets, though, it's made of code, which means the stack has to be executable for the program to work properly.
โš ๏ธ Security vibes: making the stack executable is a big deal, so be careful with trampolines on those targets ๐Ÿ™ˆ
  • -Wfloat-equal โ€” this one warns if floating-point values are used in equality comparisons ๐ŸŒˆ The idea is that sometimes it's convenient to treat floating-point values as approximations to infinitely precise real numbers. If you're doing that, you need to figure out the maximum (or likely maximum) error the computation introduces, and allow for it when you compare values (and when you produce output, but that's a different problem ๐Ÿ’…). Instead of testing for equality, you should check whether the two values have overlapping ranges. You do that with the relational operators, so equality comparisons are probably a mistake ๐Ÿซถ
  • -Wtraditional (C and Objective-C only) โ€” this one warns about 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 probably avoid. Here's what it catches:
    • Macro parameters that show up inside string literals in the macro body. Traditional C does macro replacement inside string literals, but ISO C doesn't ๐Ÿงต
    • Preprocessor directives that traditional C didn't really have. Traditional preprocessors only treated a line as a directive if the # was 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 that 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, like the _MIN/_MAX macros in <limits.h>. Using those macros in user code would 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 the 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 ๐Ÿ’€
    • The ISO type of an integer constant has a different width or signedness from its traditional type. This warning is only issued if the base of the constant is ten, so hexadecimal or octal values (which usually represent bit patterns) don't get warned about ๐Ÿ”ข
    • ISO string concatenation usage ๐Ÿงถ
    • Initialization of automatic aggregates ๐Ÿ“ฆ
    • Identifier conflicts with labels. Traditional C doesn't have a separate namespace for labels ๐Ÿท๏ธ
    • Initialization of unions. If the initializer is zero, the warning is skipped. This assumes that a zero initializer in user code is conditioned on something like __STDC__ to avoid missing-initializer warnings, and that it relies on default initialization to zero in the traditional C case ๐Ÿซ 
    • Conversions by prototypes between fixed-point and floating-point values, and vice versa. Missing these prototypes when compiling with traditional C causes serious problems. This is a subset of the possible conversion warnings; for the full set, use -Wtraditional-conversion ๐Ÿšจ
    • Use of ISO C style function definitions. This warning intentionally isn't issued for prototype declarations or variadic functions, because those ISO C features show up in your code when you use libiberty's traditional C compatibility macros, PARAMS and VPARAMS. The warning is also skipped for nested functions, since that's already a GCC extension and isn't relevant to traditional C compatibility ๐Ÿง
  • -Wtraditional-conversion (C and Objective-C only) โ€” this one warns if a prototype causes a type conversion that's different from what would happen to the same argument without a prototype ๐Ÿ” That includes fixed-point to floating-point conversions and vice versa, and conversions that change the width or signedness of a fixed-point argument, except when it matches the default promotion ๐Ÿฆ‹
  • -Wdeclaration-after-statement (C and Objective-C only) โ€” this one warns when a declaration shows up after a statement in a block ๐Ÿ“ This construct comes from C++, was introduced with ISO C99, and is allowed by default in GCC. It isn't supported by ISO C90. This warning gets upgraded to an error by -pedantic-errors ๐Ÿ™ˆ
  • -Wshadow โ€” this one 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/enum. If this warning is enabled, it also includes all instances of local shadowing. That means -Wno-shadow=local and -Wno-shadow=compatible-local are ignored when -Wshadow is used. It's the same as -Wshadow=global ๐Ÿ’…
  • -Wno-shadow-ivar (Objective-C only) โ€” this one stops the warning for when a local variable shadows an instance variable in an Objective-C method ๐Ÿ™…โ€โ™€๏ธ
  • -Wshadow=global โ€” this warns for any shadowing at all, and it's the same as -Wshadow ๐ŸŒ
  • -Wshadow=local โ€” this warns when a local variable shadows another local variable or a parameter ๐Ÿ 
  • -Wshadow=compatible-local โ€” okay so this one warns when a local variable shadows another local or a parameter whose type is compatible with its own. In C++, "compatible" means the shadowing variable can be converted to the shadowed variable's type. It exists alongside -Wshadow=local because when a local shadows one of incompatible type, it's usually on purpose and not a bug or typo. Check out this iterator situation ๐Ÿ‘€:
for (SomeIterator i = SomeObj.begin(); i != SomeObj.end(); ++i)
{
  for (int i = 0; i < N; ++i)
  {
    ...
  }
  ...
}

Since the two i variables have incompatible types, turning on only -Wshadow=compatible-local gives no warning here. And if someone accidentally swaps one i for the other, type checking should catch it and throw an error or warning. Using this instead of -Wshadow=local can cut down on the noise from intentional shadowing. Heads up, though: shadowing const char *i with char *i also won't trigger a warning with this flag. This warning is also turned on by -Wshadow=local. ๐Ÿ’…

  • -Wlarger-than=byte-size โ€” warns whenever you define an object bigger than byte-size. It's on by default with -Wlarger-than=PTRDIFF_MAX. You can silence these warnings by setting byte-size to SIZE_MAX or more, or by using -Wno-larger-than. It also warns about calls to bounded functions like memchr or strnlen when the bound is bigger than the largest possible object, which is PTRDIFF_MAX bytes by default. Those particular warnings can only be turned off with -Wno-larger-than. ๐Ÿงš
  • -Wno-larger-than โ€” disables the -Wlarger-than= warnings. It's the same as -Wlarger-than=SIZE_MAX or larger. ๐Ÿซถ
  • -Wframe-larger-than=byte-size โ€” warns if a function's stack frame is bigger than byte-size. Fair warning: the math for the frame size is approximate and not conservative, so the real requirements might be a bit bigger than byte-size even when you get no warning at all. Also, the compiler doesn't count any space from alloca, variable-length arrays, or similar stuff when it decides whether to warn. It's on by default with -Wframe-larger-than=PTRDIFF_MAX. You can disable these by setting byte-size to SIZE_MAX or more, or by using -Wno-frame-larger-than. ๐ŸŒธ
  • -Wno-frame-larger-than โ€” disables the -Wframe-larger-than= warnings. Same deal as -Wframe-larger-than=SIZE_MAX or larger. ๐Ÿฆ‹
  • -Wfree-nonheap-object โ€” warns when you try to deallocate something that wasn't allocated on the heap, or when you use a pointer that didn't come back from the matching allocation call. The example below gets flagged because stpcpy hands back a pointer to the terminating nul character instead of the start of the object, so the free call gets caught. It's included in -Wall. ๐Ÿ”ฅ
void f (char *p)
{
  p = stpcpy (p, "abc");
  // ...
  free (p);   // warning
}
  • -Wstack-usage=byte-size โ€” warns if a function's stack usage might go over byte-size. This one is conservative, so the compiler does count any space from alloca, variable-length arrays, or related constructs. The message matches what -fstack-usage outputs. Here's what you'll see depending on the situation:
    • If the stack usage is fully static but goes over the limit, you get:
warning: stack usage is 1120 bytes
  • If the stack usage is (partly) dynamic but bounded, you get:
warning: stack usage might be 1648 bytes
  • If the stack usage is (partly) dynamic and unbounded, you get:
warning: stack usage might be unbounded

It's on by default with -Wstack-usage=PTRDIFF_MAX. You can disable these by setting byte-size to SIZE_MAX or more, or by using -Wno-stack-usage. ๐Ÿ’–

  • -Wno-stack-usage โ€” disables the -Wstack-usage= warnings. It's the same as -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. If you add -funsafe-loop-optimizations, it warns whenever the compiler does make those assumptions instead. ๐Ÿ‘‘
  • -Wno-pedantic-ms-format (MinGW targets only) โ€” when you combine it with -Wformat and -pedantic without GNU extensions, this turns off the warnings about the non-ISO printf / scanf format width specifiers I32, I64, and I that get used on Windows targets. Those depend on the MS runtime, so they're not standard. ๐ŸŽ€
  • -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, which is handy for math with void * pointers and pointers to functions. In C++, it also warns when an arithmetic operation involves NULL. This warning is turned on by -Wpedantic, and -pedantic-errors upgrades it to an error. ๐Ÿง 
  • -Wno-pointer-compare โ€” stops the warning for when a pointer gets compared with a zero character constant. That usually means you meant to dereference the pointer, so it's a useful catch. Like this:
const char *p = foo ();
if (p == '\0')
  return 42;

Heads up that the code above is invalid in C++11. This warning is enabled by default. ๐Ÿ™ˆ

  • -Wno-tsan โ€” disables warnings about features that ThreadSanitizer doesn't support. Just so you know, ThreadSanitizer doesn't support std::atomic_thread_fence and can report false positives with it. ๐Ÿงต
  • -Wtype-limits โ€” warns if a comparison is always true or always false because of the limited range of the data type. Constant expressions don't get flagged, though. For example, it'll warn if an unsigned variable gets compared against zero with < or >=. This warning is also turned on by -Wextra. ๐Ÿ’œ
  • -Wabsolute-value (C and Objective-C only) โ€” warns when you call a standard function that computes an absolute value, but there's a more appropriate standard function you should be using. For instance, calling abs(3.14) triggers it, because the right function for a double is fabs. It also warns when the argument in one of those calls has an unsigned type. You can get rid of the warning with an explicit type cast, and it's also turned on 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 turned on by -Wall. ๐Ÿ’…
  • -Wtrigraphs โ€” warns if any trigraphs get encountered that might change what the program means. Trigraphs inside comments don't get warned about, except for the ones that would form escaped newlines. This option is implied by -Wall. If you don't pass -Wall, it's still turned on, unless trigraphs are enabled. If you want trigraph conversion without the warnings but still want the other -Wall warnings, use -trigraphs -Wall -Wno-trigraphs. ๐Ÿ“
  • -Wundef โ€” warns if an undefined identifier gets evaluated in an #if directive. Those identifiers get replaced with zero. ๐ŸŒ™
  • -Wexpansion-to-defined โ€” warns whenever defined shows up in the expansion of a macro, including when the macro gets expanded by an #if directive. That kind of usage isn't portable. This warning is also turned on by -Wpedantic and -Wextra. โญ
  • -Wunused-macros โ€” warns about macros defined in the main file that never get used. A macro counts as used if it gets expanded or tested for existence at least once. The preprocessor also warns if a macro hasn't been used by the time it gets redefined or undefined. Built-in macros, macros defined on the command line, and macros defined in include files don't get warned about.
Note: if a macro is only used inside skipped conditional blocks, the preprocessor still reports it as unused. To avoid that, you could improve the scope of the macro's definition by moving it into the first skipped block, for example. Or you can give it a dummy use with something like this:
#if defined the_macro_causing_the_warning
#endif
  • -Wno-endif-labels โ€” stops the warning for when an #else or an #endif gets followed by text. This pops up sometimes in older programs with code like this:
#if FOO
...
#else FOO
...
#endif FOO

The second and third FOO should be in comments. This warning is on by default. ๐Ÿฆ‹

  • -Wbad-function-cast (C and Objective-C only) โ€” warns when a function call gets cast to a type that doesn't match. For example, it warns if a call to a function that returns an integer type gets cast to a pointer type. ๐Ÿซง
  • -Wc90-c99-compat (C and Objective-C only) โ€” warns about features that are 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. This option doesn't depend on the standards mode. Warnings get disabled in the expression that follows __extension__. ๐Ÿ€
  • -Wc99-c11-compat (C and Objective-C only) โ€” warns about features that are in ISO C11 but not in ISO C99. That includes 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. This option is also independent of the standards mode, and warnings are disabled in the expression that follows __extension__. ๐ŸŒท

-Wc11-c23-compat, -Wc11-c2x-compat (C and Objective-C only) โ€” okay so this one's about stuff that's in ISO C23 but NOT in ISO C11 ๐ŸŒธ It'll warn you about things like leaving the string out of _Static_assert, using [[]] attribute syntax, or using decimal floating-point types. It works no matter which standards mode you're in ๐Ÿ’… Warnings get switched off inside the expression after __extension__. Also, heads up: the name -Wc11-c2x-compat is deprecated, so use the c23 one ๐Ÿฅบ And when you're not compiling in C23 mode, -pedantic-errors turns these warnings into full-on errors ๐Ÿ’€

-Wc23-c2y-compat (C and Objective-C only) โ€” same energy, but for stuff in ISO C2Y that isn't in ISO C23 ๐Ÿฆ‹ Example: a _Generic that picks by type name instead of by expression. Also independent of the standards mode, and disabled in the expression after __extension__. When you're not in C2Y mode, -pedantic-errors upgrades these to errors โœจ

-Wc++-compat (C and Objective-C only) โ€” this one checks for ISO C code that steps outside the shared zone of ISO C and ISO C++ ๐Ÿงš Like asking for an implicit conversion from void * to a pointer to something that isn't void.

-Wc++11-compat (C++ and Objective-C++ only) โ€” flags C++ stuff whose meaning changed between ISO C++ 1998 and ISO C++ 2011 ๐ŸŽ€ For example, identifiers that were fine in C++98 but are keywords in C++11. This one turns on -Wnarrowing and is enabled by -Wall ๐Ÿ‘‘

-Wc++14-compat (C++ and Objective-C++ only) โ€” same vibe, but for the change from ISO C++ 2011 to ISO C++ 2014 ๐ŸŒˆ Enabled by -Wall.

-Wc++17-compat (C++ and Objective-C++ only) โ€” checks for things whose meaning shifted from ISO C++ 2014 to ISO C++ 2017 โญ Enabled by -Wall.

-Wc++20-compat (C++ and Objective-C++ only) โ€” checks the glow-up from ISO C++ 2017 to ISO C++ 2020 ๐Ÿ“ Enabled by -Wall.

-Wc++26-compat (C++ and Objective-C++ only) โ€” checks for C++ constructs whose meaning differs between ISO C++ 2023 and the upcoming ISO C++ 2026 ๐Ÿ”ฅ Enabled by -Wall.

-Wno-c++11-extensions (C++ and Objective-C++ only) โ€” if you're compiling with an older C++ standard, this stops the warnings about C++11 constructs ๐Ÿ™ˆ Heads up though: some C++11 constructs only get flagged when you use -Wpedantic, even without this option.

-Wno-c++14-extensions (C++ and Objective-C++ only) โ€” same deal, but for C++14 constructs in older-standard code ๐Ÿ’œ Some of them only get diagnosed with -Wpedantic.

-Wno-c++17-extensions (C++ and Objective-C++ only) โ€” silences the C++17 construct warnings in older-standard code ๐Ÿฆ‹ Some C++17 constructs still only get flagged with -Wpedantic.

-Wno-c++20-extensions (C++ and Objective-C++ only) โ€” silences the C++20 construct warnings in older-standard code ๐ŸŒธ Some C++20 constructs still only get flagged with -Wpedantic.

-Wno-c++23-extensions (C++ and Objective-C++ only) โ€” silences the C++23 construct warnings in older-standard code โœจ Some C++23 constructs still only get flagged with -Wpedantic.

-Wno-c++26-extensions (C++ and Objective-C++ only) โ€” silences the C++26 construct warnings in older-standard code ๐Ÿ’… Some C++26 constructs still only get flagged with -Wpedantic.


-Wcast-qual โ€” this one catches pointer casts that drop a type qualifier from the target type ๐Ÿซถ Like casting a const char * to a plain char *. It also warns when a cast adds a qualifier in an unsafe way. Casting char ** to const char ** is the classic example, and it's not safe:

    /* 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 cast makes the required alignment of the target bigger ๐Ÿ˜ญ For example, casting a char * to an int * on machines where integers can only be accessed at two- or four-byte boundaries. There's also a version that warns about the same char * to int * cast no matter what target machine you're on ๐Ÿ”ฅ

-Wcast-function-type โ€” warns when a function pointer gets cast to an incompatible function pointer ๐Ÿงš For casts involving variadic function types, only the types of the initial arguments that are actually provided get checked. Any pointer-type parameter matches any other pointer-type. Harmless integral differences get ignored, like int vs. long on ILP32 targets, and type qualifiers get ignored too. The function type void (*) (void) is special and matches everything, so it can be used to suppress this warning. For pointer-to-member types, it warns whenever the cast changes the pointer-to-member type. This one's enabled by -Wextra ๐ŸŒˆ

-Wcast-user-defined โ€” warns when a cast to a reference type doesn't involve a user-defined conversion you'd probably expect to get called ๐ŸŽ€ Like this:

    struct A { operator const int&(); } a;
    auto r = (int&)a; // warning

This warning is enabled by default ๐Ÿ’–

-Wwrite-strings โ€” when you're compiling C, this gives string constants the type const char[length], so copying one's address into a non-const char * pointer gives you a warning ๐Ÿ“ It can catch code that tries to write into a string constant at compile time, but only if you've been super careful with const in your declarations and prototypes. Otherwise it's just a nuisance ๐Ÿ™„ That's why -Wall doesn't ask for it.

When you're compiling C++, it warns about the deprecated conversion from string literals to char *. This is enabled by default for C++ programs, and -pedantic-errors upgrades it to an error in C++11 mode or later ๐Ÿ‘‘

-Wclobbered โ€” warns about variables that might get changed by longjmp or vfork ๐Ÿ˜ญ This one's also enabled by -Wextra.

-Wno-complain-wrong-lang โ€” normally, language front ends complain when a command-line option is valid but doesn't apply to that front end ๐Ÿ’€ You can turn that off with -Wno-complain-wrong-lang. It's mostly handy when you invoke one compiler driver for multiple source files in different languages, like this:

    $ g++ -fno-rtti a.cc b.f90

Here the g++ driver hands a.cc to the C++ front end and b.f90 to the Fortran front end. The Fortran one complains with f951: Warning: command-line option '-fno-rtti' is valid for C++/D/ObjC++ but not for Fortran, which may be disabled with -Wno-complain-wrong-lang. This option can also be used to disable warnings like cc1plus: note: CTF debug info requested, but not supported for 'GNU C++20' frontend produced by -gctf or -gsctf for unsupported languages. ๐Ÿงš

-Wcompare-distinct-pointer-types (C and Objective-C only) โ€” warns if pointers of distinct types get compared without a cast ๐Ÿฆ‹ This one's enabled by default.

-Wconversion โ€” warns about implicit conversions that might change a value ๐Ÿ‘€ That includes conversions between real and integer types, like abs (x) when x is a double. It also covers conversions between signed and unsigned, like unsigned ui = -1, and conversions to smaller types, like sqrtf (M_PI). It won't warn about explicit casts like abs ((int) x) and ui = (unsigned) -1, or about conversions that don't change the value, like abs (2.0). If the signed/unsigned warnings are too much, you can turn those off with -Wno-sign-conversion ๐ŸŒธ

In C++, it also warns about confusing overload resolution for user-defined conversions, plus conversions that never use a type conversion operator: conversions to void, to the same type, to a base class, or to a reference to any of those. The signed/unsigned conversion warnings are disabled by default in C++ unless you explicitly enable -Wsign-conversion ๐Ÿ’… And warnings about conversion from arithmetic on a small type back to that same type only show up with -Warith-conversion โœจ

-Wdangling-else โ€” warns about code where it might be unclear which if an else branch belongs to ๐Ÿ™ˆ Here's an example that's confusing:

    {
      if (a)
        if (b)
          foo ();
      else
        bar ();
    }

In C/C++, every else branch belongs to the innermost possible if statement, which in this example is if (b). That's often not what the programmer expected, even though the indentation suggests otherwise ๐Ÿ’€ When there's potential for this confusion, GCC warns if you specify this flag. To get rid of the warning, add explicit braces around the innermost if statement, so the else can't possibly belong to the enclosing if. The fixed code looks like this:

    {
      if (a)
        {
          if (b)
            foo ();
          else
            bar ();
        }
    }

This warning is enabled by -Wparentheses ๐ŸŽ€

-Wdangling-pointer, -Wdangling-pointer=n โ€” warns about using pointers (or C++ references) to objects with automatic storage duration after their lifetime has ended ๐Ÿ‘ป That includes 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. This warning is enabled at all optimization levels, but it may give different results with optimization than without ๐Ÿซ 

  • -Wdangling-pointer=1 โ€” at level 1, it only diagnoses unconditional uses of dangling pointers ๐Ÿงš
  • -Wdangling-pointer=2 โ€” bestie, level 2 is the full drama ๐ŸŽญ It catches everything level 1 does, plus the conditional uses of dangling pointers too โœจ

The short form -Wdangling-pointer is the same as -Wdangling-pointer=2. Meanwhile -Wno-dangling-pointer and -Wdangling-pointer=0 both switch the warnings off ๐Ÿ™…โ€โ™€๏ธ And -Wdangling-pointer=2 is already included in -Wall ๐Ÿ’…

Okay, this example triggers the warning at level 1 ๐Ÿ”ฅ The address of the unnamed temporary gets referenced unconditionally outside of its scope, and that's a whole mess ๐Ÿ˜ญ

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 function, storing the address of the local variable "x" into the escaped pointer *p triggers the warning at level 1 ๐Ÿ’€

void g (int **p)
{
  int x = 7;
  // warning: storing the address of local variable 'x' in '*p'
  *p = &x;
}

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 only 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 โ€” warns when the macros __TIME__, __DATE__ or __TIMESTAMP__ show up ๐Ÿ•ฐ๏ธ They can make it impossible to get bitwise-identical reproducible compilations, so this one's a vibe check for your build ๐Ÿง
  • -Wempty-body โ€” flags an empty body in an if, else or do while statement ๐Ÿซฅ This warning is also turned on by -Wextra โœจ
  • -Wno-endif-labels โ€” chill out and don't warn about stray tokens after #else and #endif ๐ŸŒธ
  • -Wenum-compare โ€” warns about a comparison between values of different enumerated types ๐Ÿ‘€ In C++, enum mismatches in conditional expressions get diagnosed too, and this one's enabled by default there ๐Ÿ’œ In C, it's turned on by -Wall ๐Ÿ”ฅ
  • -Wenum-conversion โ€” warns when a value of enumerated type gets implicitly converted into a different enumerated type ๐Ÿฆ‹ In C, this is enabled by -Wextra ๐Ÿซถ
  • -Wenum-int-mismatch (C and Objective-C only) โ€” warns about mismatches between an enumerated type and an integer type in declarations ๐Ÿงš Like, this is the kind of messy situation I mean:
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 the underlying type of an enumerated type is implementation-defined, so these mismatches can cause portability issues ๐Ÿ˜ญ In C++, though, they're 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 the variable has already been initialized ๐Ÿ™ƒ It only cares about variables that are initialized when they're declared ๐ŸŽ€ This one is only supported for C and Objective-C, because in C++ this kind of branch is an error anyway ๐Ÿ’…

-Wjump-misses-init is included in -Wc++-compat โœจ And it can be disabled with the -Wno-jump-misses-init option ๐Ÿซง

  • -Wsign-compare โ€” warns when a comparison between signed and unsigned values could give an incorrect result, because the signed value gets converted to unsigned ๐Ÿง In C++, it's also enabled by -Wall ๐Ÿ’œ In C, it's enabled by -Wextra ๐ŸŒธ
  • -Wsign-conversion โ€” warns about implicit conversions that might change the sign of an integer value ๐Ÿ“ Think assigning a signed integer expression to an unsigned integer variable ๐Ÿ‘€ An explicit cast silences it ๐Ÿซถ In C, this option is also turned on by -Wconversion โœจ
  • -Wflex-array-member-not-at-end (C and C++ only) โ€” warns when a structure with a C99 flexible array member as its last field ends up not at the end of another structure ๐Ÿฆ‹ For example:
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 going from higher precision real values down to lower precision ones ๐Ÿ”ฅ This is also enabled by -Wconversion ๐Ÿซถ
  • -Wno-scalar-storage-order โ€” doesn't warn 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 ๐Ÿงฎ The computation won't give you the number of elements in the array, which is probably not what you meant ๐Ÿ˜ญ For example, this function would get flagged:
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 usually how you'd compute an array size, but it won't work right with pointers ๐Ÿ’€ It warns e.g. about sizeof (ptr) / sizeof (ptr[0]) when ptr is a pointer and not an array ๐ŸŒˆ This warning is 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 ๐Ÿง  It triggers for things like memset (ptr, 0, sizeof (ptr)); when ptr is a pointer and not an array, and it'll even suggest a possible fix ๐Ÿ› ๏ธ It also catches stuff like memcpy (&foo, ptr, sizeof (&foo)); ๐Ÿ™ƒ

It also warns about calls to bounded string copy functions like strncat or strncpy that use a sizeof expression of the source array as the bound ๐Ÿšจ In the following function, the strncat call uses the size of the source string as its bound, which is 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");
  ...
}

The -Wsizeof-pointer-memaccess option is enabled by -Wall ๐Ÿ’…

  • -Wno-sizeof-array-argument โ€” don't warn when the sizeof operator gets 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 ๐ŸŒŸ
  • -Wmemset-elt-size โ€” warns about suspicious calls to the memset built-in function where 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 means you probably forgot to multiply by the element size ๐Ÿ™ˆ This warning is enabled by -Wall ๐Ÿฆ‹
  • -Wmemset-transposed-args โ€” warns about suspicious calls to memset where the second argument isn't zero and the third argument is zero ๐Ÿ”„ For example, memset (buf, sizeof buf, 0) gets diagnosed, because memset (buf, 0, sizeof buf) was what was meant ๐Ÿ’€ The diagnostic is only emitted if 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 no warning shows up ๐Ÿ™…โ€โ™€๏ธ This warning is enabled by -Wall ๐Ÿ’–
  • -Waddress โ€” warns about suspicious uses of address expressions ๐Ÿ‘€ These are the sus ones you'll want to know about:

Comparing the address of a function or a declared object to the null pointer constant, like this, is one of them ๐Ÿง

void f (void);
void g (void)
{
  if (!f)   // warning: expression evaluates to false
    abort ();
}

Comparing a pointer to a string literal is another, like this ๐ŸŽ€

void f (const char *x)
{
  if (x == "abc")   // warning: expression evaluates to false
    puts ("equal");
}

And testing the result of pointer addition or subtraction for equality to null is also on the list ๐Ÿ™ƒ

void f (const int *p, int i)
{
  return p + i == NULL;
}
These uses usually mean the programmer made a mistake ๐Ÿšฉ The address of most functions and objects always evaluates to true (the exception is 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 this warning can be suppressed by casting the pointer operand to an integer type, such as intptr_t or uintptr_t ๐Ÿงš Comparisons against string literals result in unspecified behavior and aren't portable, and they usually suggest you meant to call strcmp ๐ŸŒธ The warning is suppressed if the suspicious expression comes out of a macro expansion ๐Ÿซง -Waddress is enabled by -Wall ๐Ÿ’…

  • -Wno-address-of-packed-member โ€” don't warn when the address of a packed member of a struct or union is taken ๐ŸงŠ That usually gives you an unaligned pointer value, but this is enabled by default, so you're already covered โœจ
  • -Wlogical-op โ€” warns about suspicious uses of logical operators in expressions ๐Ÿง  That includes using logical operators where a bitwise operator is probably what was expected ๐Ÿ“ It also warns when the operands of a logical operator are the same, like this:
extern int a;
if (a < 0 && a < 0) { ... }
  • -Wlogical-not-parentheses โ€” warns about a logical not used on the left hand side operand of a comparison ๐Ÿ™ˆ It doesn't warn if the right operand is considered to be a boolean expression ๐Ÿซถ Its whole purpose is to catch sus code like this:
int a;
...
if (!a > 1) { ... }

It is possible to suppress the warning by wrapping the LHS into parentheses, bestie ๐Ÿซถ โœจ

if ((!a) > 1) { ... }

This warning is enabled by -Wall ๐Ÿ’…

  • -Waggregate-return โ€” okay so this one warns if any functions that return structs or unions are defined or called ๐ŸŽ€ In languages where you can return an array, this also triggers a warning ๐ŸŒธ
  • -Wno-aggressive-loop-optimizations โ€” this turns off the warning when the compiler spots undefined behavior inside a loop that has a constant number of iterations ๐Ÿ’€ Heads up, -Waggressive-loop-optimizations is already on by default โœจ
  • -Wno-attributes โ€” no warning if an unexpected __attribute__ shows up, like unrecognized attributes or function attributes slapped onto variables, etc. ๐Ÿฆ‹ It won't stop errors for wrong use of attributes that are supported though ๐Ÿ˜ญ Also, warnings about ill-formed uses of standard attributes get upgraded to errors by -pedantic-errors โ—

Next level: using -Wno-attributes= lets you silence warnings about unknown scoped attributes (in C++11 and C23) ๐Ÿงš For example, -Wno-attributes=vendor::attr disables the warning for this declaration:

[[vendor::attr]] void f();

Or if you want the whole namespace gone, -Wno-attributes=vendor:: stops the warnings for both of these:

[[vendor::safe]] void f();
[[vendor::unsafe]] void f2();

Lowkey important: -Wno-attributes= does not imply -Wno-attributes ๐Ÿ‘‘

  • -Wno-builtin-declaration-mismatch โ€” this one warns if a built-in function gets declared with an incompatible signature or as a non-function ๐Ÿ”ฅ It also fires when a built-in function declared with a type that has no prototype gets called with arguments whose promoted types don't match what the function expects ๐Ÿ’œ With -Wextra, it also warns when a built-in function that takes arguments is declared without a prototype ๐ŸŒˆ The -Wbuiltin-declaration-mismatch warning is on by default, but to dodge it, just include the right header so the built-in function prototypes are in scope ๐Ÿซถ

Here's the drama: the memset call below gets caught because the function wants a size_t argument, but 32 is an int ๐Ÿ˜ญ With -Wextra, the declaration itself gets flagged too ๐Ÿ’…

extern void* memset ();
void f (void *d)
{
  memset (d, '\0', 32);
}
  • -Wno-builtin-macro-redefined โ€” don't warn if certain built-in macros get redefined ๐Ÿ™ˆ This quiets warnings for redefining __TIMESTAMP__, __TIME__, __DATE__, __FILE__, and __BASE_FILE__ ๐Ÿ“
  • -Wkeyword-macro โ€” warns if a keyword gets defined as a macro or undefined ๐ŸŽ€ For C++, identifiers with special meaning or standard attribute identifiers get called out too โœจ This one is enabled by default for C++26 if -Wpedantic is on, and it emits a pedwarn in that case ๐Ÿ”ฅ
  • -Wfree-labels (C and Objective-C only) โ€” warns if a label is applied to a non-statement, or sits at the end of a compound statement ๐Ÿฆ‹ C23 and later allow these labels, and GCC offers them as an extension in every other dialect ๐ŸŒธ This warning is also turned on by -Wc11-c23-compat, and -pedantic-errors turns it into an error if you're building for a C version before C23 ๐Ÿ’€
  • -Wheader-guard โ€” this warns if a valid multiple inclusion guard in a preprocessor header has a #define right after the #ifndef or #if !defined directive, and that #define defines a different macro with a similar name ๐Ÿ‘€ The real guard macro also 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 line 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 the argument types ๐ŸŒธ An old-style function definition is allowed without a warning if it's preceded by a declaration that does specify the argument types ๐Ÿงš
  • -Wold-style-declaration (C and Objective-C only) โ€” warns about obsolescent usages in a declaration, 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 ๐Ÿฆ‹ It warns even if there's already a previous prototype ๐Ÿ’… A definition using () isn't treated as old-style in C23 mode, since it's equivalent to (void) there, but it is treated as old-style for older standards ๐ŸŒˆ
  • -Wmultiple-parameter-fwd-decl-lists (C and Objective-C only) โ€” warns if more than one list of forward declarations of parameters shows up in a function prototype ๐ŸŽ€ This warning is also enabled by -Wextra ๐Ÿซถ
  • -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 enabled 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 can be handy for documenting that a parameter goes unused in the definition ๐Ÿ“ It's part of C23 and later dialects of C, and GCC offers it as an extension in all other dialects ๐ŸŒˆ This warning is also enabled by -Wc11-c23-compat, and -pedantic-errors makes it an error if you're building for a C version before C23 ๐Ÿ’…
  • -Wmissing-parameter-type (C and Objective-C only) โ€” warns when a function parameter has no type specifier in K&R-style functions, like this ๐Ÿ‘‡
void foo(bar) { }

This warning is also enabled by -Wextra ๐Ÿ”ฅ

  • -Wno-declaration-missing-parameter-type (C and Objective-C only) โ€” no warning if a function declaration has a parameter name without a type ๐Ÿ™ˆ These declarations don't give you a function prototype, so they block most type checking in function calls ๐Ÿ’€ This warning is on by default ๐Ÿซ  In C99 and later dialects of C, it's treated as an error ๐Ÿ˜ญ You can downgrade it to a warning with -fpermissive (along with certain other errors), or for this error alone with -Wno-error=declaration-missing-parameter-type ๐Ÿซถ -pedantic-errors upgrades this warning to an error ๐Ÿ‘‘
  • -Wmissing-prototypes (C and Objective-C only) โ€” warns if a global function gets defined without a previous prototype declaration ๐Ÿฆ‹ It warns even if the definition itself has a prototype ๐Ÿ’… Use this one to catch global functions that don't have a matching prototype declaration in a header file ๐ŸŒธ It isn't valid for C++, since all function declarations there provide prototypes, and a non-matching declaration declares an overload instead of conflicting with an earlier one ๐Ÿงš For missing declarations in C++, use -Wmissing-declarations instead ๐Ÿ’–
  • -Wmissing-variable-declarations (C and Objective-C only) โ€” warns if a global variable gets defined without a previous declaration ๐ŸŒˆ Use it to find global variables that don't have a matching extern declaration in a header file ๐Ÿ“
  • -Wmissing-declarations โ€” warns if a global function gets defined without a previous declaration, even if the definition itself has a prototype ๐ŸŽ€ Use this to catch global functions that aren't declared in header files โœจ In C, no warnings are issued for functions with previous non-prototype declarations, so use -Wmissing-prototypes for those ๐Ÿฆ‹ In C++, no warnings are issued for function templates, inline functions, or functions in anonymous namespaces ๐Ÿ‘‘
  • -Wmissing-field-initializers โ€” warns if a struct's initializer is missing some fields ๐Ÿซ  In this example, the code triggers the warning because x.h is implicitly zero:
struct s { int f, g, h; };
struct s x = { 3, 4 };

In C, this option doesn't warn about designated initializers, so this one is 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 };

Likewise, in C++ this option doesn't warn about the empty { } initializer, for example:

struct s { int f, g, h; };
s x = { };

This warning is included in -Wextra ๐Ÿ’… If you want the other -Wextra warnings without this one, use -Wextra -Wno-missing-field-initializers ๐ŸŒธ

  • -Wno-missing-requires โ€” by default, the compiler warns about a concept-id showing up as a C++20 simple-requirement ๐Ÿ‘€ Like here:
bool satisfied = requires { C<T> };

satisfied will be true if C<T> is a valid expression, which it is for all T ๐Ÿซ  Presumably the user meant to write this instead:

bool satisfied = requires { requires C<T> };

so satisfied is only true if concept C is satisfied for type T ๐Ÿ’– You can turn off this warning with -Wno-missing-requires ๐Ÿงš

  • -Wno-missing-template-keyword โ€” the member access tokens ., -> and :: need to be followed by the template keyword if the parent object is dependent and the member being named is a template ๐Ÿฆ‹ Here's the vibe check:
template <class X>
void DoStuff (X x)
{
  x.template DoSomeOtherStuff<X>(); // Good.
  x.DoMoreStuff<X>(); // Warning, x is dependent.
}

In rare cases you can get false positives ๐Ÿ™ˆ To silence them, wrap the expression in parentheses ๐Ÿซถ For example, the following is treated as a template, even where 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.
}

Psst, you can totally switch that one off with -Wno-missing-template-keyword if it's being extra. ๐Ÿ’…

  • -Wno-multichar โ€” bestie, don't warn if a multicharacter constant like 'FOOF' shows up. ngl these are usually a typo in your code ๐Ÿ™ˆ, since their values are implementation-defined and they shouldn't be in portable code anyway.
  • -Wnormalized=[none|id|nfc|nfkc] โ€” okay so, in ISO C and ISO C++, two identifiers are different if they're different sequences of characters. BUT when you use characters outside basic ASCII, you can end up with two totally different sequences that look the same ๐Ÿ˜ญ. The ISO 10646 standard has normalization rules that turn lookalikes into the same sequence, and GCC can warn you if your identifiers aren't normalized. This option controls that warning. There are four 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 recommended form for most uses, and it's equivalent to -Wnormalized. โœจ
    • Unfortunately, some characters that ISO C and C++ allow in identifiers stop being allowed once they're turned into NFC. So there's no way to use these symbols in portable ISO C or C++ and keep all your identifiers in NFC. -Wnormalized=id suppresses the warning for these characters. It's hoped that future versions of the standards will fix this, which is why it isn't the default. ๐Ÿฅบ
    • You can switch the warning off for all characters 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 instance, "\u207F", "SUPERSCRIPT LATIN SMALL LETTER N", displays just like a regular "n" that's been placed in a superscript. ISO 10646 defines the NFKC normalization scheme to convert all of these into a standard form too, and GCC warns if your code isn't in NFKC when you use -Wnormalized=nfkc. This is comparable to warning about every identifier that contains the letter O because it might be confused with the digit 0, so it isn't the default. It can still be a useful local coding convention if the programming environment can't be fixed to display these characters distinctly. ๐Ÿ”
  • -Wno-attribute-warning โ€” no warnings about using functions declared with the "warning" attribute. This warning is enabled by default, but -Wno-attribute-warning can turn it off, or -Wno-error=attribute-warning can turn off the error when you compile with the -Werror flag. ๐ŸŽ€
  • -Wno-deprecated โ€” no warnings about using deprecated features.
    • In C++, explicitly specifying -Wdeprecated also enables warnings about some features that are deprecated in later language standards, specifically -Wcomma-subscript, -Wvolatile, -Wdeprecated-enum-float-conversion, -Wdeprecated-enum-enum-conversion, -Wdeprecated-literal-operator, and -Wdeprecated-variadic-comma-omission.
  • -Wno-deprecated-declarations โ€” no warnings when you use functions, variables, or types marked as deprecated with the "deprecated" attribute. ๐Ÿ™„
  • -Wno-deprecated-openmp โ€” no warnings about deprecated OpenMP code.
  • -Wno-overflow โ€” no warnings about compile-time overflow in constant expressions.
  • -Wno-odr โ€” warns about One Definition Rule violations during link-time optimization. Enabled by default.
Heads up, bestie: the -Wno- in the name of -Wno-odr and -Wno-openmp doesn't match what their descriptions say. Read the description, not just the name, or you'll be confused ๐Ÿ‘€.
  • -Wopenacc-parallelism โ€” warns about potentially suboptimal choices related to OpenACC parallelism. โญ
  • -Wno-openmp โ€” warns about suspicious OpenMP code.
  • -Wopenmp-simd โ€” warns if the vectorizer cost model overrides the OpenMP simd directive that the user set. The -fsimd-cost-model=unlimited option can be used to relax the cost model. ๐Ÿ’œ
  • -Woverride-init (C and Objective-C only) โ€” warns if an initialized field without side effects is overridden when using designated initializers.
    • This warning is included in -Wextra. To get the other -Wextra warnings without this one, use -Wextra -Wno-override-init.
  • -Wno-override-init-side-effects (C and Objective-C only) โ€” no warning if an initialized field with side effects is overridden when using designated initializers. This warning is enabled by default.
  • -Wpacked โ€” warns if a structure is given the packed attribute, but the packed attribute has no effect on the layout or size of the structure. Those structures can end up misaligned for little benefit. For instance, in this code, the variable "f.x" in "struct bar" is misaligned even though "struct bar" doesn't itself have the packed attribute:
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". This got fixed in GCC 4.4, but the change can lead to 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 field "a" and "b" in this structure:
struct foo
{
  char a:4;
  char b:8;
} __attribute__ ((packed));

This warning is enabled by default. Use -Wno-packed-bitfield-compat to disable this warning. ๐Ÿฆ‹

  • -Wpacked-not-aligned (C, C++, Objective-C and Objective-C++ only) โ€” warns if a structure field with explicitly specified alignment in a packed struct or union is misaligned. For example, a warning will be issued on "struct S", like "warning: alignment 1 of 'struct S' is less than 8", in this code:
struct __attribute__ ((aligned (8))) S8 { char a[8]; };
struct __attribute__ ((packed)) S {
  struct S8 s8;
};

This warning is enabled by -Wall.

  • -Wpadded โ€” warns if padding is included in a structure, either to align an element of the structure or to align the whole structure. Sometimes it's possible to rearrange the fields of the structure to reduce the padding and make it smaller. ๐ŸŒธ
  • -Wredundant-decls โ€” warns if anything is declared more than once in the same scope, even in cases where multiple declarations are valid and change nothing.
  • -Wrestrict โ€” 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 such objects overlap. In the example below, the strcpy call tries to truncate the string by replacing its initial characters with the last four. 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);
  ...
}

The -Wrestrict option detects 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) โ€” warns if an "extern" declaration is encountered within a function.
  • -Winline โ€” warns if a function that is declared as inline cannot be inlined. Even with this option, the compiler does not warn about failures to inline functions declared in system headers.
    • The compiler uses a variety of heuristics to decide whether or not to inline a function. For example, it takes into account the size of the function being inlined and the amount of inlining that has already been done in the current function. So seemingly insignificant changes in the source program can make the -Winline warnings appear or disappear. ๐Ÿ™ˆ
  • -Winterference-size, -Wno-interference-size โ€” warns about questionable uses of the C++17 "constexpr" variables "std::hardware_destructive_interference_size" and "std::hardware_constructive_interference_size". These variables are meant for controlling class layout, to avoid false sharing in concurrent code, like this:
struct independent_fields {
  alignas(std::hardware_destructive_interference_size)
    std::atomic<int> one;
  alignas(std::hardware_destructive_interference_size)
    std::atomic<int> two;
};

Here, "one" and "two" are intended to be far enough apart that stores to "one" won't require accesses to the other to reload the cache line. ๐Ÿงš

By default, these variables are given values based on the current -mtune option, typically the L1 cache line size for the particular target CPU, and sometimes a range if tuning for a generic target. So all translation units that depend on ABI compatibility for the use of these variables must be compiled with the same -mtune (or -mcpu).

If ABI stability is important, such as if the use is in a header for a library, you should probably not use the hardware interference size variables at all.

These warnings are enabled by default. If you're confident that your use of these variables does not affect ABI outside a single build of your project, you can turn off the warning with -Wno-interference-size.

  • -Wint-in-bool-context โ€” warns about suspicious use of integer values where boolean values are expected. Examples include conditional expressions (?:) using non-boolean integer constants in a boolean context, like if (a <= b ? 2 : 3), or left shifting of signed integers in a boolean context, like for (a = 0; 1 << a; a++);. It's likewise for all kinds of multiplications, regardless of the data type. This warning is enabled by -Wall. ๐Ÿ‘‘
  • -Wno-int-to-pointer-cast โ€” suppresses warnings from casts to pointer type of an integer of a different size. In C++, casting to a pointer type of smaller size is an error. Wint-to-pointer-cast is enabled by default. ๐Ÿซถ
  • -Wno-pointer-to-int-cast (C and Objective-C only) โ€” bestie, this one silences the warning for casting a pointer to an integer type of a different size ๐Ÿ™ˆ
  • -Winvalid-pch โ€” ooh, this warns you if a precompiled header is sitting in the search path but GCC can't actually use it ๐Ÿฆ‹
  • -Winvalid-utf8 โ€” flags any invalid UTF-8 character. It's on by default for C++23 when -finput-charset=UTF-8 is used, and it becomes a full-on error with -pedantic-errors ๐Ÿ’…
  • -Wno-unicode โ€” skips complaining about invalid forms of delimited or named escape sequences that get treated as separate tokens. Heads up: -Wunicode is already on by default โœจ
  • -Wlong-long โ€” warns when the long long type shows up. It's switched on by either -Wpedantic or -Wtraditional in ISO C90 and C++98 modes. If you want it quiet, use -Wno-long-long ๐ŸŒธ

This one gets promoted to an error by -pedantic-errors, so be careful, bestie ๐Ÿ˜ญ

  • -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, and you can shush it with -Wno-variadic-macros ๐ŸŽ€
  • -Wno-varargs โ€” stops the warnings about questionable usage of the macros that handle variable arguments, like va_start. These are on by default, so this is how you turn them off ๐Ÿงš
  • -Wvector-operation-performance โ€” warns if a vector operation isn't implemented through the architecture's SIMD capabilities. It's mainly for performance tuning, and the vibes are: a vector op can be done "piecewise", which means the scalar op runs on every vector element; "in parallel", which means the vector op uses scalars of a wider type (usually way more performance efficient); or "as a single scalar", which means 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 one gets upgraded to an error by -pedantic-errors ๐Ÿ’€
  • -Wvla-larger-than=byte-size โ€” when you use this, 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 over byte-size bytes. It's kind of like how -Walloca-larger-than=byte-size works, but for VLAs ๐Ÿ‘‘

Real talk though: GCC may optimize small variable-length arrays with a known value into plain arrays, so this warning might not fire for those ones ๐Ÿ™ˆ

-Wvla-larger-than=PTRDIFF_MAX is on 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 โ€” turns off the -Wvla-larger-than= warnings. It's the same as -Wvla-larger-than=SIZE_MAX or larger ๐Ÿ’œ
  • -Wvla-parameter โ€” warns about redeclarations of functions that have variable-length array arguments of inconsistent kinds or forms, and it also turns on detection of 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 providing arguments of sufficient size, or accessing more than that max, may get diagnosed ๐Ÿ˜ญ

Here's the example, exactly as written, so you can see the chaos in action:

        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);
          ...
        }

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 has to have at least n elements. On top of that, calling f with the associated VLA bound parameter larger than the actual VLA bound also triggers a warning ๐Ÿ’…

-Wvla-parameter is included in -Wall. For similar problems with ordinary array arguments, -Warray-parameter is the one that warns ๐ŸŒˆ

  • -Wvolatile-register-var โ€” warns if a register variable is declared volatile. Real 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 the warnings about uses of ^, the exclusive or operator, when it looks like the code meant exponentiation. Specifically, it fires 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. Iykyk ๐Ÿซถ

You can silence this warning 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, 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 would likely take inordinate amounts 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 is upgraded to an error by -pedantic-errors.

  • -Wstack-protector โ€” 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 usually allow way longer strings than the standard's minimum, but very portable programs should avoid them ๐Ÿงต

The limit applies after string constant concatenation, and it doesn't count the trailing NUL. In C90, the limit was 509 characters. 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) โ€” warns about any floating constant that doesn't have a suffix. If you pair it with -Wsystem-headers, it also warns about those constants in system header files ๐ŸŒˆ This is super useful when you're prepping code 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 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, this one turns on a static analysis of program flow. It hunts for "interesting" interprocedural paths through the code and drops warnings for problems it finds on them ๐Ÿ”

Heads up, it's way more expensive than other GCC warnings ๐Ÿ’ธ

Technically speaking, it does coverage-guided symbolic execution of the code being compiled. It's neither sound nor complete, which means it can have false positives and false negatives. It's a bug-finding tool, not a tool for proving program correctness ๐Ÿซ 

The analyzer is only suitable for C code in this release.

Enabling this option effectively enables 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-throw-of-unexpected-type
-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 is only available if GCC was configured with analyzer support enabled.

  • -Wanalyzer-symbol-too-complex โ€” if -fanalyzer is enabled, the analyzer uses various heuristics to try to track the state of memory. But sufficiently complicated code can defeat those heuristics ๐ŸŒ€

By default, the analysis quietly gives up on tracking expression values once they blow past an internal limit ๐Ÿ™ˆ, and it falls back to an imprecise representation for them. The -Wanalyzer-symbol-too-complex option is the one that actually warns you when this happens ๐Ÿซถ

  • -Wanalyzer-too-complex โ€” the analyzer's explorer brain getting overwhelmed ๐Ÿงš

If -fanalyzer is on, the analyzer uses a bunch of heuristics to poke around the control flow and data flow of your program โœจ. But sufficiently complicated code can totally defeat them ๐Ÿ’€.

By default, if the code is too complicated for the analyzer to fully explore and it hits an internal limit, the analysis just stops, no fuss. -Wanalyzer-too-complex makes it warn you instead ๐Ÿ“ข.

  • -Wno-analyzer-allocation-size โ€” flags buffer sizes that don't line up with the pointer type ๐Ÿ“

This warning requires -fanalyzer, which enables it; to disable it, use -Wno-analyzer-allocation-size.

This diagnostic warns for paths through the code where a pointer to a buffer gets assigned to point at a buffer whose size isn't a multiple of sizeof (*pointer).

๐Ÿ“š See CWE-131: Incorrect Calculation of Buffer Size (https://cwe.mitre.org/data/definitions/131.html).

  • -Wno-analyzer-deref-before-check โ€” catches NULL checks that show up after the pointer was already dereferenced ๐Ÿ™ƒ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-deref-before-check to disable it.

This diagnostic warns for paths through the code where a pointer is checked for NULL after it has already been dereferenced, which suggests the pointer could have been NULL. Cases like this mean the NULL check is either redundant, or it needs to be moved to before the pointer is dereferenced.

This diagnostic also considers values passed to a function argument marked with __attribute__((nonnull)) as requiring a non-NULL value, so it'll complain if such values get checked for NULL after returning from that function call.

โš ๏ธ This diagnostic is unlikely to be reported when any level of optimization is enabled, because GCC's optimization logic typically considers those NULL checks redundant and optimizes them away before the analyzer "sees" them. So you should disable optimization when you're trying to trigger this one ๐Ÿ’…
  • -Wno-analyzer-double-fclose โ€” yells when a FILE * gets fclosed more than once ๐Ÿ“

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-double-fclose to disable it.

This diagnostic warns for paths through the code where a FILE * can have fclose called on it more than once.

๐Ÿ“š See CWE-1341: Multiple Releases of Same Resource or Handle (https://cwe.mitre.org/data/definitions/1341.html).

  • -Wno-analyzer-double-free โ€” spots pointers that get freed twice ๐Ÿ’€

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-double-free to disable it.

This diagnostic warns for paths through the code where a pointer can have a deallocator called on it more than once, either free, or a deallocator referenced by attribute malloc.

๐Ÿ“š See CWE-415: Double Free (https://cwe.mitre.org/data/definitions/415.html).

  • -Wno-analyzer-exposure-through-output-file โ€” stops secrets from leaking into output files ๐Ÿคซ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-exposure-through-output-file to disable it.

This diagnostic warns for paths through the code where a security-sensitive value is written to an output file, like writing a password to a log file ๐Ÿ˜ญ.

๐Ÿ“š See CWE-532: Information Exposure Through Log Files (https://cwe.mitre.org/data/definitions/532.html).

  • -Wanalyzer-exposure-through-uninit-copy โ€” warns about uninitialized data crossing a trust boundary ๐Ÿšจ

This warning requires both -fanalyzer and the use of a plugin to specify a function that copies across a "trust boundary". Use -Wno-analyzer-exposure-through-uninit-copy to disable it.

This diagnostic warns for "infoleaks", which are paths through the code where uninitialized values get copied across a security boundary. A classic example is code inside an OS kernel that copies a partially-initialized struct on the stack out to user space ๐Ÿ‘€.

๐Ÿ“š See CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (https://cwe.mitre.org/data/definitions/200.html).

  • -Wno-analyzer-fd-access-mode-mismatch โ€” yells when you read from a write-only fd, or write to a read-only one ๐Ÿ™…

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-fd-access-mode-mismatch to disable it.

This diagnostic warns for paths through the code where a "read" is attempted on a write-only file descriptor, or vice versa.

This diagnostic also warns for code paths where a function with attribute fd_arg_read (N) is called with a file descriptor opened with O_WRONLY at referenced argument N, or where a function with attribute fd_arg_write (N) is called with a file descriptor opened with O_RDONLY at referenced argument N.

  • -Wno-analyzer-fd-double-close โ€” catches fds that get closed more than once ๐Ÿฆ‹

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-fd-double-close to disable it.

This diagnostic warns for paths through the code where a file descriptor can be closed more than once.

๐Ÿ“š See CWE-1341: Multiple Releases of Same Resource or Handle (https://cwe.mitre.org/data/definitions/1341.html).

  • -Wno-analyzer-fd-leak โ€” flags open fds that never get released ๐ŸŒธ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-fd-leak to disable it.

This diagnostic warns for paths through the code where an open file descriptor is leaked.

๐Ÿ“š See 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 โ€” operations done at the wrong point in an fd's life ๐Ÿงฉ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-fd-phase-mismatch to disable it.

This diagnostic warns for paths through the code where an operation is attempted in the wrong phase of a file descriptor's lifetime. For example, it'll warn on attempts to call accept on a stream socket that has not yet had listen successfully called on it.

๐Ÿ“š See CWE-666: Operation on Resource in Wrong Phase of Lifetime (https://cwe.mitre.org/data/definitions/666.html).

  • -Wno-analyzer-fd-type-mismatch โ€” wrong kind of fd for the operation you're doing ๐Ÿ™ˆ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-fd-type-mismatch to disable it.

This diagnostic warns for paths through the code where an operation is attempted on the wrong type of file descriptor. For example, it'll warn on attempts to use socket operations on a file descriptor obtained via open, or when attempting to use a stream socket operation on a datagram socket.

  • -Wno-analyzer-fd-use-after-close โ€” using an fd after it's already been closed โฐ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-fd-use-after-close to disable it.

This diagnostic warns for paths through the code where a read or write is called on a closed file descriptor.

This diagnostic also warns for paths through the code where a function with attribute fd_arg (N) or fd_arg_read (N) or fd_arg_write (N) is called with a closed file descriptor at referenced argument N.

  • -Wno-analyzer-fd-use-without-check โ€” using an fd without checking it's valid first ๐Ÿซฃ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-fd-use-without-check to disable it.

This diagnostic warns for paths through the code where a file descriptor is used without being checked for validity.

This diagnostic also warns for paths through the code where a function with attribute fd_arg (N) or fd_arg_read (N) or fd_arg_write (N) is called with a file descriptor, at referenced argument N, without being checked for validity.

  • -Wno-analyzer-file-leak โ€” FILE * streams that never get cleaned up ๐Ÿ“„

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-file-leak to disable it.

This diagnostic warns for paths through the code where a <stdio.h> FILE * stream object is leaked.

๐Ÿ“š See CWE-775: Missing Release of File Descriptor or Handle after Effective Lifetime (https://cwe.mitre.org/data/definitions/775.html).

  • -Wno-analyzer-free-of-non-heap โ€” calling free on something that was never on the heap ๐Ÿชฆ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-free-of-non-heap to disable it.

This diagnostic warns for paths through the code where free is called on a non-heap pointer, like an on-stack buffer or a global.

๐Ÿ“š See CWE-590: Free of Memory not on the Heap (https://cwe.mitre.org/data/definitions/590.html).

  • -Wno-analyzer-imprecise-fp-arithmetic โ€” floating-point math where precision matters ๐Ÿงฎ

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-imprecise-fp-arithmetic to disable it.

This diagnostic warns for paths through the code where floating-point arithmetic is used in locations where precise computation is needed. At the moment, it only warns on the use of floating-point operands inside the calculation of an allocation size.

  • -Wno-analyzer-infinite-loop โ€” loops that look like they'll never end ๐Ÿ”

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-infinite-loop to disable it.

This diagnostic warns for paths through the code which appear to lead to an infinite loop.

Specifically, the analyzer will issue this warning when it "sees" a loop in which:

  • no externally-visible work could be being done within the loop
  • there is no way to escape from the loop
  • the analyzer is sufficiently confident about the program state throughout the loop to know that the above are true

One way for this warning to be emitted is when there is an execution path through a loop for which taking the path on one iteration implies that the same path will be taken on all subsequent iterations.

For example, consider:

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 complain for the above case because if opcode ever matches none of the cases, the switch will follow the implicit default case, making the body of the loop be a no-op with cpu_state.pc unchanged, and thus using the same value of opcode on all subsequent iterations, leading to an infinite loop ๐ŸŒ€.

๐Ÿ“š See CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop') (https://cwe.mitre.org/data/definitions/835.html).

  • -Wno-analyzer-infinite-recursion โ€” recursion that never bottoms out ๐Ÿชž

This warning requires -fanalyzer, which enables it; use -Wno-analyzer-infinite-recursion to disable it.

Okay so this one is about infinite recursion ๐ŸŒ€ It warns about paths in the code that look like they never stop calling themselves, which is giving "this function will never end" ๐Ÿ’€

Here's how the analyzer sees it: whenever it spots 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 of that same function on the stack ๐Ÿงš The warning only shows up if nothing in memory seems to be changing. If any parameters or globals look like they're changing, the analyzer assumes the recursion will end and quietly skips the warning ๐Ÿซถ

Heads up though ๐Ÿ™ˆ this diagnostic can miss infinite recursion that the optimizer already turned into iteration before the analyzer gets to look. So if you're trying to trigger it, disable optimization โœจ

Compare with -Winfinite-recursion, which gives a similar diagnostic but is implemented a different way ๐Ÿ’œ

See CWE-674: Uncontrolled Recursion ("https://cwe.mitre.org/data/definitions/674.html").

  • -Wno-analyzer-jump-through-null โ€” ๐Ÿฆ‹ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-jump-through-null. It warns about paths where a "NULL" function pointer gets called ๐Ÿ’€
  • -Wno-analyzer-malloc-leak โ€” ๐ŸŒธ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-malloc-leak. It warns about paths where a pointer from an allocator gets leaked. The allocator can be either malloc, or a function marked with attribute malloc ๐Ÿซ 
  • -Wno-analyzer-mismatching-deallocation โ€” ๐Ÿ’… This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-mismatching-deallocation. It warns about paths where the wrong deallocation function gets called on a pointer, based on which function allocated it. It catches mismatches between free, scalar delete, and vector delete[], plus anything marked as an allocator/deallocator pair using attribute malloc ๐Ÿ”ฅ
  • -Wno-analyzer-out-of-bounds โ€” ๐Ÿ‘‘ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-out-of-bounds. It warns about paths where a buffer is definitely read or written out of bounds. It works when the analyzer can figure out a constant offset, and for accesses past the end of a buffer with a constant capacity. It also does limited checking for accesses past the end when both the offset and the capacity are symbolic โœจ
    • See CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer ("https://cwe.mitre.org/data/definitions/119.html").
    • When the analyzer can manage it, it draws a text art diagram ๐ŸŽจ of the memory region it predicts will be accessed, next to the range that's actually valid. The diagram shows whether they overlap, touch, or sit close or far apart, which one comes first in memory, the relative sizes, whether the access is a read or a write, and sometimes the values of the data involved. You can hide this diagram with -fdiagnostics-text-art-charset=none.
  • -Wno-analyzer-overlapping-buffers โ€” ๐Ÿซถ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-overlapping-buffers. It warns about paths where overlapping buffers get passed to an API where the behavior is undefined for that kind of input. Specifically, it looks at calls to these functions, but only when the buffers are known to overlap:
    • *<"memcpy">
    • *<"strcat">
    • *<"strcpy">
  • -Wno-analyzer-possible-null-argument โ€” ๐ŸŒˆ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-possible-null-argument. It warns about 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 โ€” โœจ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-possible-null-dereference. It warns about paths where a possibly-NULL value gets dereferenced ๐Ÿ™ˆ
  • -Wno-analyzer-null-argument โ€” ๐Ÿ“ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-null-argument. It warns about 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 โ€” ๐Ÿงš This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-null-dereference. It warns about paths where a value known to be NULL gets dereferenced ๐Ÿ˜ญ
  • -Wno-analyzer-putenv-of-auto-var โ€” ๐ŸŽ€ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-putenv-of-auto-var. It warns about paths where a call to "putenv" gets passed a pointer to an automatic variable or an on-stack buffer ๐Ÿ‘€
  • -Wno-analyzer-shift-count-negative โ€” ๐ŸŒธ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-shift-count-negative. It warns about paths where a shift is attempted with a negative count. It's the analyzer's version of -Wshift-count-negative, which the C/C++ front ends do, but this one looks at interprocedural paths instead of just parsing the syntax tree. The analyzer doesn't prioritize finding these, so false negatives are more likely compared to other warnings ๐Ÿซ 
  • -Wno-analyzer-shift-count-overflow โ€” ๐Ÿ’œ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-shift-count-overflow. It warns about paths where a shift is attempted with a count greater than or equal to the precision of the operand's type. It's analogous to -Wshift-count-overflow in the C/C++ front ends, but it's based on analyzing interprocedural paths rather than just the syntax tree. Same catch as before: the analyzer doesn't prioritize these, so false negatives are more likely compared to other warnings ๐Ÿซถ
  • -Wno-analyzer-stale-setjmp-buffer โ€” ๐Ÿ˜ญ This one needs -fanalyzer to turn it on, and you can switch it off with -Wno-analyzer-stale-setjmp-buffer. It warns about paths where "longjmp" gets called to rewind to a "jmp_buf" tied to a "setjmp" call, in a function that has already returned โš ๏ธ
    • When "setjmp" is called on a "jmp_buf" to record a rewind location, it records the stack frame. That stack frame becomes invalid once the function containing the "setjmp" call returns. Trying to rewind to it with "longjmp" would reference a stack frame that no longer exists, and that's likely to crash (or worse) ๐Ÿ’€
  • -Wno-analyzer-tainted-allocation-size โ€” ๐Ÿ”ฅ This one needs -fanalyzer which turns it on, and you can switch it off with -Wno-analyzer-tainted-allocation-size. It warns about 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 maybe cause a denial of service attack ๐Ÿ™…โ€โ™€๏ธ
  • -Wno-analyzer-tainted-assertion โ€” ๐Ÿ‘‘ This one needs -fanalyzer which turns it on, and you can switch it off with -Wno-analyzer-tainted-assertion. It warns about paths where a value an attacker could control gets used as part of a condition without being sanitized first, and that condition guards a call to a function marked with attribute noreturn (like the function __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:

  • when assertion-checking is enabled, an attacker could trigger a denial of service by injecting an assertion failure
  • when assertion-checking is disabled, such as 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 removes the assertions 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. These functions are sometimes removed by the optimizer before the analyzer "sees" them, so disable optimization when you're trying to trigger this diagnostic ๐Ÿงš

The warning can also report problematic constructions such as

switch (some_tainted_value) {
  case 0:
    /* [...etc; various valid cases omitted...] */
    break;

  default:
    __builtin_unreachable (); /* BUG: attacker can trigger this  */
}

Okay so that default: branch is giving ๐Ÿ’€ because the comment literally says an attacker can trigger it ๐Ÿ˜ญ Despite the example above, it's not an assertion failure, strictly speaking ๐Ÿ™„ but it's still a whole vibe check ๐Ÿ”ฅ

  • -Wno-analyzer-tainted-array-index โ€” โœจ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-tainted-array-index to turn it off. It flags paths where a value an attacker could control gets used as an array index without being sanitized, so an attacker could sneak in an out-of-bounds access ๐Ÿ‘€
  • -Wno-analyzer-tainted-divisor โ€” ๐Ÿฆ‹ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-tainted-divisor to turn it off. It flags paths where an attacker-controllable value is used as the divisor in a division or modulus operation without being sanitized, so an attacker could inject a division-by-zero ๐Ÿ’…
  • -Wno-analyzer-tainted-offset โ€” ๐ŸŒธ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-tainted-offset to turn it off. It flags 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 ๐Ÿ™ˆ
  • -Wno-analyzer-tainted-size โ€” ๐Ÿ“ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-tainted-size to turn it off. It flags paths where an attacker-controllable value is used as the size of an operation such as memset without being sanitized, so an attacker could inject an out-of-bounds access ๐Ÿ˜ญ
  • -Wno-analyzer-throw-of-unexpected-type โ€” ๐Ÿงš This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-throw-of-unexpected-type to turn it off. Heads up, dynamic exception specifications are only available in C++14 and earlier. It flags paths where an exception is thrown from a function with a dynamic exception specification and the exception doesn't comply with that specification ๐Ÿ’œ
  • -Wno-analyzer-undefined-behavior-ptrdiff โ€” ๐Ÿซถ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-undefined-behavior-ptrdiff to turn it off. It flags paths where a pointer subtraction happens between pointers that refer to different chunks of memory. That code relies on undefined behavior, because pointer subtraction is only defined when both pointers point within (or just after) the same array โš ๏ธ
  • -Wno-analyzer-undefined-behavior-strtok โ€” ๐ŸŽ€ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-undefined-behavior-strtok to turn it off. It flags 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 is undefined behavior ๐Ÿ’€
  • -Wno-analyzer-unsafe-call-within-signal-handler โ€” ๐Ÿ”ฅ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-unsafe-call-within-signal-handler to turn it off. It flags paths where a function known to be async-signal-unsafe (such as fprintf) gets called from a signal handler ๐Ÿšจ
  • -Wno-analyzer-use-after-free โ€” ๐Ÿ‘‘ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-use-after-free to turn it off. It flags paths where a pointer gets used after a deallocator has been called on it. That means either free, or a deallocator referenced by attribute malloc ๐Ÿฅ€
  • -Wno-analyzer-use-of-pointer-in-stale-stack-frame โ€” ๐ŸŒˆ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-use-of-pointer-in-stale-stack-frame to turn it off. It flags paths where a pointer gets dereferenced while pointing to a variable in a stale stack frame โญ
  • -Wno-analyzer-va-arg-type-mismatch โ€” ๐Ÿ’– This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-va-arg-type-mismatch to turn it off. It flags interprocedural paths where the analyzer detects an attempt to use va_arg to extract a value passed to a variadic call, but the type used doesn't match the expression passed to the call ๐Ÿง
  • -Wno-analyzer-va-list-exhausted โ€” โœจ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-va-list-exhausted to turn it off. It flags interprocedural paths where the analyzer detects 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 ๐Ÿซ 
  • -Wno-analyzer-va-list-leak โ€” ๐Ÿƒ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-va-list-leak to turn it off. It flags 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 โ€” ๐ŸŽ  This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-va-list-use-after-va-end to turn it off. It flags interprocedural paths where the analyzer detects an attempt to use a va_list after va_end has been called on it ๐Ÿ›‘
  • -Wno-analyzer-write-to-const โ€” ๐Ÿฆ„ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-write-to-const to turn it off. It flags paths where the analyzer detects an attempt to write through a pointer to a const object. Fair warning though, the analyzer doesn't prioritize catching these, so false negatives are more likely compared to other warnings ๐Ÿ™ƒ
  • -Wno-analyzer-write-to-string-literal โ€” ๐ŸŒท This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-write-to-string-literal to turn it off. It flags paths where the analyzer detects an attempt to write through a pointer to a string literal. Same deal though, the analyzer doesn't prioritize these, so false negatives are more likely compared to other warnings ๐Ÿซฃ
  • -Wno-analyzer-use-of-uninitialized-value โ€” ๐Ÿญ This warning requires -fanalyzer, which enables it. Use -Wno-analyzer-use-of-uninitialized-value to turn it off. It flags paths where an uninitialized value gets used ๐Ÿฅบ

Okay, so the analyzer has hardcoded knowledge about how these memory-management functions behave ๐Ÿง โœจ

  • alloca
  • The built-in functions __builtin_alloc, __builtin_alloc_with_align, __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 the behavior of these functions for working with file descriptors ๐Ÿ“‚

  • open
  • close
  • creat
  • dup, dup2 and dup3
  • isatty
  • pipe, and pipe2
  • read
  • write
  • socket, bind, listen, accept, and connect

And these functions for working with <stdio.h> streams ๐ŸŒŠ

  • 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 finally, these ones too ๐Ÿ’…

  • 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

In addition, various functions with an __analyzer_ prefix have special meaning to the analyzer. These are described in the GCC Internals manual ๐Ÿ“– Various internal parameters, settable via --param, are used to control the exploration, and those are documented in the GCC Internals manual too ๐Ÿ”

The following options control the analyzer ๐ŸŽ€

  • -fanalyzer-assume-nothrow โ€” okay so by default, if -fexceptions is on, the analyzer assumes that calling any function without the "nothrow" attribute could throw an exception ๐Ÿ˜ญ. This is amazing for catching paths that leak because exceptions skip the clean-up code, but it can lead to false positives when you're using headers where the author didn't add the "nothrow" attribute ๐Ÿ™ˆ.
    • If -fanalyzer-assume-nothrow is enabled, the analyzer assumes external functions never throw exceptions โœจ. This can reduce the "noise" when you turn on -fexceptions for C code, but it could hide real issues if something the C code calls can raise an exception ๐Ÿ’€.
  • -fanalyzer-call-summaries โ€” this one simplifies interprocedural analysis by computing the effect of certain calls instead of exploring every path through the function from the callsite to each possible return ๐Ÿง . Only functions with more than one call site, and that are sufficiently complicated, get call summaries ๐Ÿ’….
  • -fanalyzer-checker=name โ€” restricts the analyzer to run just the named checker, and enables it ๐ŸŽฏ.
  • -fanalyzer-debug-text-art-headings โ€” this is for analyzer developers only ๐Ÿ› ๏ธ. If enabled, the analyzer adds extra annotations to any diagrams it generates ๐ŸŽจ.
  • -fno-analyzer-feasibility โ€” this one's for analyzer developers too ๐Ÿง‘โ€๐Ÿ’ป. By default, the analyzer checks that there's a feasible control flow path for each diagnostic it emits, meaning the conditions that hold aren't mutually exclusive. Diagnostics with no feasible path get rejected ๐Ÿšซ. This filtering can be suppressed with -fno-analyzer-feasibility, which is handy for debugging issues in this code ๐Ÿ”.
  • -fanalyzer-fine-grained โ€” does nothing ๐Ÿคทโ€โ™€๏ธ. It's only kept around for backward compatibility ๐Ÿ•ฐ๏ธ.
  • -fanalyzer-show-duplicate-count โ€” another developer-only option ๐Ÿ™‹โ€โ™€๏ธ. If several diagnostics are detected as duplicates of each other, this emits a note when reporting the best diagnostic, giving the number of additional diagnostics the deduplication logic suppressed ๐Ÿ”ข.
  • -fanalyzer-show-events-in-system-headers โ€” by default, the analyzer keeps simplified diagnostic paths tidy by hiding events fully located within a system header ๐Ÿงน. With -fanalyzer-show-events-in-system-headers, those events stop getting suppressed ๐Ÿ‘€.
  • -fno-analyzer-simplify-supergraph โ€” developer-only vibes ๐Ÿงช. By default, the analyzer simplifies the program supergraph before analyzing it. With -fno-analyzer-simplify-supergraph, that simplification gets skipped, which is useful for debugging issues with it ๐Ÿ›.
  • -fno-analyzer-state-merge โ€” developer-only again ๐Ÿ’ป. By default, the analyzer merges sufficiently similar states at each program point while it builds its "exploded graph" ๐Ÿงฉ. With -fno-analyzer-state-merge, that merging is suppressed, handy for debugging state-handling issues ๐Ÿ”ง.
  • -fno-analyzer-state-purge โ€” for analyzer developers ๐Ÿซถ. By default, the analyzer purges aspects of state at a program point that don't seem relevant anymore, like the values of locals that aren't accessed later in the function and aren't relevant to leak analysis ๐Ÿ—‘๏ธ. With -fno-analyzer-state-purge, that purging is suppressed, for debugging state-handling issues ๐Ÿ•ต๏ธโ€โ™€๏ธ.
  • -fno-analyzer-suppress-followups โ€” developer-only ๐Ÿ™ƒ. By default, the analyzer stops exploring an execution path after hitting certain diagnostics, so it doesn't spam you with a cascade of follow-up diagnostics ๐Ÿ›‘. The diagnostics that end 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 those paths even after such diagnostics ๐ŸŒŠ. That can be helpful for debugging the analyzer, or for microbenchmarks that detect undefined behavior ๐Ÿงซ.

  • -fanalyzer-transitivity โ€” this one enables transitivity of constraints within the analyzer ๐Ÿ”—.
  • -fno-analyzer-undo-inlining โ€” developer-only ๐Ÿง‘โ€๐Ÿ”ฌ. -fanalyzer runs relatively late compared to other code analysis tools, so some optimizations have already been applied. In particular, function inlining may have happened, which means 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 emit events showing the inlined calls โœจ. With -fno-analyzer-undo-inlining, that reconstruction is disabled, which may help when debugging issues in the analyzer ๐Ÿ› ๏ธ.
  • -fanalyzer-verbose-edges โ€” for analyzer developers ๐Ÿ“š. It turns on more verbose, lower-level detail in the descriptions of control flow within diagnostic paths ๐ŸŒ€.
  • -fanalyzer-verbose-state-changes โ€” also for analyzer developers ๐Ÿง‘โ€๐Ÿ’ป. It turns on more verbose, lower-level detail in the descriptions of events relating to state machines within diagnostic paths ๐ŸŽข.
  • -fanalyzer-verbosity=level โ€” this one controls how complex the control flow paths are that get emitted for analyzer diagnostics ๐Ÿ“œ. The level can be one of:
    • 0 โ€” interprocedural call and return events are displayed, along with the most pertinent state-change events for a diagnostic ๐Ÿ“ž. For example, for a double-"free" diagnostic, both calls to "free" will show up ๐Ÿ‘ฏ.
    • 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 control flow events that matter for triggering the issue, like "true path taken" at a conditional ๐Ÿ”€. This is the default, so it's the main character level ๐Ÿ‘‘.
    • 3 โ€” same as the previous level, but shows all control flow events, not just the significant ones ๐ŸŒŠ.
    • 4 โ€” for analyzer developers only ๐Ÿค“. It adds various other events meant 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 that's suitable for viewing with GraphViz, to file.callgraph.dot ๐Ÿ—บ๏ธ.
  • -fdump-analyzer-exploded-graph โ€” dumps a representation of the "exploded graph" that's suitable for viewing with GraphViz, to file.eg.dot ๐ŸŽจ. The nodes are color-coded based on state-machine states, so state changes really pop ๐Ÿ’–.
  • -fdump-analyzer-exploded-nodes โ€” emits diagnostics showing where nodes in the "exploded graph" sit 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, named file.eg-id.txt ๐Ÿ“‚. Heads up, this is typically a large number of dump files ๐Ÿ˜ญ.
  • -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 GraphViz, to filenames like 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 GraphViz, to filenames like file.*.infinite-loop.dot ๐ŸŒ€.
  • -fdump-analyzer-json โ€” dumps a compressed JSON representation of analyzer internals to file.analyzer.json.gz ๐Ÿ—œ๏ธ. Heads up, the precise format is subject to change โš ๏ธ.
  • -fdump-analyzer-state-purge โ€” same idea as -fdump-analyzer-supergraph, so it dumps a "supergraph" representation suitable for GraphViz ๐Ÿ—บ๏ธ. But this one annotates the graph with info on what state will be purged at each node ๐Ÿงน. The graph is written to file.state-purge.dot ๐Ÿ’œ.
  • -fdump-analyzer-supergraph โ€” dumps representations of the "supergraph" suitable for GraphViz to file.supergraph.index.kind.dot ๐ŸŒˆ. These show all of the control flow graphs in the program at various stages of the analysis ๐Ÿ“ˆ. The precise set of dumps and what they show is subject to change ๐Ÿ™ˆ.
  • -fdump-analyzer-untracked โ€” emits custom warnings with internal details meant for analyzer developers ๐Ÿšจ.

โœจ Options for Debugging Your Program โœจ

To get GCC to emit extra info for a debugger, you almost always just need to add -g to your other options ๐Ÿž. Some debug formats can co-exist (like DWARF with CTF) when each one is explicitly enabled by adding its own command line option to your other options ๐Ÿซถ.

GCC lets you use -g with -O ๐Ÿ’…. The shortcuts taken by optimized code can be a little surprising ๐Ÿ˜ณ: some variables you declared may not exist at all, flow of control may briefly move somewhere you didn't expect, some statements may not execute because they compute constant results or their values are already at hand, and some statements may execute in different places because they've been moved out of loops ๐ŸŒ€. Still, it's totally possible to debug optimized output, which makes it reasonable to use the optimizer for programs that might have bugs ๐Ÿ’–.

If you're not using some other optimization option, consider using -Og with -g ๐Ÿงš. With no -O option at all, some compiler passes that collect info useful for debugging don't run, so -Og may give you a better debugging experience โœจ.

  • -g, --debug โ€” produces debugging information in the operating system's native format (stabs, COFF, XCOFF, or DWARF) ๐ŸŽ€. GDB can work with this debugging information ๐Ÿฆ‹.

On most systems that use stabs format, -g unlocks extra debugging info that only GDB can actually use ๐Ÿ”. It makes debugging in GDB way smoother, but it can make other debuggers crash or refuse to read the program ๐Ÿ’€. If you want to be certain whether that extra info gets generated, use -gvms instead (more on that below) โœจ.

  • -ggdb โ€” Asks for the most expressive debug format available, which is DWARF, then stabs, then the native format if neither of those is supported ๐Ÿฆ‹. GDB extensions get included too, if at all possible ๐Ÿ’–.
  • -gdwarf, -gdwarf-version โ€” Makes DWARF debug info, as long as the target supports it ๐ŸŒˆ. The version can be 2, 3, 4 or 5. Most targets default to version 5, except VxWorks, TPF and Darwin / macOS (which default to version 2) and AIX (which defaults to version 4) ๐Ÿ“.
    • 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 to get the full benefit. Version 5 needs GDB 8.0 or higher.
    • GCC no longer supports DWARF Version 1, which is very different from Version 2 and later. For historical reasons, some other DWARF-related options, such as -fno-dwarf2-cfi-asm, still have Version 2 in their names, but they apply to every DWARF version that's currently supported ๐Ÿ“œ.
  • -gbtf โ€” Requests BTF debug info. BTF is the default debugging format for the eBPF target ๐Ÿงš. On other targets, like x86, BTF can be generated alongside DWARF if you turn on both formats explicitly with their own command-line options.
  • -gprune-btf, -gno-prune-btf โ€” Prunes BTF info before it gets 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. 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 they were only forward declarations in the input.
    • This option shrinks the BTF output a ton, but you lose a lot of detailed type info ๐Ÿ˜ญ. It's mainly useful for the BPF target, where it keeps the object small and drops BTF info that doesn't matter for loading the BPF program.
    • It's turned on by default for the BPF target when BTF info is being generated.
  • -gctf, -gctflevel โ€” Requests CTF debug info, and the level decides how much gets made ๐ŸŽ€. If you use -gctf without a level, the default is 2. CTF can be generated alongside DWARF if you turn on both formats explicitly with their own command-line options.
    • Level 0 produces no CTF debug info at all, so -gctf0 cancels out -gctf ๐Ÿ™….
    • Level 1 produces CTF info for tracebacks only. That includes callsite info but not type info.
    • Level 2 produces type info only for entities (functions, data objects etc.) at file scope or global scope.
  • -gvms โ€” Makes debug info in the Alpha/VMS debug format, if that's supported. It's the format DEBUG uses on Alpha/VMS systems ๐Ÿ’œ.
  • -gcodeview โ€” Makes debug info in the CodeView format, if that's supported. Microsoft Visual C++ uses this one on Windows ๐ŸชŸ.
  • -glevel, -ggdblevel, -gvmslevel โ€” Requests debug info and uses the level to set how much you get ๐Ÿ“Š. The default level is 2.
    • Level 0 produces no debug info at all, so -g0 cancels out -g.
    • Level 1 produces minimal info, enough for backtraces in parts of the program you don't plan to debug. That includes descriptions of functions and external variables, plus line number tables, but no info about local variables.
    • Level 3 adds extra info, like all the macro definitions in the program. Some debuggers can do macro expansion when you use -g3 ๐Ÿ”Ž.
    • If you use several -g options, with or without level numbers, the last one wins ๐Ÿ‘‘.
    • -gdwarf doesn't accept a concatenated debug level, so it won't get confused with -gdwarf-level. Use an extra -glevel option to change the debug level for DWARF instead ๐Ÿซถ.
  • -fno-eliminate-unused-debug-symbols โ€” By default, no debug info gets produced for symbols that aren't actually used. Use this if you want debug info for all symbols ๐Ÿ™ˆ.
  • -femit-class-debug-always โ€” Instead of emitting debug info for a C++ class in just one object file, this emits it in every object file that uses the class. Only use it with debuggers that can't handle the way GCC normally emits class debug info, because it can make the debug info up to twice as big ๐Ÿ˜ฌ.
  • -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 makes the debug info in the output file smaller, but it costs more link time. Merging is enabled by default ๐Ÿงต.
  • -fdebug-prefix-map=old=new โ€” When compiling files in directory old, the debug info describes them as if they lived in directory new instead ๐Ÿ—บ๏ธ. You can use it to swap a build-time path for an install-time path in the debug info. You can also turn an absolute path into a relative one by using . for new. That can give more reproducible builds, which don't depend on location, but you might 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 ๐Ÿ•ต๏ธโ€โ™€๏ธ. It works out where variables are stored at each position in the code, so better debug info gets generated (if the debug format supports that). It's enabled by default when you compile with optimization (-Os, -O, -O2, ...), with debug info (-g), and the debug info format 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 turned on even when var-tracking is off. In that case the annotations get created and maintained, then thrown away at the end.
    • By default, this flag is enabled together with -fvar-tracking, except when selective scheduling is enabled.
  • -fvar-tracking-uninit, -fno-var-tracking-uninit โ€” Does variable tracking and also marks uninitialized variables in the debug info ๐Ÿท๏ธ. This flag is enabled by default by -fvar-tracking, and it also implies -fvar-tracking. To do variable tracking without marking uninitialized variables, use -fvar-tracking -fno-var-tracking-uninit.
  • -gsplit-dwarf โ€” If DWARF debug info is enabled, it pulls out as much of that info as possible into a separate output file with the .dwo extension ๐Ÿ“ฆ. This lets your build system avoid linking files that have 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 one ๐Ÿ”ข. The default depends on the target, but on most targets it's -gdwarf32. The 32-bit format is smaller, but none of the DWARF debug info sections can go past 2GiB. The 64-bit format allows bigger debug info, though 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 โ€” Generates the DWARF .debug_pubnames and .debug_pubtypes sections ๐Ÿ“š.
  • -ggnu-pubnames โ€” Generates .debug_pubnames and .debug_pubtypes in a format that's suitable for converting into a GDB index. This is only useful with a linker that can produce GDB index version 7 ๐Ÿงฉ.
  • -gno-pubnames โ€” Doesn't generate the DWARF .debug_pubnames and .debug_pubtypes sections ๐Ÿšซ.
  • -fdebug-types-section โ€” With DWARF Version 4 or higher, type DIEs can go into their own .debug_types section instead of living in .debug_info ๐Ÿ—‚๏ธ. It's more efficient to put them in a separate comdat section, since the linker can then remove duplicates. But not every DWARF consumer supports .debug_types yet, and for some objects it makes the debug info bigger instead of smaller ๐Ÿ˜ต.
  • -grecord-gcc-switches, -gno-record-gcc-switches โ€” Appends the command-line options that might affect code generation to the DW_AT_producer attribute in DWARF debug info ๐Ÿ“. The options are joined with spaces, separating them from each other and from the compiler version. It's enabled by default. See also -frecord-gcc-switches for another way to store compiler options in the object file.
  • -gstrict-dwarf โ€” Disallows using extensions from a later DWARF standard version than the one you picked with -gdwarf-version ๐Ÿšง. On most targets, using non-conflicting DWARF extensions from later standard versions is allowed.
  • -gno-strict-dwarf โ€” Allows using extensions from a later DWARF standard version than the one you picked with -gdwarf-version ๐ŸŒธ.
  • -gas-loc-support: okay so this one tells the compiler the assembler speaks .loc directives ๐Ÿงš, which means the compiler can let the assembler build the DWARF2+ line number tables. Ngl that's super desirable, because assembler-made line-number tables are a lot more compact than the ones the compiler can cook up on its own ๐Ÿ’…. It's on by default if GCC's configure step found the assembler supports those directives โœจ.
  • -gno-as-loc-support: the opposite energy ๐Ÿ™…โ€โ™€๏ธ. It forces GCC to generate DWARF2+ line number tables internally, but only if DWARF2+ line number tables are being generated in the first place.
  • -gas-locview-support: tells the compiler the assembler can handle "view" assignment and reset assertion checking inside .loc directives ๐Ÿฆ‹. It's on by default if, at GCC configure time, the assembler was found to support them.
  • -gno-as-locview-support: forces GCC to assign view numbers internally ๐Ÿซถ, but only when -gvariable-location-views has been explicitly requested.
  • -gcolumn-info, -gno-column-info: puts location column info into the DWARF debugging information, not just file and line ๐ŸŽ€. It's on by default, bestie.
  • -gstatement-frontiers, -gno-statement-frontiers: GCC drops markers into its internal representation at the start of each statement and keeps them roughly in place through the whole compile ๐Ÿ“. Then it uses them to steer where is_stmt markers show up in the line number table. This is on 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: adds progressive view numbers, implied from the line number table, onto the variable location lists ๐Ÿ‘‘. This lets debug info consumers inspect state at certain points in the program, even when no instructions tied to those source locations are present there ๐Ÿ”. If the assembler can't handle view numbers in line number tables, the compiler emits the line number table itself, which makes things 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. They just won't get any benefit from them either ๐Ÿคทโ€โ™€๏ธ.

This is on by default when outputting DWARF 2 debug info at the normal level, as long as there is assembler support, -fvar-tracking-assignments is enabled, and -gstrict-dwarf is not. When assembler support is missing, it can still be enabled, but it forces GCC to output internal line number tables. And if -ginternal-reset-location-views is not enabled, that most certainly leads to silently mismatching location views ๐Ÿ˜ญ.

โš ๏ธ Heads up, bestie: there's a proposed view-number representation 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, so they'd be unable to decode location lists using it ๐Ÿ’€.
  • -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 requires the compiler to have very accurate instruction length estimates, which isn't always the case. It may also silently generate incorrect view lists when using an assembler that doesn't support location view lists. The GNU assembler flags any such error as a "view number mismatch". It's 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 are 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 the capabilities of the assembler and linker used. type may be one of none (don't compress debug sections), zlib (use zlib compression in ELF gABI format), or zstd (use zstd compression in ELF gABI format). If the linker doesn't support writing compressed debug sections, the option is 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 at 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 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 the struct-like types GCC generates debug info for ๐Ÿ”. The intent is to reduce duplicate struct debug info between different object files within the same program. It's the detailed version of -femit-struct-debug-reduced and -femit-struct-debug-baseonly, and it serves most needs. A specification has 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 arise through pointers to structs. That is, when use of an incomplete struct is valid, the use is indirect. An example is struct one direct; and 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 does not yet implement them.
    • The third word specifies the source files for those structs for which the compiler should emit debug info. The values none and any have the normal meaning. The value base means that the base of the name of the file in which 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 determine 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 it's useful to have GCC emit debug info for all types declared in a compilation unit, used or not. For example, if in the debugger you want to cast a value to a type that isn't actually used in your program (but is declared). More often, though, this just results in a significant amount of wasted space ๐Ÿ™ƒ.

โœจ Options That Control Optimization โœจ

These options control various sorts of optimizations ๐Ÿช„.

Without any optimization option, the compiler's goal is to reduce the cost of compilation and to make debugging produce the expected results. Statements are independent: if you stop the program with a breakpoint between statements, you can then assign a new value to any variable or change the program counter to any other statement in the function and get exactly the results you expect from the source code ๐ŸŽฏ.

Turning on optimization flags makes the compiler attempt to improve the performance and/or code size at the expense of compilation time and possibly the ability to debug the program ๐Ÿ‹๏ธโ€โ™€๏ธ.

The compiler performs optimization based on the knowledge it has of the program. Compiling multiple files at once to a single output file mode allows the compiler to use information gained from all of the files when compiling each of them ๐Ÿง .

Not all optimizations are controlled directly by a flag. Only optimizations that have a flag are listed in this section.

Most optimizations are completely disabled at -O0 or if an -O level is not set on the command line, even if individual optimization flags are specified. Similarly, -Og suppresses many optimization passes ๐Ÿ™ˆ.

Depending on the target and how GCC was configured, a slightly different set of optimizations may be enabled at each -O level than those listed here. You can invoke GCC with -Q --help=optimizers to find out the exact set of optimizations that are enabled at each level ๐Ÿ”Ž.

  • -O, -O1, --optimize: optimize, bestie ๐Ÿ”ฅ. Optimizing compilation takes somewhat more time, and a lot more memory for a large function.

Okay so with -O, the compiler tries to shrink your code and make it run faster ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ, but it won't do anything that takes forever to compile. Lowkey the perfect balance, bestie โœจ

If you're dealing with huge machine-generated code, -O is the vibe ๐Ÿ’…. It's the recommended level because it balances compile time and memory use. The higher levels run optimizations with way more algorithmic complexity than plain -O does, so they cost more.

-O turns on this squad of 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 performs nearly all supported optimizations that don't involve a space-speed tradeoff. Compared to -O, this option increases both the compilation time and the performance of the generated code, so it's a glow-up with a cost.

-O2 turns on all optimization flags specified by -O1. It also turns on these extras:

-falign-functions  -falign-jumps -falign-labels  -falign-loops
-fcaller-saves -fcode-hoisting -fcrossjumping -fcse-follow-jumps
-fcse-skip-blocks -fdelete-null-pointer-checks -fdep-fusion
-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
-fspeculatively-call-stored-functions -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
Heads up, bestie ๐Ÿšจ: please check the warning under -fgcse about invoking -O2 on programs that use computed gotos. It's important, no cap.
NOTE ๐ŸŽ€: In Ubuntu 8.10 and later versions, -D_FORTIFY_SOURCE=2, and in Ubuntu 24.04 and later versions, -D_FORTIFY_SOURCE=3, is set by default. It's activated when -O is set to 2 or higher. This enables additional 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 every optimization specified by -O2, and it also brings along these flags:

-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, so you get it without asking ๐Ÿ’….

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. Many optimizations depend on code analysis or canonicalization passes that -O turns on, so running individual optimization passes in isolation wouldn't be useful anyway. Not worth the chaos, bestie.


๐Ÿ“ -Os ๐Ÿ“

Optimize for size! ๐Ÿงธ -Os enables all the -O2 optimizations except the ones that often make your code bigger:

-falign-functions  -falign-jumps -falign-labels  -falign-loops
-fprefetch-loop-arrays  -freorder-blocks-algorithm=stc

It also enables -finline-functions, tells the compiler to tune for code size instead of execution speed, and runs further optimizations designed to shrink your code.


โšก -Ofast โšก

Disregard strict standards compliance. ๐Ÿ˜ˆ -Ofast enables all -O3 optimizations. It also enables 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.

โš ๏ธ Danger zone, bestie: because -Ofast enables optimizations that aren't valid for all standard-compliant programs, it can change how your code behaves. Only reach for it if you've checked that your code can handle it. ๐Ÿ’€

๐Ÿ” -Og ๐Ÿ”

Optimize while keeping debugging in mind! ๐Ÿ•ต๏ธโ€โ™€๏ธ -Og should be the level of choice for the standard edit-compile-debug cycle. It gives you a reasonable blend of optimization, fast compilation and a good debugging experience, especially for code with a high abstraction penalty. Unlike -O0, this enables -fvar-tracking-assignments and -fvar-tracking, which handle debug information in the prologue and epilogue of functions better than -O0 does.

Like -O0, -Og completely skips a number of optimization passes, so the individual options that control them have no effect. Otherwise, -Og enables all -O1 optimization flags except the ones known to greatly interfere with 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. Main character energy, the last one wins ๐Ÿ‘‘.

Options of the form -fflag specify machine-independent flags. Most flags have both positive and negative forms, and the negative form of -ffoo is -fno-foo. In the list below, only one of the forms is listed, the one you typically use. You can figure out the other form by either removing no- or adding it. Easy peasy ๐ŸŒˆ.

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" of the optimizations to be performed is desired. Only touch these if you know what you're doing, bestie ๐Ÿซถ.

  • -fno-defer-pop โ€” For machines that must pop arguments after a function call, this always pops the arguments as soon as each function returns. At levels -O1 and higher, -fdefer-pop is the default, which lets the compiler let arguments pile up on the stack for several function calls and then pop them all at once.
  • -fforward-propagate โ€” Runs 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 are performed and the second one is scheduled after loop unrolling.

This option is enabled by default at optimization levels -O1, -O2, -O3 and -Os.

  • -favoid-store-forwarding / -fno-avoid-store-forwarding โ€” Lots of CPUs stall for many cycles when a load partially depends on previous smaller stores. This pass tries to spot 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 turns off floating-point expression contraction. -ffp-contract=fast enables it, such as forming fused multiply-add operations, if 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.

  • -ffp-int-builtin-inexact โ€” Allows 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, does not. ๐Ÿค“

The default is -fno-fp-int-builtin-inexact, which disallows the exception from being raised, unless C17 or an earlier C standard is selected. This option does nothing unless -ftrapping-math is in effect.

NOTE ๐ŸŒท: Even if -fno-fp-int-builtin-inexact is used, if these functions generate a call to a library function, the "inexact" exception may still be raised if the library implementation doesn't follow TS 18661.
  • -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 has no effect, 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 bestie, this one is a CRC detective ๐Ÿ•ต๏ธโ€โ™€๏ธโœจ It spots loops doing CRC math (that's polynomial long division, so fancy ๐Ÿงฎ) and swaps them for a faster version ๐Ÿš€ It recognizes 8, 16, 32, and 64 bit CRCs using a constant polynomial without the leading 1 bit, in both bit-forward and bit-reversed flavors ๐Ÿฆ‹ If the target has a CRC instruction and your polynomial matches it, it generates that instruction ๐Ÿ’– Otherwise, if the target has a carry-less-multiplication instruction, it uses that to make the CRC ๐ŸŒธ and if neither is available, it falls back to a table-based CRC ๐Ÿ“
    • Enabled by default at -O2 and higher. ๐Ÿ‘‘
  • -foptimize-sibling-calls โ€” this one optimizes sibling and tail recursive calls, so your recursion stops wasting stack space ๐Ÿงš
    • Enabled at levels -O2, -O3, -Os.
  • -foptimize-strlen โ€” it glows up standard C string functions like "strlen", "strchr", or "strcpy", plus their "_FORTIFY_SOURCE" counterparts, into faster alternatives ๐ŸŽ๏ธ๐Ÿ’จ
    • Enabled at levels -O2, -O3.
  • -finline-atomics, -fno-inline-atomics โ€” inlines __atomic operations whenever a lock-free instruction sequence is available ๐Ÿ”’ This optimization is enabled by default. ๐Ÿ’…
  • -finline-stringops[=fn] โ€” expands memory and string operations (for now, only "memset") inline, even when the length is variable or big enough to need looping ๐Ÿงต This is most useful alongside -ffreestanding and -fno-builtin.
    • In some circumstances it can take advantage of known alignment and length multipliers, but even then it might be less efficient than optimized runtime implementations ๐Ÿ™ˆ It can also bloat code size so much that a less performant but shared implementation ends up running faster, thanks to better use of code caches.
    • This option is disabled by default. ๐Ÿซถ
  • -fno-inline โ€” don't expand any functions inline unless they're marked with the "always_inline" attribute ๐Ÿšซ This is the default when not optimizing.
    • You can exempt single 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 function call code, so the whole program gets smaller ๐Ÿ“‰ The compiler heuristically decides which functions are simple enough for this, and it applies to all functions, even ones not declared inline โœจ
    • Enabled at levels -O2, -O3, -Os.
  • -findirect-inlining โ€” inlines indirect calls that are known at compile time thanks to previous inlining ๐Ÿง  This only does anything 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 ones not declared inline ๐Ÿ’– The compiler heuristically decides which are worth integrating.
    • If all calls to a given function get integrated, and the function is declared "static", then it's normally not output as assembler code in its own right. ๐ŸŽ€
    • Enabled at levels -O2, -O3, -Os. Also enabled by -fprofile-use and -fauto-profile. ๐Ÿ”ฅ
  • -finline-functions-called-once โ€” considers all "static" functions called once for inlining into their caller, even if they aren't marked "inline" ๐ŸŒˆ If a call to a function gets integrated, the function isn't output as assembler code on its own either.
    • Enabled at levels -O1, -O2, -O3 and -Os, but not -Og.
  • -fearly-inlining โ€” inlines functions marked "always_inline", plus functions whose body seems smaller than the call overhead, early ๐Ÿƒโ€โ™€๏ธ This happens before the -fprofile-generate instrumentation and the real inlining pass. It makes profiling significantly cheaper and usually makes inlining faster on programs with large 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 caps the size of functions it'll inline ๐Ÿ“ This flag gives you coarse control over that cap. n is the size of functions that can be inlined, measured in pseudo instructions.
    • Inlining is actually controlled by a bunch of internal parameters, which are documented in the GCC Internals manual and shouldn't normally be set directly ๐Ÿ™…โ€โ™€๏ธ
    • Note: there may be no value of -finline-limit that results in default behavior. ๐Ÿคทโ€โ™€๏ธ
Note: a pseudo instruction, in this particular context, is an abstract measurement of a function's size ๐Ÿ’€ It in no way represents a count of assembly instructions, so its exact meaning might change from one release to another.
  • -fno-keep-inline-dllexport โ€” this is a more fine-grained version of -fkeep-inline-functions, and it 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 the function got inlined into all of its 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 the variables aren't referenced ๐Ÿ“
    • GCC enables this option 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 option is the default for optimized compilation if the assembler and linker support it. Use -fno-merge-constants to inhibit this behavior.
    • 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 considers things like even constant initialized arrays, or initialized constant variables with integral or floating-point types ๐Ÿง
โš ๏ธ Bestie, listen: languages like C or C++ require each variable, including multiple instances of the same variable in recursive calls, to have distinct locations. Using this option results in non-conforming behavior ๐Ÿ˜ญ
  • -fmodulo-sched โ€” performs swing modulo scheduling immediately before the first scheduling pass ๐ŸŒ€ It looks at innermost loops and reorders their instructions by overlapping different iterations.
  • -fmodulo-sched-allow-regmoves โ€” 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 is only effective with -fmodulo-sched enabled.
  • -fno-branch-count-reg โ€” disables the optimization pass that scans 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 is only meaningful on architectures that support such instructions, which include x86, PowerPC, IA-64 and S/390.
    • Note that -fno-branch-count-reg doesn't remove the decrement and branch instructions introduced into the generated instruction stream by other optimization passes.
    • The default is -fbranch-count-reg at -O1 and higher, except for -Og. ๐Ÿ’…
  • -fno-function-cse โ€” doesn't put function addresses in registers ๐Ÿ™…โ€โ™€๏ธ Instead, each instruction that calls a constant function contains the function's address explicitly.
    • This results in less efficient code, but some strange hacks that alter the assembler output may get confused by the optimizations performed when this option isn't used ๐Ÿ’€
    • The default is -ffunction-cse
  • -ffuse-ops-with-volatile-access โ€” allows limited optimization of operations with volatile memory access when doing so does not change the semantics outlined in
    • The default is -ffuse-ops-with-volatile-access ๐ŸŒˆ
  • -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 that ๐Ÿงธ
    • 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 one is found ๐Ÿฆ˜ If so, the first branch is redirected to either the destination of the second branch or a point immediately following 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, such as "long long" on a 32-bit system, the registers get split apart and allocated independently ๐Ÿง  This normally generates better code for those types, but may make debugging more difficult ๐Ÿ™ˆ
    • Enabled at levels -O1, -O2, -O3, -Os.
  • -fsplit-wide-types-early โ€” fully splits wide types early, instead of very late โฐ This option has no effect unless -fsplit-wide-types is turned on.
    • This is the default on some targets. โœจ
  • -fcse-follow-jumps โ€” in common subexpression elimination (CSE), it scans through jump instructions when the target of the jump isn't reached by any other path ๐Ÿ‘€ For example, when CSE encounters an "if" statement with an "else" clause, CSE follows the jump when the condition tested is false.
    • Enabled at levels -O2, -O3, -Os. ๐Ÿซถ
  • -fcse-skip-blocks โ€” okay bestie, this is basically -fcse-follow-jumps but with more โœจReACHโœจ ๐Ÿฆ‹ It makes CSE follow jumps that conditionally skip over blocks. When CSE hits a simple if with no else clause, -fcse-skip-blocks tells it to follow the jump right around the body of the if. ๐Ÿ’…

Enabled at levels -O2, -O3, -Os.

Wait, let me redo that properly, because I'm getting ahead of myself. Here it is in order:

โœจ OPTIONS (continued) โœจ

  • -fcse-skip-blocks โ€” this is giving "follow the jump" energy ๐Ÿฆ‹ It's similar to -fcse-follow-jumps, but CSE also follows jumps that conditionally skip over blocks. When CSE runs into a simple if with no else clause, -fcse-skip-blocks makes it follow the jump around the body of the if. ๐Ÿ’…

Enabled at levels -O2, -O3, -Os.

  • -frerun-cse-after-loop โ€” okay so this one re-runs common subexpression elimination after the loop optimizations finish ๐Ÿ”โœจ main character re-run energy ๐Ÿ’–

Enabled at levels -O2, -O3, -Os.

  • -fgcse โ€” slay ๐Ÿ’… this performs a global common subexpression elimination pass, and it also does global constant and copy propagation. iconic behavior ๐Ÿ‘‘
โš ๏ธ Heads up, bestie: when you're compiling a program that uses computed gotos (a GCC extension ๐Ÿ™ˆ), you might get better run-time performance if you disable the global CSE pass by adding -fno-gcse to the command line. no cap.

Enabled at levels -O2, -O3, -Os.

  • -fgcse-lm โ€” lowkey my favourite of the GCSE crew ๐ŸŒธ When it's enabled, global CSE tries to move loads that are only killed by stores into themselves. So a loop with a load/store sequence can become a load outside the loop and a copy/store inside the loop. ๐Ÿงš

Enabled by default when -fgcse is enabled.

  • -fgcse-sm โ€” store motion, babe ๐Ÿฆ‹ When it's enabled, a store motion pass runs after global CSE and tries to move stores out of loops. Used together with -fgcse-lm, a loop with a load/store sequence can turn into a load before the loop and a store after it. ๐Ÿซถ

Not enabled at any optimization level.

  • -fgcse-las โ€” this one is so precious ๐Ÿฅน It makes the global CSE pass eliminate redundant loads that come after stores to the same memory location (both partial and full redundancies). no notes ๐Ÿ’œ

Not enabled at any optimization level.

  • -fgcse-after-reload โ€” clean-up crew vibes ๐Ÿงนโœจ When it's enabled, a redundant load elimination pass runs after reload. Its whole purpose is to tidy up redundant spilling.

Enabled by -O3, -fprofile-use and -fauto-profile.

  • -faggressive-loop-optimizations โ€” the loop optimizer gets to use language constraints to work out bounds on how many iterations a loop does ๐Ÿ”ฅ It assumes loop code doesn't invoke undefined behavior, like causing signed integer overflows or out-of-bounds array accesses ๐Ÿ˜ญ Those bounds then guide loop unrolling, 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) may later be overridden with longer trailing arrays. It prevents certain optimizations that depend on knowing the array bounds. ๐ŸŽ€
  • -fcrossjumping โ€” performs the cross-jumping transformation, which unifies equivalent code and saves code size ๐Ÿ“ The resulting code may or may not perform better than without cross-jumping, so it's a mixed bag ngl.

Enabled at levels -O2, -O3, -Os.

  • -fauto-inc-dec โ€” 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 ๐Ÿ—‘๏ธ๐Ÿ’… Enabled by default at -O1 and higher.
  • -fdse โ€” performs dead store elimination (DSE) on RTL ๐ŸŒˆ Enabled by default at -O1 and higher.
  • -fif-conversion โ€” attempts to transform conditional jumps into branch-less equivalents ๐Ÿฆ‹ This includes conditional moves, min, max, set flags and abs instructions, and some tricks doable with standard arithmetic. Whether conditional execution gets used on chips that have 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 so the C++ ABI wants 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, meaning two copies of the function. With this option, those two variants become thunks that call a common implementation. ๐Ÿงš

Enabled by -Os.

  • -fdelete-null-pointer-checks โ€” assumes programs can't safely dereference null pointers, and that no code or data element lives at address zero ๐Ÿ’€ This option enables simple constant folding optimizations at all optimization levels. Other GCC passes use this flag to control global dataflow analyses that eliminate useless null pointer checks. These assume that a memory access to address zero always results in a trap, so if a pointer is checked after it has already been dereferenced, it can't be null.
โš ๏ธ Careful, bestie: in some environments this assumption isn't true. Use -fno-delete-null-pointer-checks to disable this optimization for programs that depend on that behavior.

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 โ€” attempts 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 โ€” attempts to convert calls to virtual functions into speculative direct calls ๐Ÿ”ฎ Based on analysis of the type inheritance graph, it figures out the set of likely targets for a given call. If the set is small (preferably size 1), the call becomes a conditional that picks between a direct and an indirect call. The speculative calls enable more optimizations, like inlining. If they seem useless after further optimization, they get converted back into their original form.
  • -fdevirtualize-at-ltrans โ€” streams extra information needed for aggressive devirtualization when running the link-time optimizer in local transformation mode ๐Ÿ“ฆ This enables more devirtualization but significantly increases the size of streamed data. For this reason it's disabled by default.
  • -fexpensive-optimizations โ€” performs a number of minor optimizations that are relatively expensive ๐Ÿ’ธ

Enabled at levels -O2, -O3, -Os.

  • -fext-dce, -fno-ext-dce โ€” performs dead code elimination on zero and sign extensions, using special dataflow analysis ๐Ÿงผ
  • -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 is only 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's value persisting past the object's lifetime, use this flag to turn that optimization off. 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, use -flifetime-dse=1. The default behavior can be explicitly selected with -flifetime-dse=2. -flifetime-dse=0 is equivalent to -fno-lifetime-dse.
  • -flive-range-shrinkage โ€” attempts to decrease register pressure through register live range shrinkage ๐ŸŒฌ๏ธ This is 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* โ€” picks 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 loops with small register pressure as the regions. This 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 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.

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: bestie, this one is a hard pass ๐Ÿ™…โ€โ™€๏ธ It disables sharing of stack slots allocated for pseudo-registers ๐Ÿงฑ Every pseudo-register that doesn't get a hard register gets its very own stack slot, so function stack frames end up larger ๐Ÿ“ฆ๐Ÿ’€
  • -flra-remat: okay this one is giving main character energy โœจ It turns on CFG-sensitive rematerialization in LRA ๐Ÿฆ‹ Instead of loading values of spilled pseudos, LRA tries to rematerialize (recalculate) them whenever that's profitable ๐Ÿงฎ Enabled at levels -O2, -O3, -Os ๐Ÿ’…
  • -fdelayed-branch: if the target machine supports it, GCC tries reordering instructions so they can use the instruction slots after delayed branch instructions ๐Ÿง  Enabled at levels -O1, -O2, -O3, -Os, but not at -Og ๐Ÿ™…โ€โ™€๏ธ
  • -fschedule-insns: if the target supports it, GCC reorders instructions to get rid of execution stalls caused by required data being unavailable โณ This helps machines with slow floating point or memory load instructions, since other instructions can get issued while you wait for the load or float result to show up ๐Ÿซถ It's conventionally enabled at -O2 and -O3, but lowkey plenty of targets override that ๐Ÿ˜ญ On x86 it's disabled at all levels, while on AArch64 it's only enabled at -O3 ๐ŸŒธ
  • -fschedule-insns2: same main vibe as -fschedule-insns, but this one adds an extra pass of instruction scheduling after register allocation ๐Ÿ” It's especially useful on machines with a relatively small number of registers, where memory load instructions take more than one cycle ๐Ÿงƒ Enabled at levels -O2, -O3, -Os โœจ
  • -fno-sched-interblock: disables 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: disables speculative motion of non-load instructions ๐Ÿ™…โ€โ™€๏ธ This is normally enabled when scheduling before register allocation, i.e. with -fschedule-insns or at -O2 or higher ๐Ÿซง
  • -fsched-pressure: turns on register pressure sensitive insn scheduling before register allocation ๐Ÿง  It only makes sense when scheduling before register allocation is enabled, i.e. with -fschedule-insns or at -O2 or higher ๐ŸŽ€ Using it can improve the generated code and shrink its size by stopping register pressure from climbing above the number of available hard registers, which would otherwise mean spills later in register allocation ๐Ÿซ 
  • -fsched-spec-load: allows speculative motion of some load instructions ๐Ÿ”ฎ It only makes sense when scheduling before register allocation, i.e. with -fschedule-insns or at -O2 or higher ๐ŸŒท
  • -fsched-spec-load-dangerous: allows speculative motion of more load instructions โš ๏ธ The name is literally a warning label ๐Ÿ’€ It only makes sense when scheduling before register allocation, i.e. with -fschedule-insns or at -O2 or higher ๐Ÿ™ˆ
  • -fsched-stalled-insns, -fsched-stalled-insns=n: this decides how many insns (if any!) can be moved prematurely from the queue of stalled insns into the ready list during the second scheduling pass ๐Ÿšš -fno-sched-stalled-insns means none get moved early, -fsched-stalled-insns=0 means there's no limit on how many queued insns can be moved prematurely, and -fsched-stalled-insns with no value is the same as -fsched-stalled-insns=1 ๐Ÿง
  • -fsched-stalled-insns-dep, -fsched-stalled-insns-dep=n: defines how many insn groups (cycles) get checked for a dependency on a stalled insn that's a candidate for premature removal from the queue of stalled insns ๐Ÿ” This only has an effect during the second scheduling pass, and only if -fsched-stalled-insns is used ๐Ÿซถ -fno-sched-stalled-insns-dep is equivalent to -fsched-stalled-insns-dep=0, and -fsched-stalled-insns-dep without a value is equivalent to -fsched-stalled-insns-dep=1 ๐Ÿ’–
  • -fsched2-use-superblocks: when scheduling after register allocation, this uses superblock scheduling ๐Ÿงฉ That allows motion across basic block boundaries, which gives faster schedules โšก Heads up, though: this one is experimental, because not all machine descriptions model the CPU closely enough to avoid unreliable results from the algorithm ๐Ÿฅบ It only makes sense after register allocation, i.e. with -fschedule-insns2 or at -O2 or higher ๐Ÿ’…
  • -fsched-group-heuristic: enables the group heuristic in the scheduler ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ It favors the instruction that belongs to a schedule group ๐ŸŽ€ This is enabled by default when scheduling is enabled, i.e. with -fschedule-insns or -fschedule-insns2 or at -O2 or higher ๐ŸŒˆ
  • -fsched-critical-path-heuristic: enables the critical-path heuristic in the scheduler ๐Ÿ›ค๏ธ It favors instructions on the critical path ๐Ÿ‘‘ This is enabled by default when scheduling is enabled, i.e. with -fschedule-insns or -fschedule-insns2 or at -O2 or higher โญ
  • -fsched-spec-insn-heuristic: enables the speculative instruction heuristic ๐Ÿ”ฎ It favors speculative instructions with greater dependency weakness ๐Ÿฆ‹ This is enabled by default when scheduling is enabled, i.e. with -fschedule-insns or -fschedule-insns2 or at -O2 or higher ๐Ÿซถ
  • -fsched-rank-heuristic: enables the rank heuristic ๐Ÿ† It favors the instruction belonging to a basic block with greater size or frequency ๐Ÿ“ˆ This is enabled by default when scheduling is enabled, i.e. with -fschedule-insns or -fschedule-insns2 or at -O2 or higher ๐Ÿ’œ
  • -fsched-last-insn-heuristic: enables the last-instruction heuristic ๐Ÿ•ฐ๏ธ It favors the instruction that's less dependent on the last instruction scheduled ๐Ÿงƒ This is enabled by default when scheduling is enabled, i.e. with -fschedule-insns or -fschedule-insns2 or at -O2 or higher ๐ŸŒธ
  • -fsched-dep-count-heuristic: enables the dependent-count heuristic ๐Ÿงฎ It favors the instruction that has more instructions depending on it ๐Ÿซ‚ This is enabled by default when scheduling is enabled, i.e. with -fschedule-insns or -fschedule-insns2 or at -O2 or higher โœจ
  • -fspeculatively-call-stored-functions: GCC attempts to convert indirect calls of function pointers into calls through pointers loaded from a structure field, but only if all visible stores to that field store just a single candidate ๐ŸŽฒ When it works, the call becomes a conditional that picks between the direct call and the original indirect one ๐Ÿง  These speculative calls often enable more optimizations, such as inlining ๐Ÿš€ And if they seem useless after further optimization, they get converted back into their original form ๐Ÿ”„
  • -freschedule-modulo-scheduled-loops: modulo scheduling is performed before traditional scheduling ๐Ÿ” If a loop is modulo scheduled, later scheduling passes may change its schedule, and this option is how you control that behavior ๐ŸŽ›๏ธ
  • -fselective-scheduling: schedules instructions using the selective scheduling algorithm ๐ŸŽฏ Selective scheduling runs instead of the first scheduler pass ๐Ÿซถ
  • -fselective-scheduling2: same energy, schedules instructions with the selective scheduling algorithm, but this one runs instead of the second scheduler pass ๐Ÿฅฐ
  • -fsel-sched-pipelining: enables software pipelining of innermost loops during selective scheduling ๐ŸŒ€ This option has no effect unless -fselective-scheduling or -fselective-scheduling2 is turned on ๐Ÿ™ˆ
  • -fsel-sched-pipelining-outer-loops: when pipelining loops during selective scheduling, this also pipelines outer loops ๐Ÿช† It has no effect unless -fsel-sched-pipelining is turned on ๐Ÿซ 
  • -fsemantic-interposition: okay this one is a lot ๐Ÿซ  Some object formats, like ELF, allow the dynamic linker to interpose symbols ๐Ÿ”€ That means for symbols exported from the DSO, the compiler can't do interprocedural propagation, inlining, or other optimizations, because it has to assume the function or variable in question might get swapped out ๐Ÿ˜ต This is useful for things like rewriting memory allocation functions with a debugging implementation, but it's expensive in terms of 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 ๐ŸŽ€ The flag has no effect for functions explicitly declared inline (where interposition is never allowed to change semantics), and for 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 ๐ŸŽ This flag is enabled by default at -O and higher โœจ
  • -fshrink-wrap-separate: shrink-wraps separate parts of the prologue and epilogue separately, so those parts only run when they're needed ๐ŸŒ™ This option is on by default, but has no effect unless -fshrink-wrap is also turned on and the target supports this ๐Ÿฆ‹
  • -fcaller-saves: enables allocating values to registers that get clobbered by function calls, by emitting extra instructions to save and restore those registers around such calls ๐Ÿงบ This allocation only happens when it seems to result in 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: uses 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 (this happens on targets that don't expose prologues and epilogues in RTL) ๐Ÿ™ƒ
  • -fconserve-stack: minimizes stack usage ๐Ÿฅน The compiler tries 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 โ€” okay so this one does reassociation on trees ๐Ÿฆ‹ It's switched on by default at -O1 and higher โœจ
  • -fcode-hoisting โ€” code hoisting, bestie ๐Ÿซถ It 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 ๐Ÿ’… On by default at -O2 and higher.
  • -ftree-pre โ€” partial redundancy elimination (PRE) on trees ๐ŸŒธ Enabled by default at -O2 and -O3.
  • -ftree-partial-pre โ€” makes PRE more aggressive ๐Ÿ”ฅ Enabled by default at -O3.
  • -ftree-forwprop โ€” forward propagation on trees ๐Ÿงš On by default at -O1 and higher.
  • -ftree-fre โ€” full redundancy elimination (FRE) on trees ๐Ÿ’– The tea is that FRE only considers expressions computed on all paths leading to the redundant computation, while PRE is the bigger, messier sibling. FRE's analysis is faster than PRE, though it exposes fewer redundancies. On by default at -O1 and higher.
  • -ftree-phiprop โ€” hoists loads from conditional pointers on trees โœจ This pass is enabled by default at -O1 and higher.
  • -fhoist-adjacent-loads โ€” speculatively hoists loads from both branches of an if-then-else, but only if the loads come from adjacent locations in the same structure and the target architecture has a conditional move instruction ๐ŸŽ€ On by default at -O2 and higher.
  • -ftree-copy-prop โ€” copy propagation on trees ๐Ÿฆ‹ This pass gets rid of unnecessary copy operations. On by default at -O1 and higher.
  • -fipa-pure-const โ€” figures out which functions are pure or constant ๐Ÿ”ฎ Enabled by default at -O1 and higher.
  • -fipa-reference โ€” finds which static variables do not escape the compilation unit ๐Ÿ‘€ Enabled by default at -O1 and higher.
  • -fipa-reference-addressable โ€” finds read-only, write-only and non-addressable static variables ๐Ÿ’… Enabled by default at -O1 and higher.
  • -fipa-reorder-for-locality โ€” groups call chains close together in the binary layout ๐Ÿงš The goal is better code locality and shorter jump distances between frequently called functions. Unlike -freorder-functions, this pass looks at the call chains between functions and groups them together, instead of putting all hot/normal/cold/never-executed functions into separate sections. Unlike -fprofile-reorder-functions, it aims to improve code locality throughout the runtime of the program rather than focusing on program startup.
This option is incompatible with an explicit -flto-partition= option, since it enforces a custom partitioning scheme.

If you use this option, 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 if possible ๐ŸŒˆ Enabled by default.
  • -fipa-pta โ€” interprocedural pointer analysis and interprocedural modification and reference analysis ๐Ÿง 
This one can cause excessive memory and compile-time usage on large compilation units, so be careful, bestie ๐Ÿ’€ It is not enabled by default at any optimization level.
  • -fipa-profile โ€” interprocedural profile propagation ๐Ÿ“ˆ Functions called only from cold functions are marked as cold. Functions executed once (such as "cold", "noreturn", static constructors or destructors) are identified too. Cold functions and loop less parts of functions executed once are then optimized for size. Enabled by default at -O1 and higher.
  • -fipa-modref โ€” interprocedural mod/ref analysis ๐Ÿฆ‹ This optimization analyzes the side effects of functions (memory locations that are modified or referenced), which lets the compiler optimize better across the function call boundary. On by default at -O1 and higher.
  • -fipa-cp โ€” interprocedural constant propagation ๐Ÿ‘‘ This optimization analyzes the program to figure out when values passed to functions are constants, then optimizes accordingly. It can substantially increase performance if your app passes constants to functions. Enabled by default at -O2, -Os and -O3. It's also enabled by -fprofile-use and -fauto-profile.
  • -fipa-cp-clone โ€” function cloning to make interprocedural constant propagation stronger ๐Ÿ“ When enabled, interprocedural constant propagation clones a function when an externally visible function can be called with constant arguments.
Because this optimization can create multiple copies of functions, it may significantly increase code size ๐Ÿ˜ญ Enabled by default at -O3. It's also enabled by -fprofile-use and -fauto-profile.
  • -fipa-bit-cp โ€” interprocedural bitwise constant propagation ๐Ÿ’œ When enabled, it does the bitwise version of the constant propagation. Enabled by default at -O2, and by -fprofile-use and -fauto-profile. It requires that -fipa-cp is enabled.
  • -fipa-vrp โ€” interprocedural propagation of value ranges ๐ŸŒธ When enabled, it propagates value ranges across functions. Enabled by default at -O2. It requires that -fipa-cp is enabled.
  • -fipa-icf-functions, -fipa-icf-variables, -fipa-icf โ€” Identical Code Folding ๐Ÿงš -fipa-icf-functions does it for functions, -fipa-icf-variables does it for read-only variables, and -fipa-icf does both. This reduces code size, but it may disturb unwind stacks by swapping a function out for an equivalent one with a different name. It works even better with link-time optimization enabled โœจ

Although the behavior is similar to the Gold Linker's ICF optimization, GCC ICF works on different levels, so the optimizations are not the same. There are equivalences that only GCC finds and equivalences that only Gold finds ๐Ÿฆ‹

-fipa-icf is enabled by default at -O2 and -Os.

  • -flate-combine-instructions โ€” turns on two instruction combination passes that run pretty late in the compilation process ๐Ÿ”ฅ 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 flag by default at -O2 and -Os.
  • -flive-patching=level โ€” controls GCC's optimizations so the output is suitable for live-patching ๐Ÿฉน Here's the deal: if an optimization uses a function's body (or info pulled from it) to change another function, that other function is called an impacted function. If a function gets patched, its impacted functions should be patched too. The 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 is cloned and its caller is changed to call the clone, or when a function's pureness/constness info is extracted to optimize its direct or indirect callers. Usually, the more IPA optimizations are on, the more impacted functions each function has. So IPA optimizations can be partially enabled at two different levels, which keeps the list manageable ๐Ÿงฎ

The level argument should be one of the following:

  • inline-clone โ€” only inlining and cloning optimizations are enabled, which includes inlining, cloning, interprocedural scalar replacement of aggregates and partial inlining. So when patching a function, all its callers and its clones' callers are impacted, and they need to be patched too.

-flive-patching=inline-clone 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 patching a static function, all its callers are impacted and need to be patched too.

On top of everything -flive-patching=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 from dereferencing a null pointer (with -fdelete-null-pointer-checks enabled) or from a division by zero ๐Ÿšง It isolates those paths from the main control flow and turns the statement with erroneous or undefined behavior into a trap. On by default at -O2 and higher.
  • -fisolate-erroneous-paths-attribute โ€” detects paths that trigger erroneous or undefined behavior because a null value is used in a way forbidden by a "returns_nonnull" or "nonnull" attribute ๐Ÿ™… It isolates those paths from the main control flow and turns the statement into a trap.
This is not currently enabled, but it may be enabled by -O2 in the future ๐Ÿ‘€
  • -ftree-sink โ€” forward store motion on trees ๐Ÿฆ‹ On by default at -O1 and higher.
  • -ftree-bit-ccp โ€” sparse conditional bit constant propagation on trees, plus pointer alignment info propagation ๐ŸŽ€ This pass only operates on local scalar variables. It's enabled by default at -O1 and higher, except for -Og. It requires that -ftree-ccp is enabled.
  • -ftree-ccp โ€” sparse conditional constant propagation (CCP) on trees ๐Ÿ’– This pass only operates on local scalar variables. Enabled by default at -O1 and higher.
  • -fssa-backprop โ€” propagates info about uses of a value up the definition chain to simplify the definitions ๐Ÿงน For example, this pass strips sign operations if the sign of a value never matters. Enabled by default at -O1 and higher.

Okay bestie, let's keep going ๐Ÿ’…โœจ These are the tree-level passes doing the behind-the-scenes cleanup, and they are giving main character energy ๐Ÿฆ‹

  • -fssa-phiopt โ€” pattern matching on SSA PHI nodes to tidy up conditional code ๐Ÿงš It's on by default at -O1 and higher, except for -Og ๐Ÿ’–
  • -ftree-switch-conversion โ€” turns simple initializations inside a switch into initializations from a scalar array ๐ŸŽ€ On by default at -O2 and higher.
  • -ftree-tail-merge โ€” hunts down identical chunks of code and, when it finds them, swaps one for a jump to the other ๐Ÿฆ‹ This is known as tail merging or cross jumping. On by default at -O2 and higher. You can cap how long this pass takes with the max-tail-merge-comparisons parameter and the max-tail-merge-iterations parameter ๐Ÿ™ˆ
  • -ftree-cselim โ€” conditional store elimination on trees ๐Ÿ’œ On by default at -O1 and higher, but only on targets that have conditional move instructions.
  • -ftree-dce โ€” dead code elimination (DCE) on trees ๐Ÿ’€ On by default at -O1 and higher.
  • -ftree-builtin-call-dce โ€” conditional DCE for calls to built-in functions that may set "errno" but are otherwise free of side effects ๐ŸŒธ On by default at -O2 and higher, as long as -Os isn't also specified.
  • -ffinite-loops โ€” assumes a loop with an exit will eventually take that exit instead of spinning forever ๐ŸŒˆ This lets the compiler remove loops that have no side effects, since it isn't worrying about endless looping. It's on by default at -O2 for C++ with -std=c++11 or higher.
  • -ftree-dominator-opts โ€” a bunch of simple scalar cleanups (constant/copy propagation, redundancy elimination, range propagation and expression simplification) based on a dominator tree traversal โœจ It also does jump threading, which reduces jumps to jumps. On by default at -O1 and higher.
  • -ftree-dse โ€” dead store elimination (DSE) on trees ๐Ÿงน A dead store is a store into a memory location that gets overwritten later by another store with no loads in between. In that case the earlier store can be deleted. On by default at -O1 and higher.
  • -ftree-ch โ€” loop header copying on trees ๐Ÿ‘‘ This helps code motion optimizations work better and saves one jump. On by default at -O1 and higher, but not for -Os, since it usually bumps up code size.
  • -ftree-loop-optimize โ€” loop optimizations on trees ๐Ÿ” On by default at -O1 and higher.
  • -ftree-loop-linear, -floop-strip-mine, -floop-block โ€” loop nest optimizations, same as -floop-nest-optimize ๐Ÿงš To use this transformation, GCC has to be configured with --with-isl so the Graphite loop transformation infrastructure is enabled.
  • -fgraphite-identity โ€” enables the identity transformation for graphite ๐Ÿชž For every SCoP, GCC builds the polyhedral representation and transforms it back to gimple. With -fgraphite-identity you can check the costs or benefits of the GIMPLE -> GRAPHITE -> GIMPLE round trip. The isl code generator also does some minimal optimizations, 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 built on the Pluto optimization algorithms, and it works out a loop structure tuned for data-locality and parallelism.
โš ๏ธ This option is experimental, so handle it with care bestie ๐Ÿ™ˆ
  • -floop-parallelize-all โ€” uses Graphite data dependence analysis to find loops that can be parallelized ๐Ÿงต It parallelizes every loop that can be analyzed as having no loop carried dependences, without checking whether parallelizing is actually profitable.
  • -ftree-coalesce-vars โ€” when the program is transformed out of SSA representation, this tries to reduce copying by coalescing versions of different user-defined variables, not just compiler temporaries ๐Ÿซถ In the negated form, this flag prevents SSA coalescing of user variables. It's on by default if optimization is enabled, and it does very little otherwise.
โš ๏ธ This may severely limit your ability to debug an optimized program compiled with -fno-var-tracking-assignments ๐Ÿ˜ญ
  • -ftree-loop-if-convert โ€” transforms conditional jumps in the innermost loops into branch-less equivalents ๐ŸŒ™ The goal is to strip control flow out of those loops so the vectorization pass can handle them better. It's on by default if vectorization is enabled.
  • -ftree-loop-distribution โ€” loop distribution ๐Ÿ“ This can improve cache performance on big loop bodies and lets further loop optimizations, like parallelization or vectorization, take place. For example, this loop:
DO I = 1, N
  A(I) = B(I) + C
  D(I) = E(I) * F
ENDDO

gets split into this:

DO I = 1, N
   A(I) = B(I) + C
ENDDO
DO I = 1, N
   D(I) = E(I) * F
ENDDO

On by default at -O3. It's also enabled by -fprofile-use and -fauto-profile ๐Ÿ’–

  • -ftree-loop-distribute-patterns โ€” distributes loops that can be code generated as calls to a library ๐Ÿงธ On by default at -O2 and higher, and also enabled by -fprofile-use and -fauto-profile.

This pass distributes the initialization loops and generates a call to memset zero. For example, this loop:

DO I = 1, N
  A(I) = 0
  B(I) = A(I) + I
ENDDO

becomes this:

DO I = 1, N
   A(I) = 0
ENDDO
DO I = 1, N
   B(I) = A(I) + I
ENDDO

and the initialization loop turns into a call to memset zero ๐ŸŒˆ

  • -floop-interchange โ€” loop interchange, done outside of graphite ๐Ÿ”„ This can improve cache performance on loop nests and lets further loop optimizations, like vectorization, happen. For example, 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 transformed 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];

On by default at -O3. It's also enabled by -fprofile-use and -fauto-profile ๐Ÿซง

  • -floop-unroll-and-jam โ€” applies unroll and jam transformations to feasible loops ๐Ÿซง In a loop nest, this unrolls the outer loop by some factor and fuses the resulting multiple inner loops. On by default at -O3, and also enabled by -fprofile-use and -fauto-profile.
  • -ftree-loop-im โ€” loop invariant motion on trees ๐Ÿงฒ This only moves the invariants that are hard to handle at RTL level, like function calls or operations that expand into nontrivial sequences of insns. With -funswitch-loops, it also moves operands of invariant conditions out of the loop, so loop unswitching can rely on just trivial invariantness analysis. The pass also includes store motion.
  • -ftree-loop-ivcanon โ€” creates a canonical counter for the number of iterations in loops where figuring that count out needs complicated analysis ๐Ÿ”ข Later optimizations can then find the number easily. It's especially useful together with unrolling.
  • -ftree-scev-cprop โ€” final value replacement โœจ If a variable changes in a loop in a way that its value on exit can be worked out from just its initial value and the number of loop iterations, uses of the final value get replaced with that computation, as long as it's cheap enough. This reduces data dependencies and may allow more simplifications. On by default at -O1 and higher.
  • -fivopts โ€” induction variable optimizations on trees ๐Ÿ’œ That's strength reduction, induction variable merging and induction variable elimination. On by default at -O1 and higher.
  • -ftree-parallelize-loops, -ftree-parallelize-loops=n โ€” parallelizes loops by splitting their iteration space to run in multiple threads ๐Ÿงต This only works for loops whose iterations are independent and can be arbitrarily reordered. It's only worth it on multiprocessor machines, for CPU-intensive loops rather than ones held back by something like memory bandwidth. This option implies -pthread, so it's only supported on targets that support -pthread.
    • With a positive value n, the number of threads is fixed at compile time and can't be changed after compilation. The compiler generates #pragma omp parallel num_threads(n).
    • Without =n (just -ftree-parallelize-loops), the number of threads is decided at program execution time through the OMP_NUM_THREADS environment variable. If OMP_NUM_THREADS isn't set, the OpenMP runtime auto-detects the number of available processors and uses that value. This lets you make binaries that adapt to different hardware without recompiling ๐ŸŒธ
  • -ftree-pta โ€” function-local points-to analysis on trees ๐ŸŽฏ On by default at -O1 and higher, except for -Og.
  • -ftree-sra โ€” scalar replacement of aggregates ๐Ÿฌ This swaps structure references for scalars so structs don't get committed to memory too early. On 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 merges them into fewer, wider stores to cut down on instructions. On by default at -O2 and higher, as well as -Os ๐Ÿซถ
  • -ftree-ter โ€” okay bestie, this one does temporary expression replacement during the SSA->normal phase ๐Ÿ’… Single use/single def temporaries get swapped out at their use location for their defining expression โœจ It leaves you with non-GIMPLE code, but the expanders get way more complex trees to work with, which means better RTL generation ๐Ÿงš It's on by default at -O1 and higher ๐ŸŒธ
  • -ftree-slsr โ€” this is giving straight-line strength reduction on trees ๐Ÿ”ฅ It spots related expressions involving multiplications and swaps them for cheaper calculations when it can ๐Ÿ’– Enabled by default at -O1 and higher โœจ
  • -ftree-vectorize โ€” vectorization on trees ๐Ÿฆ‹ This flag also turns on -ftree-loop-vectorize and -ftree-slp-vectorize, as long as you haven't explicitly specified them ๐Ÿ’œ
  • -ftree-loop-vectorize โ€” loop vectorization on trees ๐ŸŒˆ It's on by default at -O2, and it also gets switched on by -ftree-vectorize, -fprofile-use, and -fauto-profile ๐Ÿซถ
  • -ftree-slp-vectorize โ€” basic block vectorization on trees ๐ŸŽ€ On by default at -O2, and also enabled by -ftree-vectorize, -fprofile-use, and -fauto-profile ๐Ÿ’…
  • -ftrivial-auto-var-init=choice โ€” main character energy alert ๐Ÿšจ This one initializes automatic variables and temporary objects with either a pattern or zeroes, which boosts security and predictability by stopping uninitialized memory from being leaked or used ๐Ÿ›ก๏ธ GCC still treats an automatic variable with no explicit initializer as uninitialized, so -Wuninitialized and -Wanalyzer-use-of-uninitialized-value will still warn about those, and the compiler will still optimize as if the variable were uninitialized ๐Ÿ‘€ With this flag on, GCC also zeroes any padding in automatic variables or temporaries that have structure or union types ๐Ÿงธ Heads up: the current implementation can't initialize automatic variables whose initialization gets bypassed through a switch or goto statement ๐Ÿ˜ญ Use -Wtrivial-auto-var-init to report all such cases ๐Ÿ”
    • The three values of choice are:
      • uninitialized โ€” doesn't initialize any automatic variables ๐Ÿ™…
      • pattern โ€” initializes automatic variables with values that will likely turn logic bugs into crashes down the line ๐Ÿ’ฅ They're easy to recognize in a crash dump ๐Ÿ”Ž and they aren't values programmers can rely on for useful program semantics ๐Ÿšซ The current value is a byte-repeatable pattern with byte 0xFE, and the values used for pattern initialization might be changed in the future ๐Ÿ”ฎ
      • zero โ€” initializes automatic variables with zeroes ๐Ÿ“
    • The default is uninitialized except for C++26. In C++26, if -ftrivial-auto-var-init= isn't specified at all, automatic variables and temporary objects are zero initialized, but zero initialization of padding bits does not happen ๐Ÿงธ
    • Quick note: the initializer values, whether zero or pattern, refer to the data representation (in memory or machine registers), not to their interpretation as numerical values ๐Ÿค“ This distinction can matter 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 the choice, whether or not those representations stand for values in that range. Even if they do, the interpretation of the value held by the variable 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. That means zero initialization won't convert to the representation for "false", even if it would for a static variable of the same type ๐Ÿ’€ So the initializer pattern doesn't generally depend on the type of the initialized variable. One notable exception is that (non-hardened) boolean variables that fit in registers are initialized with "false" (zero), even when pattern is requested ๐Ÿ™ˆ
    • You can control this for a specific variable with the uninitialized variable attribute (the standard attribute) or C++26's [[indeterminate]] ๐ŸŽฏ
  • -fvect-cost-model=model โ€” tweaks the cost model used for vectorization ๐Ÿ’œ The model argument should be one of unlimited, dynamic, cheap, or very-cheap:
    • unlimited โ€” the vectorized code path is assumed to be profitable ๐Ÿ’ช
    • dynamic โ€” a runtime check guards the vectorized code path, so it only gets enabled for iteration counts that will likely run faster than the original scalar loop ๐Ÿƒโ€โ™€๏ธ
    • cheap โ€” disables vectorization of loops where doing so would be cost prohibitive, for example because of required runtime checks for data dependence or alignment. Otherwise it's equal to 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 equal to cheap ๐Ÿงƒ
    • The default cost model depends on other optimization flags and is either dynamic or cheap ๐ŸŒธ
  • -fsimd-cost-model=model โ€” same vibe, but for loops marked with the OpenMP simd directive ๐ŸŽ€ The model argument should be one of unlimited, dynamic, or cheap. All values of model have the same meaning as described in -fvect-cost-model, and by default the cost model defined with -fvect-cost-model is used ๐Ÿซถ
  • -ftree-vrp โ€” Value Range Propagation on trees ๐Ÿ“ˆ It's similar to the constant propagation pass, but instead of values, ranges of values get propagated ๐Ÿ’… That lets the optimizers remove unnecessary range checks, like array bound checks and null pointer checks ๐Ÿ—‘๏ธ Enabled by default at -O2 and higher ๐Ÿ”ฅ Null pointer check elimination only happens if -fdelete-null-pointer-checks is enabled ๐Ÿ™ˆ
  • -fsplit-paths โ€” splits paths leading to loop backedges โœ‚๏ธ This can improve dead code elimination and common subexpression elimination ๐Ÿงน Enabled by default at -O3 and above ๐Ÿ’–
  • -fsplit-ivs-in-unroller โ€” lets values of induction variables in later iterations of an unrolled loop be expressed using the value from the first iteration ๐Ÿง  That breaks long dependency chains, which improves the efficiency of the scheduling passes โšก
    • A combination of -fweb and CSE is often enough to get the same effect ๐Ÿค But that's not reliable when the loop body is more complicated than a single basic block ๐Ÿ˜ฌ It also doesn't work at all on some architectures, due to restrictions in the CSE pass ๐Ÿšซ
    • This optimization is enabled by default โœจ
  • -fvariable-expansion-in-unroller โ€” when unrolling a loop, the compiler creates multiple copies of some local variables, which can result in superior code ๐Ÿ’Ž Enabled by default for PowerPC targets, but disabled by default otherwise ๐Ÿฆš
  • -fpartial-inlining โ€” inlines parts of functions ๐Ÿงฉ This option only has an effect when inlining itself is turned on by -finline-functions or -finline-small-functions ๐Ÿซถ Enabled at levels -O2, -O3, -Os ๐ŸŒˆ
  • -fpredictive-commoning โ€” performs predictive commoning, which means reusing computations (especially memory loads and stores) performed in previous loop iterations ๐Ÿ” Enabled at level -O3, and also by -fprofile-use and -fauto-profile ๐Ÿ’œ
  • -fprefetch-loop-arrays โ€” if the target machine supports it, generates instructions to prefetch memory, which can improve the performance of loops that access large arrays ๐ŸŽ๏ธ This may generate better or worse code, since results are highly dependent on the structure of loops in the source code ๐Ÿคท Disabled at level -Os ๐Ÿ˜ด
  • -fno-printf-return-value โ€” don't substitute constants for the known return values of formatted output functions like "sprintf", "snprintf", "vsprintf", and "vsnprintf" (but not "printf" or "fprintf") ๐Ÿ™…โ€โ™€๏ธ This lets GCC optimize or even eliminate branches based on the known return value of these functions, when they're called with arguments that are constant, or whose values are known to be in a range that makes the exact return value possible to determine ๐Ÿงฎ 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 yields the best results with -O2 and above ๐Ÿ† It works in tandem with -Wformat-overflow and -Wformat-truncation ๐Ÿค The -fprintf-return-value option is enabled by default โœจ

  • -fno-peephole, -fno-peephole2 โ€” disables any machine-specific peephole optimizations ๐Ÿงผ The difference between -fno-peephole and -fno-peephole2 is in 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 โ€” don't guess branch probabilities using 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, the heuristics are used to guess the rest of the control flow graph, taking the __builtin_expect info into account ๐Ÿง  The interactions between the heuristics and __builtin_expect can get complex, so in some cases it's useful to disable the heuristics, making the effects of __builtin_expect easier to understand ๐Ÿซ 
    • It's also possible to 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 โ€” reorders basic blocks in the compiled function to reduce the number of taken branches and improve code locality ๐Ÿฆ‹

Okay so this one is enabled at -O1, -O2, -O3, and -Os ๐Ÿ’…โœจ

-freorder-blocks-algorithm=algorithm ๐Ÿฆ‹

Tells the compiler which algorithm to use when it shuffles basic blocks around. You can pick simple, which doesn't bloat your code size (though sometimes alignment side effects can nudge it a bit ๐Ÿ™ˆ), or stc, the "software trace cache" algorithm. stc groups all the frequently run code together so fewer branches get executed, but it does it by making extra copies of code, so it's giving bigger binary energy ๐Ÿ“

The default is simple at -O1 and -Os, and stc at -O2 and -O3.

-freorder-blocks-and-partition ๐ŸŒธ

On top of reordering basic blocks inside each compiled function (to cut down on taken branches), this one splits hot and cold blocks into separate sections in the assembly and .o files. The result is better paging and cache locality, which is a whole vibe ๐Ÿงš

This optimization gets turned off automatically in a few situations: when exception handling or unwind tables are present (on targets using setjump/longjump or a target-specific scheme), for linkonce sections, for functions with a user-defined section attribute, and on any architecture that doesn't support named sections. If you're using -fsplit-stack, it's not enabled by default (to avoid linker errors), but you can turn it on explicitly if your linker actually works ๐Ÿ’€

Enabled for x86 at -O2, -O3, and -Os.

-freorder-functions ๐Ÿ‘‘

Reorders functions in the object file to improve code locality. Unlike -fipa-reorder-for-locality, this one prioritizes grouping every function in a category (hot, normal, cold, or never-executed) together. It does this with special subsections: .text.hot for the most frequently executed functions and .text.unlikely for the ones that are unlikely to run. The linker does the actual reordering, so your object file format needs to support named sections, and the linker has to place them sensibly โœจ

This option doesn't do anything unless you either provide profile feedback (see -fprofile-arcs for details) or manually annotate functions with hot or cold attributes. No profile, no annotations, no glow-up ๐Ÿฅ€

Enabled at levels -O2, -O3, and -Os.

-fstrict-aliasing ๐Ÿ’–

This lets the compiler assume the strictest aliasing rules that apply to the language being compiled. For C (and C++), that turns on optimizations based on the type of expressions. Specifically, accessing an object of one type through an expression of a different type is not allowed, unless the types are compatible, differ only in signedness or qualifiers, or the expression has a character type. Accessing scalar objects through a corresponding vector type is also allowed.

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 you most recently wrote to (called "type-punning") is super common. Even with -fstrict-aliasing, type-punning is allowed in C, as long as the memory is accessed through the union type. In ISO C++, type-punning through a union type is undefined behavior, but GCC supports it as an extension, 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;
        }

Same energy goes for accessing something by taking its address, casting the resulting pointer, and dereferencing it. That 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;
        }
โš ๏ธ Heads up bestie: all of the above is undefined behavior territory in some cases, so be careful with your pointers ๐Ÿ˜ญ

The -fstrict-aliasing option is enabled at levels -O2, -O3, and -Os.

-fipa-strict-aliasing ๐Ÿ”ฅ

Controls whether the rules of -fstrict-aliasing get applied across function boundaries. Fun fact: 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, but it only does anything in combination with -fstrict-aliasing.

-falign-functions ๐ŸŽ€

Aligns the start of functions to the next power-of-two greater than or equal to n, skipping up to m-1 bytes. This makes sure that 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 is ignored for functions considered cold. If you need alignment for every single function, use -fmin-function-alignment instead ๐Ÿซถ

The forms you can use are:

-falign-functions
-falign-functions=n
-falign-functions=n:m
-falign-functions=n:m:n2
-falign-functions=n:m:n2:m2

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 that can be done by skipping 23 bytes or less. -falign-functions=32:7 aligns to the next 32-byte boundary only if that can be done by skipping 6 bytes or less.

The second pair, n2:m2, lets you set a secondary alignment. -falign-functions=64:7:32:3 aligns to the next 64-byte boundary if that can be done by skipping 6 bytes or less, and otherwise aligns to the next 32-byte boundary if that 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, the value 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 value is 65536.

Enabled at levels -O2 and -O3.

-flimit-function-alignment ๐Ÿฆ‹

When this is enabled, the compiler tries not to overalign functions for no reason. It asks the assembler to align by the amount specified by -falign-functions, but it won't skip more bytes than the size of the function itself ๐Ÿ™…โ€โ™€๏ธ

-falign-labels ๐ŸŒˆ

Aligns all branch targets to a power-of-two boundary.

The parameters for this option work the same way as they do for -falign-functions. -fno-align-labels and -falign-labels=1 are equivalent and both mean labels are not aligned.

The forms you can use are:

-falign-labels
-falign-labels=n
-falign-labels=n:m
-falign-labels=n:m:n2
-falign-labels=n:m:n2:m2

If -falign-loops or -falign-jumps apply and are greater than this value, their values are 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 value is 65536.

Enabled at levels -O2 and -O3.

-falign-loops ๐Ÿ“

Aligns loops to a power-of-two boundary. If the loops run many times, this makes up for the cost of 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, its value is used instead.

The parameters work like -falign-functions. -fno-align-loops and -falign-loops=1 are equivalent and both mean loops are not aligned. The maximum allowed n value is 65536.

The forms you can use are:

-falign-loops
-falign-loops=n
-falign-loops=n:m
-falign-loops=n:m:n2
-falign-loops=n:m:n2:m2

If n isn't specified or is zero, the compiler uses a machine-dependent default.

Enabled at levels -O2 and -O3.

-falign-jumps ๐Ÿงš

Aligns branch targets to a power-of-two boundary, but only for branch targets that can only be reached by jumping. In that case no dummy operations need to run. It's a code performance optimization, and alignment is ignored for jumps considered cold.

If -falign-labels is greater than this value, its value is used instead.

The parameters work like -falign-functions. -fno-align-jumps and -falign-jumps=1 are equivalent and both mean loops are not aligned (yes, the original says loops, it's a lil mix-up ๐Ÿ™ˆ). The maximum allowed n value is 65536.

The forms you can use are:

-falign-jumps
-falign-jumps=n
-falign-jumps=n:m
-falign-jumps=n:m:n2
-falign-jumps=n:m:n2:m2

If n isn't specified or is zero, the compiler uses a machine-dependent default.

Enabled at levels -O2 and -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 gets applied to all functions, even the ones considered cold. It's also not affected by -flimit-function-alignment ๐Ÿ‘‘

-fno-allocation-dce โœจ

Stops the compiler from removing unused C++ allocations (the ones using operator "new" and operator "delete") during dead code elimination. So those allocations stay put ๐Ÿซถ

See also -fmalloc-dce.

-fallow-store-data-races โญ

Lets the compiler do 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 if you know global data won't be accessed by multiple threads ๐Ÿงต

Examples of optimizations this enables include hoisting, or if-conversions that might cause a value already in memory to be rewritten with that same value. That rewrite is safe in a single-threaded context but can be unsafe in a multi-threaded one. Also, on some processors, if-conversions may be required before vectorization can happen.

โš ๏ธ Only use this if global data is single-threaded, otherwise you're playing with fire ๐Ÿ”ฅ

Enabled at level -Ofast.

-funit-at-a-time ๐ŸŒธ

This one is only here for compatibility reasons. -funit-at-a-time has no effect, but -fno-unit-at-a-time implies -fno-toplevel-reorder and -fno-section-anchors ๐Ÿ™ƒ

Enabled by default.

-fno-toplevel-reorder ๐Ÿ’…

Stops the compiler from reordering top-level functions, variables, and asm statements. It outputs them in the same order they appear in the input file. When this is used, unreferenced static variables are not removed. This option exists to support existing code that relies on a specific ordering. For new code, it's better to use attributes whenever you can ๐ŸŽ€

-ftoplevel-reorder is the default at -O1 and higher ๐Ÿ’…, and at -O0 too if you explicitly ask for -fsection-anchors ๐Ÿฆ‹. Also, -fno-toplevel-reorder implies -fno-section-anchors ๐Ÿ™ˆ.

  • -funreachable-traps โ€” okay so this one is a whole vibe ๐Ÿ’€. With it on, the compiler turns calls to __builtin_unreachable into traps instead of using them for optimization. It also hits any of those calls the compiler generates on its own ๐Ÿงš.

This is the same as -fsanitize=unreachable -fsanitize-trap=unreachable, but it doesn't change the values of those options ๐Ÿซถ. If -fsanitize=unreachable is enabled, that one takes priority over this one ๐Ÿ‘‘. It's enabled by default at -O0 and -Og โœจ.

  • -fweb โ€” this builds webs for register allocation and gives each web its own pseudo register ๐Ÿง . That lets the register allocation pass work on pseudos directly and also boosts CSE, the loop optimizer, and the trivial dead code remover ๐Ÿ”ฅ. The catch? It can make debugging impossible, since variables no longer stay in a "home register" ๐Ÿ˜ญ. It's enabled by default with -funroll-loops.
  • -fwhole-program โ€” you're basically telling the compiler "this compilation unit is the whole program" ๐Ÿ‘‘. All public functions and variables become static, except main and anything merged by attribute externally_visible, so the interprocedural optimizers can go hard ๐Ÿ’œ.

With -flto, this one has limited use ๐Ÿฅบ. Usually the linker plugin already knows the precise list of symbols used or exported from the binary, from the resolution info the linker passes to the link-time optimizer. It's still useful if no linker plugin is used, or during the incremental link step when final code is produced (with -flto -flinker-output=nolto-rel).

-flto[=n]

Okay, the link-time optimizer is literally so iconic ๐Ÿฆ‹. This option runs the standard link-time optimizer. When you invoke it with source code, it generates GIMPLE (one of GCC's internal representations) and writes it to special ELF sections in the object file ๐ŸŒธ. When the object files get linked together, all the function bodies are read back from those ELF sections and instantiated as if they had been part of the same translation unit โœจ.

To use it, -flto and optimization options should be specified at compile time and during the final link ๐ŸŽ€. It's recommended that you compile all the files in the same link with the same options, and specify those options at link time too. 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 save a bytecode representation of GIMPLE into special ELF sections inside foo.o and bar.o ๐Ÿ’พ. The final invocation reads that 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 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. So, for example, the inliner can inline functions in bar.o into functions in foo.o and vice-versa ๐Ÿ˜ญ.

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 into a single GIMPLE representation, and optimizes them as usual to produce myprog ๐ŸŒˆ.

The important thing to keep in mind is that you need to use the GCC driver to perform 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 ๐Ÿ™ˆ.

To make whole-program optimization effective, you have to make certain 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 that information about used and externally visible symbols to the compiler. When the linker plugin isn't available, use -fwhole-program so the compiler can make those assumptions, which leads to more aggressive optimization decisions ๐Ÿ’….

When a file is compiled with -flto without -fuse-linker-plugin, the generated object file is bigger than a regular one ๐Ÿ“ฆ. That's because it contains GIMPLE bytecodes and the usual final code (see -ffat-lto-objects). So object files with LTO information can still be linked as normal object files. If -fno-lto is passed to the linker, though, 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 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 to link without further processing โœจ.

Generally, options specified at link time override those specified at compile time ๐Ÿ’–. In some cases, though, GCC attempts 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 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 GCC preserves when generating bytecodes, since they need to be used during the final link ๐ŸŽ€. Currently, 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 options -fPIC, -fpic, -fpie and -fPIE get 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 ๐Ÿšซ. Trying to override them at link time with a conflicting value gets 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, get 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 get passed through to the link stage too, 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 such 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 instead ๐Ÿ’œ.

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 enables 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 across translation units is discouraged ๐Ÿ˜ฌ.

If LTO encounters objects with C linkage declared with incompatible types in separate translation units being linked together, a non-fatal diagnostic may be issued ๐Ÿšจ. That's undefined behavior according to ISO C99 6.2.7, and the behavior is still undefined at run time ๐Ÿ’€. Similar diagnostics may be raised for other languages too.

Another feature of LTO is that you can apply interprocedural optimizations to 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 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, as long as 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. 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, so they become part of the aggregated GIMPLE image to be optimized ๐ŸŒŸ.

Okay so if you're NOT using a linker with plugin support and/or you don't turn the linker plugin on ๐Ÿ’…, the objects inside libfoo.a still get extracted and linked like normal, but they just sit out the LTO optimization process ๐Ÿ˜ญ. If you want a static library that works for BOTH LTO optimization and regular linking, compile its object files with -flto -ffat-lto-objects ๐ŸŽ€.

Link-time optimizations don't need the whole program to be present to work ๐Ÿฆ‹. If the program doesn't need any symbols exported, you can combine -flto and -fwhole-program so the interprocedural optimizers can make more aggressive assumptions, which may open up better optimization opportunities โœจ. You don't need -fwhole-program when the linker plugin is active (see -fuse-linker-plugin).

The current LTO implementation makes no attempt to generate bytecode that's portable between different types of hosts ๐Ÿ™ˆ. The bytecode files are versioned with a strict version check, so bytecode from one version of GCC won't work with an older or newer GCC. Not it ๐Ÿ’€.

Link-time optimization doesn't work well with generating debugging information on systems other than the ones using a combination of ELF and DWARF ๐Ÿ˜ญ.

If you specify the optional n, the link-time optimization and code generation run in parallel using n jobs by utilizing an installed make program ๐Ÿ”ฅ. The environment variable MAKE can be used to override which program gets used.

You can also use -flto=jobserver to tap into GNU make's job server mode and let it decide how many parallel jobs to run ๐Ÿ‘‘. This is super useful when the Makefile calling GCC is already running in parallel. You must prepend a + to the command recipe in the parent Makefile for this to work. This option likely only works if MAKE is GNU make. Even without the option value, GCC tries to automatically detect a running GNU make's job server ๐Ÿซถ.

Use -flto=auto to use GNU make's job server if it's available, or otherwise fall back to autodetecting the number of CPU threads in your system โญ.


-flto-partition=alg ๐Ÿงฉ

Okay this one picks the partitioning algorithm the link-time optimizer uses ๐Ÿ’–. The value is one of:

  • 1to1, which mirrors the partitioning of the original source files
  • balanced, which splits things into equally sized chunks (whenever possible)
  • max, which creates a new partition for every symbol where possible
  • cache, which balances chunk sizes while keeping related symbols together for better caching in incremental LTO

Specifying none as the algorithm disables partitioning and streaming completely ๐Ÿ™…. The default is balanced. 1to1 can be used as a workaround for various code ordering issues, but max partitioning is intended for internal testing only ๐Ÿงช. The value one specifies that exactly one partition should be used, while none bypasses partitioning and runs the link-time optimization step directly from the WPA phase ๐Ÿฆ‹.

-flto-incremental=path โšก

Enables incremental LTO, with its cache living in the given existing directory. It can seriously shorten your edit-compile cycles with LTO ๐ŸŒธ.

When you use LTO (-flto), the output of translation units inside LTO gets cached ๐Ÿ’œ. Cached translation units are likely to be hit again when you recompile after small code changes, which means faster recompile times โœจ.

Multiple GCC instances can share the same cache in parallel ๐Ÿซถ.

-flto-incremental-cache-size=n ๐Ÿ—‚๏ธ

Specifies the number of cache entries in incremental LTO after which old entries get pruned. This is a soft limit, so there may temporarily be more entries than that ๐Ÿคทโ€โ™€๏ธ.

-flto-compression-level=n ๐Ÿ—œ๏ธ

This sets the compression level for the intermediate language written to LTO object files ๐Ÿ’…. It only matters together with LTO mode (-flto). GCC currently supports two LTO compression algorithms. For zstd, valid values are 0 (no compression) to 19 (maximum compression), while zlib supports values from 0 to 9. Values outside this range get clamped to the minimum or maximum of the supported values ๐Ÿ“. If you don't give the option, a default balanced compression setting is used ๐ŸŽ€.

-flto-toplevel-asm-heuristics ๐Ÿ”

This enables heuristics that find symbols used in top-level basic asm ๐Ÿ•ต๏ธโ€โ™€๏ธ. It restricts the link-time optimizations that could rename or delete those symbols, which would otherwise cause missing symbol errors from the linker ๐Ÿšซ.

This flag is meant for projects that haven't switched to top-level extended asm, which specifies the usage directly without any false positives ๐ŸŒˆ.

The heuristics are simple and don't parse the assembly. They scan through top-level assembly for all possible identifiers. If an identifier matches one of the declared symbols, that symbol gets marked to restrict link-time optimizations. Static symbols disable more optimizations. Identifiers followed by ':' also disable more optimizations, because they might be a locally defined symbol in the assembly, even when the declaration is marked extern ๐Ÿ‘€.

-fuse-linker-plugin ๐Ÿ”Œ

Enables the use of a linker plugin during link-time optimization ๐Ÿ’–. This relies on plugin support in the linker, which is available in gold or in GNU ld 2.21 or newer.

This option lets object files with GIMPLE bytecode get extracted out of library archives. That improves optimization quality by exposing more code to the link-time optimizer ๐Ÿ”ฅ. It also tells the optimizer which symbols can be accessed externally (by a non-LTO object or during dynamic linking). The resulting code quality improvements on binaries (and on shared libraries that use hidden visibility) are similar to -fwhole-program ๐Ÿ‘‘. See -flto for a description of the effect of this flag and how to use it.

This option is enabled by default when LTO support in GCC is enabled and GCC was configured for use with a linker that supports plugins (GNU ld 2.21 or newer, or gold) ๐Ÿซถ.

-ffat-lto-objects ๐Ÿฑ

Fat LTO objects are object files that contain both the intermediate language and the object code ๐Ÿฅฐ. That makes them usable for both LTO linking and normal linking. This option only works when you're compiling with -flto, and it's ignored at link time.

-fno-fat-lto-objects improves compilation time over plain LTO, but it requires the complete toolchain to be aware of LTO ๐Ÿ˜ค. It needs a linker with linker plugin support for basic functionality. On top of that, nm, ar and ranlib need to support linker plugins to allow 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. With non-fat LTO, makefiles need to be modified to use them ๐Ÿ“.

Modern binutils come with 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, this identifies arithmetic instructions that compute processor flags similar to a comparison operation based on that arithmetic ๐Ÿงฎ. If possible, it eliminates the explicit comparison operation โœ‚๏ธ.

This pass only applies to certain targets that can't explicitly represent the comparison operation before register allocation is complete ๐ŸŽฏ.

Enabled at levels -O1, -O2, -O3, -Os โญ.

-ffold-mem-offsets / -fno-fold-mem-offsets ๐Ÿง 

Tries to eliminate 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 runs a copy-propagation pass to try to reduce scheduling dependencies and occasionally eliminate the copy ๐ŸŒท.

Enabled at levels -O1, -O2, -O3, -Os ๐Ÿ’….

-fprofile-correction ๐Ÿฉน

Profiles collected using an instrumented binary for multi-threaded programs can be inconsistent because of missed counter updates ๐Ÿงต. When you specify this option, GCC uses heuristics to correct or smooth out those inconsistencies. By default, GCC emits an error message when it detects an inconsistent profile ๐Ÿšจ.

This option is enabled by -fauto-profile ๐Ÿซถ.

-fprofile-partial-training ๐ŸŽ“

With -fprofile-use, all portions of programs not executed during training runs get optimized aggressively for size rather than speed ๐Ÿœ. Sometimes it isn't practical to train every possible hot path in the program. For example, it may contain functions specific to certain hardware, and training may not cover every hardware configuration the program will later run on ๐Ÿ–ฅ๏ธ.

With -fprofile-partial-training, profile feedback is ignored for all functions not executed during the training runs. Those functions get optimized as if they were compiled without profile feedback. This leads to better performance when the training isn't representative, at the cost of significantly bigger code ๐Ÿ“ฆ.

-fprofile-use / -fprofile-use=path ๐Ÿš€

Enables profile feedback-directed optimizations, plus the following optimizations, many of which are generally only 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 may result in poorly optimized code ๐Ÿ’€. Also by default, GCC emits a warning message if the feedback profiles don't exist (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 โ€” okay bestie, this one switches on sampling-based feedback-directed optimizations ๐Ÿ”ฅ and a whole squad of other optimizations that are mostly worth it 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-correction
    • path is the name of the file that holds your AutoFDO profile info ๐Ÿงš. 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 the full tea, check out https://perfwiki.github.io/main/ ๐ŸŒธ

E.g.

        perf record -e br_inst_retired:near_taken -b -o perf.data \
            -- your_program

Then you use the create_gcov tool to turn the raw profile data into something GCC can actually read ๐Ÿซถ. You also have to hand this tool the unstripped binary for your program. See https://github.com/google/autofdo for more.

E.g.

        create_gcov --binary=your_program.unstripped --profile=perf.data \
            --gcov=profile.afdo
  • -fauto-profile-inlining โ€” when auto-profile is available, this inlines all the relevant functions that got inlined during the training run before the profile feedback gets read. It makes the context sensitivity of the profile way better โœจ. Enabled by default.

The following options control compiler behavior regarding floating-point arithmetic. These options trade speed for correctness ๐Ÿงฎ, so every single one of them must be specifically enabled. No sneaky auto-magic here, bestie ๐Ÿ™…โ€โ™€๏ธ.

  • -fexcess-precision=style โ€” okay so this one lets you control excess precision on machines where floating-point ops happen in a format with more precision or range than the IEEE standard and interchange floating-point types. The 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 extra precision is harmless ๐Ÿ’–, but some programs rely on the C or C++ standards' rules for handling IEEE values, so they can get messy.
    • By default, -fexcess-precision=fast is in effect. That means ops may run in a wider precision than the source types if it makes the code faster, and it's unpredictable when rounding to the source types actually happens ๐ŸŒ€.
    • When compiling C or C++, if you use -fexcess-precision=standard, the excess precision follows the rules in ISO C99 or C++. Casts and assignments both round values to their semantic types (whereas -ffloat-store only affects assignments).
    • This is enabled by default for C or C++ if a strict conformance option like -std=c99 or -std=c++17 is used.
    • -ffast-math enables -fexcess-precision=fast by default, regardless of whether a strict conformance option is used.
    • If you specify -fexcess-precision=16, constants and the results of expressions with types _Float16 and __bf16 are computed without excess precision.
    • -fexcess-precision=standard is not implemented for languages other than C or C++. On x86 it has no effect if -mfpmath=sse or -mfpmath=sse+387 is specified. With the former, IEEE semantics apply without excess precision. With the latter, rounding is unpredictable.
  • -ffloat-store โ€” do not store floating-point variables in registers, and block other options that might change whether a floating-point value is pulled from a register or from memory ๐Ÿ™…โ€โ™€๏ธ. This has mostly been subsumed by -fexcess-precision=standard, which is more general. If you do use -ffloat-store, you might need to tweak your program to explicitly store intermediate computations in temporary variables, since -ffloat-store only handles rounding to IEEE format on assignments and not on casts like -fexcess-precision=standard does.
  • -ffast-math โ€” 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 ๐ŸŽ๏ธ.
    • This option causes the preprocessor macro __FAST_MATH__ to be defined.
    • It is not turned on by any -O option besides -Ofast, since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that don't need the guarantees of those specifications.
  • -fno-math-errno โ€” do not set errno after calling math functions that run with a single instruction, like sqrt. A program that relies on IEEE exceptions for math error handling might want this flag for speed while still keeping IEEE arithmetic compatibility ๐Ÿฆ‹.
    • It is not turned on by any -O option besides -Ofast, since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that don't need the guarantees of those specifications.
    • 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. When used at link time, it may pull in libraries or startup files that change the default FPU control word or do other similar optimizations ๐Ÿ‘€.
    • It is not turned on by any -O option besides -Ofast, since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that don't need the guarantees of those specifications.
    • This 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 standards by possibly changing the computation result ๐Ÿ˜ฌ.
    • > NOTE: 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 behavior like (x + 2**52) - 2**52. It may also reorder floating-point comparisons, so it may not be used when ordered comparisons are required.
    • This option requires that both -fno-signed-zeros and -fno-trapping-math be in effect. Moreover, 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 โ€” allows the reciprocal of a value to 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. Heads up though: 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 ๐Ÿ™ˆ.
    • It is not turned on by any -O option besides -Ofast, since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that don't need the guarantees of those specifications.
    • The default is -fno-finite-math-only.
  • -fno-signed-zeros โ€” allows optimizations for floating-point arithmetic that ignore the sign of zero. IEEE arithmetic specifies how distinct +0.0 and -0.0 values behave, and that stops you from simplifying expressions like x+0.0 or 0.0*x (even with -ffinite-math-only). This option basically says the sign of a zero result isn't significant ๐ŸŒˆ.
    • The default is -fsigned-zeros.
  • -fno-trapping-math โ€” compiles code assuming floating-point operations can't generate user-visible traps. Those traps include division by zero, overflow, underflow, inexact result and invalid operation ๐Ÿ’€. This option requires that -fno-signaling-nans be in effect. Turning it on may allow faster code if you rely on "non-stop" IEEE arithmetic, for example.
    • It is not turned on by any -O option besides -Ofast, since it can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions.
    • The default is -ftrapping-math.
    • Future versions of GCC may give 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 behavior (round-to-nearest). You should specify this for programs that change the FP rounding mode dynamically, or that might run with a non-default rounding mode ๐ŸŒธ. It also disables constant folding of floating-point expressions at compile time (which can be affected by rounding mode), and arithmetic transformations that are unsafe when sign-dependent rounding modes are involved.
    • 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 give 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 โ€” ๐Ÿ’… Tells GCC to compile your code assuming IEEE signaling NaNs can throw user-visible traps during floating-point operations. Turning it on disables optimizations that might change how many exceptions show up with signaling NaNs, and it implies -ftrapping-math. โœจ
    • ๐ŸŽ€ It defines the preprocessor macro __SUPPORT_SNAN__.
    • ๐ŸŒธ The default is -fno-signaling-nans.
๐Ÿšจ bestie, this one is experimental. It does not currently guarantee that GCC disables all optimizations that affect signaling NaN behavior. ๐Ÿ’€
  • -fsingle-precision-constant โ€” ๐Ÿฆ‹ Floating-point constants get treated as single precision, instead of being quietly upgraded to double-precision constants.
  • -fcx-limited-range โ€” ๐ŸŒˆ When this is on, GCC says a range reduction step isn't needed for complex division. It also skips checking whether a complex multiplication or division result is "NaN + I*NaN" and trying to rescue it. โœจ -ffast-math turns it on.
    • ๐Ÿ‘‘ It controls the default setting of the ISO C99 "CX_LIMITED_RANGE" pragma, but it applies to all languages, not just C.
  • -fcx-fortran-rules โ€” ๐Ÿงš Complex multiplication and division follow Fortran rules. Range reduction happens as part of complex division, but there's no checking for "NaN + I*NaN" results and no rescue attempt in that case. ๐Ÿ’œ
  • -fcx-method=method โ€” ๐Ÿ“ Complex multiplication and division follow the method you name. The method value should be one of limited-range, fortran or stdc.
    • ๐ŸŒธ By default, GCC honors language-specific constraints, so it uses fortran for Fortran and stdc otherwise.

The following options control optimizations that may improve performance, but none of them are enabled by any -O options. โœจ This section also includes experimental options that may produce broken code, so ๐Ÿ‘€ be careful with these.

  • -fbranch-probabilities โ€” ๐Ÿ”ฅ After you run a program compiled with -fprofile-arcs, you can compile it a second time with -fbranch-probabilities to improve optimizations based on how many times each branch was taken. ๐Ÿ“Š
    • ๐Ÿ’– When a program compiled with -fprofile-arcs 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 details about the file naming in -fprofile-arcs.
    • ๐Ÿง  With -fbranch-probabilities, GCC puts a REG_BR_PROB note on each JUMP_INSN and CALL_INSN. Right now, those notes are only used in one place: in reorg.cc. Instead of guessing which path a branch is most likely to take, the REG_BR_PROB values are used to figure out exactly which path is taken more often. ๐ŸŽฏ
    • ๐ŸŒˆ Enabled by -fprofile-use and -fauto-profile.
  • -fprofile-values โ€” ๐Ÿซถ If you combine it with -fprofile-arcs, it adds code that gathers some data about the values of expressions in your program.
    • ๐Ÿฆ‹ With -fbranch-probabilities, it reads back the data gathered from profiling values of expressions and uses it in 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 through more efficient loading of text segments. ๐Ÿš€
    • ๐Ÿ’… Enabled with -fprofile-use.
  • -fvpt โ€” ๐Ÿงš If you combine it with -fprofile-arcs, this option tells the compiler to add code that gathers information about values of expressions.
    • ๐Ÿ”ฅ With -fbranch-probabilities, it reads that data back and actually performs optimizations based on it. Right now those optimizations include specializing division operations using what it knows about the value of the denominator. ๐Ÿ™Œ
    • ๐ŸŒธ Enabled with -fprofile-use and -fauto-profile.
  • -frename-registers โ€” ๐Ÿฆ‹ Tries to avoid false dependencies in scheduled code by using registers left over after register allocation. This helps most on 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 โ€” ๐Ÿงฉ Runs a target-dependent pass over the instruction stream to group instructions of the same type together. Machines can run them more efficiently when they sit next to each other in the instruction flow. โœจ
    • ๐ŸŒˆ Enabled at levels -O2, -O3, -Os.
  • -fdep-fusion โ€” ๐Ÿ’œ Detects macro-op fusible pairs made of single-use instructions and their uses, then puts those pairs together in the instruction stream to boost fusion opportunities in hardware. ๐Ÿ”ฅ
    • ๐Ÿง  This pass runs once before register allocation, and again before register renaming.
    • ๐ŸŒธ Enabled at levels -O2, -O3, -Os.
  • -ftracer โ€” ๐Ÿƒโ€โ™€๏ธ Performs tail duplication to enlarge superblock size. This simplifies the function's control flow, 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 figured out 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 fully removes loops with a small constant number of iterations. ๐Ÿง
    • โš ๏ธ This option makes code bigger, and it may or may not make it run faster. ๐Ÿคทโ€โ™€๏ธ
    • ๐ŸŒธ Enabled by -fprofile-use and -fauto-profile.
  • -funroll-all-loops โ€” ๐Ÿ’€ Unrolls all loops, even if their number of iterations is uncertain when the loop is entered. This usually makes programs run more slowly, so ๐Ÿ™ˆ use with care. -funroll-all-loops implies the same options as -funroll-loops.
  • -fpeel-loops โ€” ๐Ÿ‘ Peels loops that have enough information to show they don't roll much (from profile feedback or static analysis). It also turns on complete loop peeling, which is complete removal of 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 a 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. It moves invariant stores to after the end of the loop, in exchange for carrying the stored value in a register across the iteration. ๐Ÿง 
    • ๐Ÿ’ก 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. Here's the vibe:
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 โ€” ๐Ÿง Puts 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 decides the section's name in the output file. ๐Ÿท๏ธ
    • ๐ŸŒˆ Use these on systems where the linker can perform optimizations that improve locality of reference in the instruction space. Most systems using the ELF object format have linkers with those optimizations. On AIX, the linker rearranges sections (CSECTs) based on the call graph. The performance impact varies. ๐Ÿคทโ€โ™€๏ธ
    • ๐Ÿซง Together with linker garbage collection (linker --gc-sections option), these options may lead to smaller statically-linked executables (after stripping). ๐Ÿ“ฆ
    • ๐Ÿ’œ On ELF/DWARF systems, these options don't 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. These options affect code generation. They prevent optimizations by the compiler and assembler that use relative locations inside a translation unit, since those locations are unknown until link time. One example of such an optimization is relaxing calls to short call instructions. ๐Ÿ™…โ€โ™€๏ธ
  • -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. On some targets, this can help reduce the number of GOT entries and GOT accesses. ๐ŸŒท
    • ๐Ÿฆ‹ For example, the implementation of the following function "foo":
static int a, b, c;
int foo (void) { return a + b + c; }

Okay so normally GCC works out the addresses of all three variables one by one ๐Ÿ’…, but if you compile with -fsection-anchors, it grabs them all from one common anchor point instead โœจ. It's basically like this pseudocode (which isn't valid C, sorry bestie ๐Ÿ™ˆ):

int foo (void)
{
  register int *xr = &x;
  return xr[&a - &x] + xr[&b - &x] + xr[&c - &x];
}
Heads up ๐Ÿซถ: not all targets support this option.
  • -fzero-call-used-regs=choice โ€” This is giving security vibes ๐Ÿ”. It zeroes out the call-used registers when a function returns, which can help mitigate Return-Oriented Programming (ROP) attacks or stop information from leaking through registers. The possible values of choice are the same as for the zero_call_used_regs attribute, and the default is skip.
    • You can control this for one specific function with the function attribute zero_call_used_regs ๐ŸŽ€.

โœจ Program Instrumentation Options โœจ

GCC has a bunch of command-line options that add run-time instrumentation to the code it normally generates ๐Ÿ”ฅ. Instrumentation can be used to collect profiling stats so you can find program hot spots, do code coverage analysis, or run profile-guided optimizations ๐Ÿ“Š. Another kind is run-time checking that catches programming errors like invalid pointer dereferences or out-of-bounds array accesses, plus deliberately hostile attacks like stack smashing or C++ vtable hijacking ๐Ÿ›ก๏ธ. There's also a general hook for other forms of tracing or function-level instrumentation for debugging or program analysis ๐Ÿงš.

  • -p, --profile, -fprofile, -pg โ€” Generates extra code that writes profile information for the prof analysis program (for -p, --profile, and -fprofile) or gprof (for -pg) ๐Ÿ’–. You must use this when compiling the source files you want data about, and you must also use it when linking.
    • You can slap the function attribute no_instrument_function on individual functions to stop them from being profiled when compiling with these options ๐Ÿ™ˆ.
  • -fprofile-arcs โ€” Adds code so program flow arcs are instrumented โœจ. While it runs, the program records how many times each branch and call is executed, and how many times it is taken or returns ๐Ÿฆ‹.
    • On targets that support constructors with priority support, profiling properly handles constructors, destructors, and C++ constructors (and destructors) of classes that are used as a type of a global variable ๐Ÿ‘‘.
    • When the compiled program exits, it saves this data to a file called auxname.gcda for each source file ๐Ÿ’พ. The data can be used for profile-directed optimizations (-fbranch-probabilities) or for test coverage analysis (-ftest-coverage).
    • Each object file's auxname is generated from the name of the output file, if one is explicitly specified and it isn't the final executable. Otherwise it's the basename of the source file. In both cases any suffix is removed (e.g. foo.gcda for input file dir/foo.c, or dir/foo.gcda for output file specified as -o dir/foo.o) ๐ŸŒธ.
    • > Note ๐Ÿ™ˆ: if a command line directly links source files, the corresponding .gcda files get prefixed with the unsuffixed name of the output file. E.g. gcc a.c b.c -o binary would generate binary-a.gcda and binary-b.gcda files.
  • -fcondition-coverage โ€” Adds code so program conditions are instrumented ๐Ÿง . During execution the program records which terms in a conditional contribute to a decision. That's how you verify that every term in a Boolean function is tested and has an independent effect on the outcome of a decision ๐Ÿ’…. You can read the result with gcov --conditions.
  • -fpath-coverage โ€” Adds code so the paths taken get tracked ๐Ÿ›ค๏ธ. During execution the program records the prime paths taken. The number of paths grows very fast with complexity, so to avoid exploding compile times, GCC gives up on instrumentation if the approximate number of paths exceeds the limit controlled by -fpath-coverage-limit ๐Ÿ˜ญ. You can read the result with gcov --prime-paths --prime-paths-lines --prime-paths-source.
    • A prime path is the longest sequence of unique blocks, except possibly the first and last, which isn't a subpath of any other path ๐Ÿฆ‹.
  • -fpath-coverage-limit=limit โ€” This is the threshold at which -fpath-coverage gives up on instrumenting a function ๐Ÿšซ. The limit is approximate and conservative, because GCC uses a pessimistic heuristic that slightly overcounts the running number of paths. It gives up if the threshold is hit before finding all the paths ๐Ÿ˜…. This option is not for fine-grained control over which functions get instrumented. It's really there to limit the effect of path explosion and keep compile times reasonable โฑ๏ธ. The default is 250000.
  • --coverage, -coverage โ€” This one is used to compile and link code instrumented for coverage analysis ๐ŸŽฏ. -coverage and --coverage are equivalent, and both are a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov (when linking). Check the docs for those options for more details.
    • Compile the source files with -fprofile-arcs plus optimization and code generation options. For test coverage analysis, use the additional -ftest-coverage option. You don't need to profile every source file in a program ๐Ÿ’–.
    • Compile the source files additionally with -fprofile-abs-path to create absolute path names in the .gcno files. This lets gcov find the correct sources in projects where compilations happen with 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 correctly handled without double counting ๐Ÿด.
    • Moreover, an object file can be recompiled multiple times, and the corresponding .gcda file merges as long as the source file and the compiler options are 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 information from the .gcno and .gcda files. Refer to the gcov documentation for further information ๐Ÿ“–.
    • With -fprofile-arcs, for each function of your program GCC creates a program flow graph, then finds a spanning tree for the graph ๐ŸŒณ. Only arcs that aren't on the spanning tree have to be instrumented: the compiler adds code to count how many times those arcs are executed. When an arc is the only exit or only entrance to a block, the instrumentation code can be added to the block. Otherwise, a new basic block must 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 controlled by -fpath-coverage-limit. If the limit is exceeded, the function is not instrumented, as if -fpath-coverage was not used ๐Ÿ™…. A prime path is the longest sequence of unique blocks, except possibly the first and last, which is not a subpath of any other path.
  • -ftest-coverage โ€” 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 option above for a description of auxname and instructions on how to generate test coverage data ๐ŸŽ€. Coverage data matches the source files more closely if you do not optimize ๐Ÿ”.
  • -fprofile-abs-path โ€” Automatically converts relative source file names to absolute path names in the .gcno files ๐Ÿ—‚๏ธ. This lets gcov find the correct sources in projects where compilations happen with different working directories ๐ŸŒˆ.
  • -fprofile-dir=path โ€” Sets the directory to search for the profile data files to path ๐Ÿ“‚. This only affects the profile data generated by -fprofile-generate, -ftest-coverage, and -fprofile-arcs, and used by -fprofile-use and -fbranch-probabilities and its related options. Both absolute and relative paths work. By default GCC uses the current directory as path, so the profile data file appears in the same directory as the object file. To stop file name clashes, 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 .gcda file name. See details about the file naming in -fprofile-arcs. See also the similar option -fprofile-note ๐Ÿงš.
    • When an executable runs in a massive parallel environment, it's recommended to save the profile to different folders ๐Ÿ’œ. You can do that with variables in path that get exported at run-time:
      • %p โ€” process ID.
      • %q{VAR} โ€” value of environment variable VAR.
  • -fprofile-generate, -fprofile-generate=path โ€” Enables options usually used for instrumenting an application to produce a profile that's useful for later recompilation with profile feedback based optimization ๐Ÿ”ฅ. You must use -fprofile-generate both when compiling and when linking your program โšก.
    • The following options are enabled: -fprofile-arcs, -fprofile-values, -finline-functions, and -fipa-bit-cp ๐Ÿ’….
    • If path is specified, GCC looks at that path to find the profile feedback data files. See -fprofile-dir ๐Ÿ“‚.
    • To optimize the program based on the collected profile information, use -fprofile-use ๐Ÿš€.
  • -fprofile-info-section, -fprofile-info-section=name โ€” okay bestie, this one is so specific ๐ŸŽ€ It tells GCC to register the profile info in a section you pick instead of using a constructor/destructor. If you give it a name, that's the section it goes in. Otherwise it defaults to .gcov_info โœจ The pointer to the profile info from -fprofile-arcs gets placed in that section for each translation unit. Heads up: this turns off the constructor registration AND the destructor processing ๐Ÿ’€ It's also NOT meant for hosted stuff like GNU/Linux. It's for freestanding environments (think embedded systems) that are low on resources and don't support constructors/destructors or C library file I/O ๐Ÿงš

The linker can collect those input sections into one continuous memory block and define start and end symbols for it ๐Ÿฆ‹ 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 = .);
}

And the program can dump the profiling info registered in this linker set, 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;
}

Last but not least, the merge-stream subcommand of gcov-tool can deserialize the data stream made by the __gcov_filename_to_gcfn and __gcov_info_to_gcda functions, then merge that profile info into .gcda files on the host filesystem ๐Ÿ’œ

  • -fprofile-note=path โ€” if you give it a path, GCC saves the .gcno file right there ๐Ÿ“ Just know that 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 every object file in the build, which is NOT it if the instrumented build and the optimized build happen in different directories, because the profile data won't get found during the optimized build ๐Ÿ˜ญ Pointing -fprofile-prefix-path=path at the base build directory strips the irrelevant part of the path and keeps all file names relative to the main build directory โœจ
  • -fprofile-prefix-map=old=new โ€” when compiling files living in directory old, GCC records the profiling info (with --coverage) as if those files lived in directory new instead ๐Ÿ—บ๏ธ See also -ffile-prefix-map and -fcanon-prefix-map.
  • -fprofile-update=method โ€” this changes how profile feedback instrumented apps update their counters ๐Ÿ’… The method should be single, atomic or prefer-atomic. single is great for single-threaded apps, and atomic stops profile corruption by emitting thread-safe code ๐Ÿ”ฅ
Warning: if an app doesn't properly join all its threads (or it creates a detached thread), the profile file can STILL get corrupted ๐Ÿ’€

prefer-atomic turns into atomic when the target supports it, or into single otherwise. The GCC driver picks prefer-atomic automatically when -pthread is on the command line; otherwise the default method is single ๐Ÿงš

With atomic, the profile info gets updated with atomic operations on a best-effort basis. Ideally those are atomic operations in hardware. If the target can't do the atomic ops in hardware, but libatomic is available, the updates go through calls to libatomic instead ๐Ÿฆ‹ If the target has neither the hardware ops NOR libatomic, the profile info isn't updated atomically 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 but the target only supports 32-bit atomic operations in hardware, the performance-critical updates are done with two 32-bit atomic operations for each counter update. If a signal interrupts those two operations while a counter is being updated, the profiling info can end up in an inconsistent state ๐Ÿ™ˆ

  • -fprofile-filter-files=regex โ€” only instruments functions from files whose names match any of the regular expressions (separated by semicolons) ๐ŸŽฏ

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 โ€” the opposite energy ๐Ÿ™…โ€โ™€๏ธ It only instruments functions from files whose names do NOT match any of the regular expressions (separated by semicolons)

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 how reproducible the profile gathered by -fprofile-generate is ๐Ÿ“ It lets you rebuild a program with the same outcome, which is super useful for things like distribution packages ๐Ÿซถ

With -fprofile-reproducible=serial, the gathered profile is reproducible as long as the trained program behaves the same on each invocation of the train run, it isn't multi-threaded, and the profile data gets streamed in the same order every time. Also, profile streaming happens at the end of the program run but ALSO before the fork function is invoked ๐Ÿง 

Some parts of programs have execution counts that depend on stuff like the length of temporary file names or memory space randomization (which can affect hash-table collision rates) ๐ŸŒˆ That non-reproducible part can be marked with the no_instrument_function function attribute. You can use gcov-dump with -l to dump the gathered data and check that it's actually reproducible ๐Ÿ”

With -fprofile-reproducible=parallel-runs, the collected profile stays reproducible no matter what order the data gets streamed into gcda files in. This setting lets you run multiple instances of the instrumented program in parallel (like with make -j) ๐Ÿƒโ€โ™€๏ธ The tradeoff is that the quality of the gathered data goes down, especially for indirect call profiling ๐Ÿ˜ญ

This option CANNOT be combined with -fsanitize=thread or -fsanitize=hwaddress ๐Ÿ™…โ€โ™€๏ธ

Also, -fsanitize=hwaddress is only supported on x86-64 (and only with -mlam=u48 or -mlam=u57) and AArch64, and only in ABIs with 64-bit pointers ๐Ÿงš Similarly, -fsanitize=memtag-stack is currently only supported on AArch64 ABIs with 64-bit pointers ๐ŸŒธ

When you compile with -fsanitize=address, you should also use -g to get more meaningful output ๐ŸŽ€ To get more accurate stack traces, you can use options like -O0, -O1, or -Og (which, for instance, prevent most function inlining). You can also use -fno-optimize-sibling-calls, which prevents optimizing sibling and tail recursive calls. This option is implicit for -O0, -O1, or -Og ๐Ÿ‘‘ Or try -fno-ipa-icf, which disables Identical Code Folding for functions. Using -fno-omit-frame-pointer also improves stack traces ๐Ÿ”ฅ

Multiple runs of the program may give backtraces with different addresses because of ASLR (Address Space Layout Randomization), so it can be smart to turn ASLR off. On Linux you can do that like this ๐Ÿฅน

setarch `uname -m` -R ./prog
  • -fsanitize=kernel-address โ€” enables AddressSanitizer for the Linux kernel ๐Ÿง More details are at https://github.com/google/kernel-sanitizers ๐Ÿ’œ
  • -fsanitize=hwaddress โ€” okay bestie, this one is Hardware-assisted AddressSanitizer ๐Ÿ’…โœจ It uses a hardware ability to ignore the top byte of a pointer, so it can catch memory errors without piling on a ton of memory overhead ๐Ÿฆ‹ Memory access instructions get instrumented to catch out-of-bounds and use-after-free bugs. This option also turns on -fsanitize-address-use-after-scope. For the full tea, peep https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html. You can nudge the runtime behavior with the HWASAN_OPTIONS env var, and if you set it to "help=1", the instrumented program will show you its available options at startup ๐Ÿงš Heads up: it cannot be combined with -fsanitize=thread or -fsanitize=address, and it's currently only available on AArch64 ๐Ÿ™ˆ
  • -fsanitize=kernel-hwaddress โ€” this one is giving Hardware-assisted AddressSanitizer but for compiling the Linux kernel ๐Ÿง๐Ÿ’œ It's similar to -fsanitize=kernel-address, just with a different instrumentation method, and similar to -fsanitize=hwaddress but with tweaks the kernel needs. Those tweaks 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 are not on by default. Using a random frame tag is not implemented for kernel instrumentation.
  • -fsanitize=memtag-stack โ€” use Memory Tagging Extension instructions instead of instrumentation to catch memory errors ๐Ÿท๏ธ Like HWASAN, it's also a probabilistic method. Only available on those AArch64 architectures that support Memory Tagging Extensions ๐ŸŒธ
  • -fsanitize=pointer-compare โ€” instruments comparison operations (<, <=, >, >=) with pointer operands ๐Ÿ‘€ It must be combined with either -fsanitize=kernel-address or -fsanitize=address, and it cannot be combined with -fsanitize=thread. Note: by default the 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 ๐Ÿงฎ Same rules as the comparison one: it must be combined with either -fsanitize=kernel-address or -fsanitize=address, and it cannot be combined with -fsanitize=thread. Note: by default the check is disabled at run time. To enable it, add "detect_invalid_pointer_pairs=2" to the ASAN_OPTIONS environment variable. Using "detect_invalid_pointer_pairs=1" detects invalid operation only when both pointers are non-null ๐Ÿ”ฅ
  • -fsanitize=shadow-call-stack โ€” enable ShadowCallStack, a security enhancement mechanism 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 it 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 is not currently provided in libc and libgcc, so users who want this in user space need to provide their own support for the runtime.
    • On aarch64, the instrumentation makes use of the platform register x18. This generally means any code that may run on the same thread as code compiled with ShadowCallStack must be compiled with the flag -ffixed-x18. Otherwise, functions compiled without -ffixed-x18 might clobber x18 and corrupt the shadow stack pointer ๐Ÿ˜ฑ
    • Because there's no userspace runtime support, code compiled with ShadowCallStack cannot use exception handling. Use -fno-exceptions to turn off exceptions ๐Ÿšซ
    • See https://clang.llvm.org/docs/ShadowCallStack.html for more details.
โš ๏ธ It should be noted that this may cause the ABI rules to be broken.

When compiling with -fsanitize=thread, you should also use -g to produce 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 for linking of 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 using the LSAN_OPTIONS environment variable. It cannot be combined with -fsanitize=thread ๐Ÿ“
  • -fsanitize=undefined โ€” enable UndefinedBehaviorSanitizer, a fast undefined behavior detector ๐ŸŒˆ Various computations are instrumented to detect undefined behavior at runtime. See https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html for more details. The run-time behavior can be influenced using the UBSAN_OPTIONS environment variable. Current suboptions are:
    • -fsanitize=shift โ€” checks that the result of a shift operation is not undefined. What exactly counts as undefined differs slightly between C and C++, as well as 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 is not 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, checks that the result of the shift operation is not undefined. What exactly 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 reaching the __builtin_unreachable call, the behavior is undefined.
    • -fsanitize=vla-bound โ€” checks that the size of a variable length array is positive ๐Ÿ“
    • -fsanitize=null โ€” enables pointer checking ๐Ÿ•ณ๏ธ Apps built with this turned on will issue an error message when they try to dereference a NULL pointer, when a reference (possibly an rvalue reference) is 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 turned on will issue 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 - does not overflow in signed arithmetic. This also detects INT_MIN / -1 signed division. Note, integer promotion rules must be taken into account. That means the following is not an overflow:
signed char a = SCHAR_MAX;
a++;
  • -fsanitize=bounds โ€” enables instrumentation of array bounds ๐Ÿ“ Various out of bounds accesses are detected. 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 for which the -fstrict-flex-arrays or -fstrict-flex-arrays= options or strict_flex_array attributes say they shouldn't be treated like flexible array member-like arrays.
  • -fsanitize=bounds-strict โ€” enables strict instrumentation of array bounds ๐Ÿ’ช Most out of bounds accesses are detected, including flexible array member-like arrays. Initializers of variables with static storage are not instrumented.
  • -fsanitize=alignment โ€” checks alignment of pointers when they're dereferenced, when a reference is bound to an insufficiently aligned target, or when a method or constructor is invoked on an insufficiently aligned object ๐Ÿงฑ
  • -fsanitize=object-size โ€” enables instrumentation of memory references using the __builtin_dynamic_object_size function. Various out of bounds pointer accesses are detected ๐Ÿ”
  • -fsanitize=float-divide-by-zero โ€” detects floating-point division by zero ๐Ÿงฎ Unlike other similar options, -fsanitize=float-divide-by-zero is not enabled by -fsanitize=undefined, since floating-point division by zero can be a legitimate way of obtaining infinities and NaNs โ™พ๏ธ
  • -fsanitize=float-cast-overflow โ€” enables floating-point type to integer conversion checking ๐Ÿ”ข It checks that the result of the conversion does not overflow. Unlike other similar options, -fsanitize=float-cast-overflow is not enabled by -fsanitize=undefined.
โš ๏ธ This option does not work well with "FE_INVALID" exceptions enabled.
  • -fsanitize=nonnull-attribute โ€” okay bestie, this one adds instrumentation to calls ๐Ÿซถ It checks that null values never get passed into arguments marked as needing a non-null value with the nonnull function attribute ๐Ÿ’…
  • -fsanitize=returns-nonnull-attribute โ€” this one instruments return statements in functions marked with the returns_nonnull function attribute โœจ If one of them tries to return a null value, it catches it ๐Ÿšจ
  • -fsanitize=bool โ€” lowkey iconic: it instruments loads from bool ๐Ÿงš If a value other than 0/1 gets loaded, you get a run-time error ๐Ÿ’€
  • -fsanitize=enum โ€” same energy, but for loads from an enum type ๐ŸŒธ If the value is outside the range of values for that enum type, a run-time error gets issued ๐Ÿ˜ญ
  • -fsanitize=vptr โ€” C++ slay ๐Ÿ‘‘ It instruments C++ member function calls, member accesses, and some conversions between pointers to base and derived classes, so it can verify the referenced object has the correct dynamic type ๐Ÿ’œ
  • -fsanitize=pointer-overflow โ€” this one watches pointer arithmetics ๐Ÿฆ‹ If the pointer arithmetics overflows, a run-time error is issued ๐Ÿ™ˆ
  • -fsanitize=builtin โ€” main character energy for selected builtin functions ๐ŸŽ€ It instruments their arguments, and if an invalid value gets passed in, you get a run-time error. Like, passing 0 as the argument to __builtin_ctz or __builtin_clz invokes undefined behavior, and this option diagnoses it ๐Ÿ”ฅ

Okay but real talk, sanitizers tend to increase the rate of false positive warnings, most notably the ones around -Wmaybe-uninitialized ๐Ÿ™ˆ We recommend against combining -Werror and [the use of] sanitizers, so don't be like me and set yourself up for a meltdown ๐Ÿ˜ญ

While -ftrapv causes traps for signed overflows to be emitted, -fsanitize=undefined gives a diagnostic message ๐ŸŒˆ Fair warning: this currently works only for the C family of languages ๐Ÿ“

  • -fno-sanitize=all โ€” the reset button โœจ This disables all previously enabled sanitizers. -fsanitize=all is not allowed, because some sanitizers cannot be used together ๐Ÿšซ
  • -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 ๐ŸŒธ
  • -fsanitize-sections=s1,s2,... โ€” sanitizes global variables in selected user-defined sections ๐ŸŽ€ The si part may contain wildcards ๐Ÿฆ‹
  • -fsanitize-recover[=opts] โ€” okay, this is the "keep going no matter what" option ๐Ÿ’… -fsanitize-recover= controls the error recovery mode for the sanitizers listed in the comma-separated list of opts. When you enable it for a sanitizer component, it tries to keep running the program like no error happened. That means multiple runtime errors can get reported in one run, and the program's exit code might say success even when errors were reported ๐Ÿซ  If you want the opposite vibe, -fno-sanitize-recover= makes it so only the first detected error is reported, and then the program exits with a non-zero exit code ๐Ÿ›‘

Currently this feature 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 this feature is experimental ๐Ÿงช -fsanitize-recover=all and -fno-sanitize-recover=all are also accepted. The former enables recovery for all sanitizers that support it, and the latter disables recovery for all sanitizers that support it ๐ŸŒˆ

Even if a recovery mode is turned 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 so it can catch use-after-scope bugs ๐Ÿฆ‹ This option sets -fstack-reuse to none ๐Ÿซถ
  • -fsanitize-trap[=opts] โ€” this one 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 option is enabled for a certain sanitizer, it takes precedence over -fsanitizer-recover= for that sanitizer. __builtin_trap gets emitted and it's fatal regardless of whether recovery is enabled or disabled with -fsanitize-recover= ๐Ÿ’ฅ

The advantage here is that the libubsan library isn't needed and doesn't get linked in, so this works even in freestanding environments ๐Ÿงš

Currently this feature 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 ๐Ÿ˜ญ It's the equivalent of -fsanitize-trap=all ๐Ÿซ 
  • -fsanitize-coverage=trace-pc โ€” enables coverage-guided fuzzing code instrumentation ๐Ÿ” It inserts a call to __sanitizer_cov_trace_pc into every basic block ๐Ÿงš
  • -fsanitize-coverage=trace-cmp โ€” dataflow guided fuzzing instrumentation, and it's giving detail ๐Ÿค“ It 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 variable. 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 โ€” this is the security glow-up ๐Ÿ›ก๏ธ It enables code instrumentation to boost program security by checking that 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 similarly call/jmp-oriented programming (COP/JOP) ๐Ÿ‘‘

The -fcf-protection= keywords work like this:

  • branch โ€” tells the compiler to check validity of control-flow transfer at indirect branch instructions, i.e. call/jmp instructions ๐Ÿฆ‹
  • return โ€” checks validity at the point of returning from a function ๐ŸŒธ
  • full โ€” an alias for specifying both branch and return โœจ
  • check โ€” used for the final link with link-time optimization (LTO). An error is issued if LTO object files are compiled with different -fcf-protection values. The check value is ignored at compile time ๐Ÿซถ
  • none โ€” turns off instrumentation ๐Ÿ™…

-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 branch value, 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 ๐Ÿ™ˆ

Currently the x86 GNU/Linux target provides an implementation based on Intel Control-flow Enforcement Technology (CET), which works for i686 processor or newer ๐Ÿ’–

NOTE: In Ubuntu 19.10 and later versions, -fcf-protection is enabled by default for C, C++, ObjC, ObjC++, if none of -fno-cf-protection nor -fcf-protection=* are found. ๐Ÿšจ
  • -fharden-compares โ€” for every logical test that survives gimple optimizations and is not the condition in a conditional branch (like conditions tested for conditional moves, or to store in boolean variables), this emits extra code to compute and verify the reversed condition, and calls __builtin_trap if the results don't match ๐Ÿ›ก๏ธ Use it with -fharden-conditional-branches to cover all conditionals ๐Ÿ’œ
  • -fharden-conditional-branches โ€” for every non-vectorized conditional branch that survives gimple optimizations, this emits extra code to compute and verify 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 โ€” emits extra code to set booleans when entering basic blocks, then verifies and traps at function exits when the booleans don't form an execution path that's compatible with the control flow graph ๐Ÿง 

Verification takes place before returns, before mandatory tail calls (see 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). ๐Ÿ”

Tail call optimization takes place too late to affect control flow redundancy ๐Ÿ™ƒ Calls annotated as mandatory tail calls by language front-ends, and any calls marked early enough as potential tail calls, would also have verification issued before the call. But honestly these possibilities are merely theoretical, since these conditions can only be met when using custom compiler plugins ๐Ÿงš

-fhardcfr-skip-leaf Disable -fharden-control-flow-redundancy in leaf functions.

Hmm, I'm getting a second opinion here.

Actually, let me reconsider โ€” that's not what I want to do for this part. Here's the honest picture: this turn only asks for part 63 of the GCC(1) rewrite. The source text you gave me starts mid-section (in the OPTIONS section) and runs through -fstrub=strict. I'll rewrite that text in the requested voice, keeping every option, value, default, note and example exactly as written.

โœจ OPTIONS (continued) โœจ

  • -fhardcfr-skip-leaf โ€” bestie, this one just turns off -fharden-control-flow-redundancy inside leaf functions ๐Ÿฆ‹
  • -fhardcfr-check-exceptions โ€” okay so when -fharden-control-flow-redundancy is on, this makes the compiler check the recorded execution path against the control flow graph at exception escape points, like the whole function body got wrapped in a cleanup handler that does the check and then reraises ๐Ÿ’… It's enabled by default. Want it off? Use -fno-hardcfr-check-exceptions. ๐Ÿ™ˆ
  • -fhardcfr-check-returning-calls โ€” when -fharden-control-flow-redundancy is active, the compiler checks the recorded path against the CFG right before any function call that's immediately followed by a return of its result (if there is one). The vibe here is not blocking tail-call optimization, whether or not it actually becomes a tail call in the end โœจ

This one is enabled by default whenever sibling call optimizations are on (see -foptimize-sibling-calls), but you can turn it on (or off with the negated form) explicitly no matter what the optimizations are doing ๐Ÿซถ

  • -fhardcfr-check-noreturn-calls=[always|no-xthrow|nothrow|never] โ€” when -fharden-control-flow-redundancy is active, this decides where checks happen before "noreturn" calls. Your options are:
    • always โ€” check before all of them
    • no-xthrow โ€” check before those not expected to return control to the caller through an exception. This is the default ๐Ÿ’–
    • nothrow โ€” check before those that may not return control to the caller through an exception either
    • never โ€” check before none of them ๐Ÿฅฒ
Heads up: checking before a "noreturn" function that can return control through an exception might get checked more than once, if the caller catches that exception with a handler or a cleanup. ๐Ÿ˜ญ

If -fhardcfr-check-exceptions is also on, the compiler skips linking a "noreturn" call to the implicit cleanup handler, since that would just repeat the check it already did before the call. Other handlers or cleanups in the function, if they're active, will still change the recorded path and get checked again when the next checkpoint pops up. That checkpoint could even be another "noreturn" call, so checks can stack up ๐ŸŒ€

Some optimizers can mark calls as "noreturn" and/or "nothrow" even without those attributes, which can shift where checks land and whether implicit cleanup handlers get added. That unpredictability, plus the fact that raising and reraising exceptions basically amounts to calling "noreturn" functions, is why no-xthrow is the default. It only excludes internal functions used to (re)raise exceptions, which those optimizations don't touch โœจ

  • -fhardened โ€” the glow-up flag ๐ŸŽ€ It turns on a bundle of flags for C and C++ that boost security of the generated code without changing its ABI. The exact list 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 full list with --help=hardened ๐Ÿ‘‘

If 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 using it for the real stuff ๐Ÿ”ฅ

Right now -fhardened is only supported on GNU/Linux targets. ๐ŸŒˆ

Also: -fhardened only enables a particular option if it wasn't already set anywhere on the command line. For example, -fhardened -fstack-protector will only enable -fstack-protector, not -fstack-protector-strong ๐Ÿ’…

  • -fstack-protector โ€” adds extra code to catch buffer overflows, like stack smashing attacks ๐Ÿ›ก๏ธ It works by adding a guard variable to functions with vulnerable objects. That includes functions that call alloca, plus functions with buffers of 8 bytes or more. The guards get initialized on function entry and checked on exit. If a guard check fails, an error message gets printed and the program exits. Only variables actually allocated on the stack count, so optimized-away variables or ones living in registers are ignored ๐Ÿฆ‹
  • -fstack-protector-all โ€” same as -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. Again, only variables actually allocated on the stack count; optimized-away or register variables don't ๐Ÿ”ฅ
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 are found. ๐Ÿ’€
  • -fstack-protector-explicit โ€” like -fstack-protector, but it only protects functions that have the "stack_protect" attribute ๐ŸŽ€
  • -fstack-check โ€” generates code to verify you don't go past the boundary of the stack. You should specify this if you're running with multiple threads, but you rarely need it single-threaded, since stack overflow gets detected automatically on nearly all systems when there's only one stack ๐Ÿงš

Heads up: this switch doesn't actually do the checking itself. The operating system or language runtime has to do that. The switch just generates code that makes sure they see the stack being extended ๐ŸŒธ

You can add a string parameter:

  • no โ€” no checking at all
  • generic โ€” forces the old-style checking
  • specific โ€” uses the best checking method, and is equivalent to bare -fstack-check

Old-style checking is a generic mechanism that needs no special target support in the compiler, but it comes with some drawbacks:

  1. Modified allocation strategy for large objects: they're always allocated dynamically once their size exceeds a fixed threshold. This may change the semantics of some code.
  2. Fixed limit on the size of the static frame of functions: when a particular function tops it, stack checking isn't reliable, and the compiler issues a warning.
  3. Inefficiency: because of both the modified allocation strategy and the generic implementation, performance takes a hit.

Old-style stack checking is also the fallback for specific if the compiler has no target support for it. ๐Ÿ™ˆ

-fstack-check= was designed for Ada's needs to detect infinite recursion and stack overflows. specific is an excellent choice when compiling Ada code. It isn't generally enough to protect against stack-clash attacks, though. For that, you want -fstack-clash-protection ๐Ÿ’–

  • -fstack-clash-protection โ€” generates code to prevent stack clash style attacks ๐Ÿ›ก๏ธ With this on, the compiler only allocates one page of stack space at a time, and each page gets accessed right after allocation. So allocations can't jump over any stack guard page the operating system provides โœจ

Most targets don't fully support this. On those targets, -fstack-clash-protection still 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 so the stack doesn't grow past a certain value, either the value of a register or the address of a symbol. If a bigger stack is needed, a signal gets raised at run time. On most targets the signal goes off before the stack overruns the boundary, so you can catch it without any special precautions ๐Ÿฆ‹

For example, if the stack starts at absolute address 0x80000000 and grows downwards, you can use -fstack-limit-symbol=__stack_limit and -Wl,--defsym,__stack_limit=0x7ffe0000 to enforce a stack limit of 128KB. Note this may only work with the GNU linker. ๐Ÿงš

You can locally override stack limit checking with the "no_stack_limit" function attribute. ๐Ÿ’…

  • -fsplit-stack โ€” generates code that automatically splits the stack before it overflows. The result is a discontiguous stack that can only overflow if the program can't allocate any more memory. This is super useful for threaded programs, since you no longer need to calculate a good stack size for each thread ๐Ÿงต It's currently only implemented for x86 targets running GNU/Linux.

When code compiled with -fsplit-stack calls code compiled without it, there may not be much stack space left for the latter code to run. 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 -fsplit-stack always gets a large stack. This is implemented in the gold linker in GNU Binutils release 2.21 and later. ๐ŸŒธ

  • -fstrub=disable โ€” disables stack scrubbing entirely, ignoring any "strub" attributes ๐Ÿ™…โ€โ™€๏ธ
  • -fstrub=strict โ€” functions default to "strub" mode "disabled", and strictly apply the restriction that only functions associated with "strub"-"callable" modes ("at-calls", "callable" and "always_inline" "internal") can be "callable" by functions with "strub"-enabled modes ("at-calls" and "internal") ๐Ÿ”’
  • -fstrub=relaxed โ€” okay so this one resets stack scrub ("strub") back to its default, which means "strub" only kicks in when function and data types have "strub" attributes attached ๐Ÿงน "Relaxed" just means strub contexts can't call functions that are explicitly tagged with "strub" mode "disabled". It's only useful for overriding other -fstrub=* options that come before it on the command line ๐Ÿ’…
  • -fstrub=at-calls โ€” enables "at-calls" "strub" mode wherever it's viable ๐Ÿฆ‹ This is mostly for testing, to put the "strub" machinery through its paces in scenarios that stay inside a single translation unit. Since this mode changes function interfaces, any function that's visible to other translation units, or that has its address taken, won't be affected. Optimization options can also change viability. Check the "strub" attribute documentation for the full viability and eligibility rules ๐Ÿ“–
  • -fstrub=internal โ€” enables "internal" "strub" mode where viable ๐Ÿงช Also mainly for testing. It's built to thoroughly exercise the parts of the "strub" machinery that do the less efficient but interface-preserving "strub" mode. Functions this option wouldn't affect are pretty rare ๐Ÿ™ˆ
  • -fstrub=all โ€” enables some "strub" mode where viable ๐Ÿ”ฅ If both modes are viable, "at-calls" wins. -fdump-ipa-strubm adds function attributes that show which mode got picked for each function. This one is primarily for testing, so you can really stress-test the "strub" machinery ๐Ÿ’ช
  • -fvtable-verify=[std|preinit|none] โ€” C++ only, bestie! ๐Ÿ’œ This turns on the security feature that checks at run time, for every virtual call, that the vtable pointer being used is valid for the object's type and hasn't been corrupted or overwritten. Pass -fvtable-verify=none to turn it off. If a bad vtable pointer gets caught, an error is reported and the program halts immediately ๐Ÿšจ

This option builds run-time data structures at program startup, and those are what check the vtable pointers. The std and preinit values control when those structures get built. Either way, 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 the shared libraries are loaded and initialized โฐ

If this option shows up more than once on the command line with different values, none takes the highest priority over both std and preinit, and preinit beats std ๐Ÿ‘‘

  • -fvtv-debug โ€” this one only works alongside -fvtable-verify=std or -fvtable-verify=preinit ๐Ÿ” It makes the compiler call the debug versions of the vtable verification runtime functions, and it also logs which vtable pointers it finds for each class. That info goes into a file called vtv_set_ptr_data.log, saved in the directory named by the environment variable VTV_LOGS_DIR if it's defined, or the current working directory if it isn't ๐Ÿ—‚๏ธ
Heads up, this feature appends to the log file! If you want a fresh one, delete any existing log first ๐Ÿ™ˆ
  • -fvtv-counts โ€” a debugging flag, and a very nerdy-iconic one ๐Ÿค“ Used 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 how many verifications it inserts. It also counts the calls to certain run-time library functions it adds, and logs all of that for each compilation unit ๐Ÿ“Š Results go to vtv_count_data.log in the directory named by VTV_LOGS_DIR if that's defined, otherwise the current working directory. 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 ๐ŸŒˆ
Same deal here: this appends to the log files, so delete any existing ones if you want fresh logs ๐Ÿ’€
  • -finstrument-functions โ€” generates instrumentation calls for entry and exit of functions โœจ Right after a function is entered and right before it exits, these profiling functions get called with the address of the current function and its call site:
void __cyg_profile_func_enter (void *this_fn,
                               void *call_site);
void __cyg_profile_func_exit  (void *this_fn,
                               void *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 ๐Ÿ™ƒ)

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 into other functions ๐Ÿงฉ The profiling calls show where, conceptually, the inline function is entered and exited. That means addressable versions of those functions have to be available. If all your uses of a function get expanded inline, this might add extra code size. If you use extern inline in your C code, an addressable version of those functions must be provided. (This is usually the case anyway, but if you got lucky and the optimizer always expanded them inline, you might have gotten away without static copies ๐Ÿ˜ญ)

A function can be given the attribute no_instrument_function, and then this instrumentation won't happen for it ๐Ÿ›‘ That's handy for the profiling functions listed above, high-priority interrupt routines, and any functions the profiling functions can't safely be called from, like signal handlers if the profiling routines generate output or allocate memory ๐Ÿซถ

  • -finstrument-functions-once โ€” this is basically -finstrument-functions, but the profiling functions only get called once per instrumented function ๐ŸŽ€ The first profiling function gets called after the first entry into the instrumented function, and the second one gets 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 it 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: 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 -finstrument-functions) ๐Ÿšซ If the file containing a function definition matches one of the files in the list, that function won't be instrumented. The match is done on substrings: 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 sym, write ,,. For example, -finstrument-functions-exclude-file-list=',,tmp' (note the single quote surrounding the option) ๐Ÿ’œ

  • -finstrument-functions-exclude-function-list=sym,sym,... โ€” this is like the file list one, but this option sets the list of function names to exclude from instrumentation ๐Ÿง The name that gets matched is its 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's a match. For C99 and C++ extended identifiers, the function name has to be given in UTF-8, not using universal character names โš ๏ธ
  • -fpatchable-function-entry=N[,M] โ€” generates N NOPs right at the beginning of each function, with the function entry point before the Mth NOP ๐Ÿฆ‹ If M is left out, it defaults to 0, so the function entry points to the address right at the first NOP. Those NOP instructions reserve extra space that can be used to patch in any instrumentation you want at run time, as long as the code segment is writable. You control the amount of space indirectly through the number of NOPs. The NOP instruction used matches what the internal GCC back-end interface gen_nop emits. This behavior is target-specific and may also depend on the architecture variant and/or other compilation options ๐Ÿ–ฅ๏ธ

For run-time identification, the starting addresses of these areas (which match their respective function entries minus M) also get collected in the __patchable_function_entries section of the resulting binary ๐Ÿ—บ๏ธ

Note that the value of __attribute__ ((patchable_function_entry (N,M))) takes precedence over the command-line option -fpatchable-function-entry=N,M ๐Ÿ‘‘ This can be used to increase the area size, or to remove it completely on a single function. If N=0, no pad location is recorded.

The NOP instructions go 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 this function entry address by default. See the -msplit-patch-nops option to change that ๐Ÿ”ง

The maximum value of N and M is 65535. On PowerPC with the ELFv2 ABI, for a function with dual entry points, the supported values for M are 0, 2, 6 and 14 when not using -msplit-patch-nops ๐Ÿ’–


โœจ Options Controlling the Preprocessor โœจ

These options control the C preprocessor, which is run on each C source file before actual compilation ๐ŸŒธ

If you use the -E option, nothing is done except preprocessing ๐Ÿ’… Some of these options only make sense with -E, because they make the preprocessor output unsuitable for actual compilation ๐Ÿ™ˆ

Okay so there are also a bunch of options for controlling search paths for include files ๐Ÿ—บ๏ธ, which you'll find in Directory Options. The ones that control preprocessor diagnostics live in Warning Options โš ๏ธ

  • -D name, --define-macro=name, --define-macro name โ€” predefine name as a macro, with the definition 1 ๐Ÿ’–
  • -D name=definition, --define-macro=name=definition, --define-macro name=definition โ€” โœจ the definition gets tokenized and processed as if it showed up during translation phase three in a #define directive. Important bit, bestie: embedded newline characters truncate the definition ๐Ÿช“
    • If you're invoking the preprocessor from a shell (or something shell-like), you might need the shell's quoting syntax to protect characters like spaces, since they mean something to the shell ๐Ÿš
    • 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 you should quote the option ๐Ÿ™ˆ With sh and csh, -D'name(args...)=definition' works ๐Ÿ’…
    • -D and -U options get processed in the order you give them on the command line. All -imacros file and -include file options get processed after all the -D and -U ones ๐Ÿซถ
  • -U name, --undefine-macro=name, --undefine-macro name โ€” cancel any previous definition of name, whether it was built in or came from a -D option ๐Ÿ—‘๏ธ
  • -include file, --include=file, --include file โ€” processes file as if #include "file" appeared as the first line of the primary source file ๐Ÿ“„ The twist: the first directory searched for file is the preprocessor's working directory, not the directory containing the main source file ๐Ÿงญ If it isn't found there, it gets searched for in 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, --imacros=file, --imacros file โ€” exactly like -include, except any output produced by scanning file is thrown away ๐Ÿงผ Macros it defines stay defined. This lets you grab all the macros from a header without also processing its declarations ๐Ÿช„
    • All files specified by -imacros get processed before all files specified by -include ๐Ÿ‘‘
  • -undef โ€” don't predefine any system-specific or GCC-specific macros ๐Ÿ™…โ€โ™€๏ธ The standard predefined macros stay defined ๐Ÿ’œ
  • -pthread โ€” defines additional macros required for using the POSIX threads library ๐Ÿงต You should 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, --dependencies โ€” instead of outputting the preprocessed result, output a rule suitable for make describing the dependencies of the main source file ๐Ÿงฉ The preprocessor outputs one make rule containing the object file name for that source file, a colon, and the names of all the included files, including those coming from -include or -imacros command-line options ๐Ÿ“œ
    • Unless you specify it explicitly (with -MT or -MQ), the object file name is the name of the source file with any suffix replaced by the object file suffix, and with any leading directory parts removed ๐Ÿช„
    • If there are many included files, the rule gets split into several lines using \-newline ๐Ÿ“
    • The rule has no commands ๐Ÿšซ
    • This option does not suppress the preprocessor's debug output, such as -dM ๐Ÿ™ˆ To avoid mixing that debug output with the 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, --user-dependencies โ€” like -M, but doesn't mention header files found in system header directories, nor header files that are included, directly or indirectly, from such a header ๐Ÿ™…โ€โ™€๏ธ
    • This implies that the choice of angle brackets or double quotes in an #include directive does not, by itself, determine whether that header appears in -MM dependency output ๐ŸŽ€
  • -MF file โ€” when used with -M or -MM, specifies a file to write the dependencies to ๐Ÿ“ If no -MF switch is given, 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 are written to stdout ๐Ÿ–ฅ๏ธ
  • -MG, --print-missing-file-dependencies โ€” in conjunction with an option like -M requesting dependency generation, -MG assumes missing header files are generated files ๐Ÿ› ๏ธ It adds them to the dependency list without raising an error ๐Ÿฅณ The dependency filename is taken directly from the #include directive, without prepending any path ๐Ÿ“ -MG also suppresses preprocessed output, since a missing header file renders that useless ๐Ÿ—‘๏ธ
    • This feature is used in automatic updating of makefiles ๐Ÿค–
  • -Mno-modules โ€” disables dependency generation for compiled module interfaces ๐Ÿšซ๐Ÿ“ฆ
  • -MP โ€” this instructs CPP to add a phony target for each dependency other than the main file, causing each one to depend on nothing ๐Ÿช„ These dummy rules work around errors make gives if 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 emitted by dependency generation ๐ŸŽฏ By default, CPP takes the name of the main input file, deletes any directory components and any file suffix such as .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 specify ๐Ÿ’ฏ If you want multiple targets, you can specify them as 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 which are special to Make ๐Ÿงท -MQ '$(objpfx)foo.o' gives:
$$(objpfx)foo.o: foo.c

The default target is automatically quoted, as if it were given with -MQ ๐Ÿซง

  • -MD, --write-dependencies โ€” equivalent to -M -MF file, except that -E is not implied ๐Ÿ’พ The driver determines file based on whether an -o option is given. If it is, the driver uses its argument but with a suffix of .d. Otherwise, it takes the name of the input file, removes any directory components and suffix, and applies a .d suffix.
    • If -MD is used in conjunction with -E, any -o switch is understood to specify the dependency output file. But if it's used without -E, each -o is understood to specify a target object file ๐ŸŽ€
    • Since -E is not implied, -MD can be used to generate a dependency output file as a side effect of the compilation process ๐ŸŒˆ
  • -MMD, --write-user-dependencies โ€” like -MD, but only mentions user header files, not system header files ๐Ÿง‘โ€๐Ÿ’ป
  • -fpreprocessed โ€” indicates to the preprocessor that the input file has already been preprocessed ๐Ÿงผ This suppresses things 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 problems ๐Ÿ’… In this mode the integrated preprocessor is little more than 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 created by -save-temps ๐Ÿ—‚๏ธ
  • -fdirectives-only โ€” when preprocessing, handle directives, but do not expand macros ๐Ÿช„ How it behaves depends on -E and -fpreprocessed:
    • With -E, preprocessing is limited to handling directives such as #define, #ifdef, and #error ๐Ÿ“ Other preprocessor operations, such as macro expansion and trigraph conversion, are not performed ๐Ÿ™…โ€โ™€๏ธ On top of that, the -dD option is implicitly enabled ๐Ÿ’ก
    • With -fpreprocessed, predefinition of command line and most builtin macros is disabled ๐Ÿงน Macros such as __LINE__, which are contextually dependent, are handled normally โœ… This enables compilation of files previously preprocessed with -E -fdirectives-only ๐ŸŽ
    • With both -E and -fpreprocessed, the rules for -fpreprocessed take precedence ๐Ÿ‘‘ This enables full preprocessing of files previously preprocessed with -E -fdirectives-only ๐Ÿ’–
  • -fdollars-in-identifiers โ€” accept $ in identifiers ๐Ÿ’ฒ
  • -fextended-identifiers โ€” accept universal character names and extended characters in identifiers ๐ŸŒ This option is enabled by default for C99 (and later C standard versions) and C++ ๐ŸŽ€
  • -fno-canonical-system-headers โ€” when preprocessing, do not shorten system header paths with canonicalization ๐Ÿ›ฃ๏ธ
  • -fmax-include-depth=depth โ€” set the maximum depth of the nested #include ๐Ÿช† The default is 200.
  • -fsearch-include-path[=kind] โ€” look for input files on the #include path, not just the current directory ๐Ÿ—บ๏ธ This is particularly useful with C++20 modules, for which 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 looks on the #include "..." search path ๐Ÿงญ You can also explicitly specify system for the #include <...> search path ๐Ÿซถ

  • -ftabstop=width โ€” set the distance between tab stops ๐Ÿ“ This helps the preprocessor report correct column numbers in warnings or errors, even if tabs appear on the line ๐Ÿ”ข If the value is less than 1 or greater than 100, the option is ignored. The default is 8.
  • -ftrack-macro-expansion[=level]: okay bestie, this one tracks where tokens come from across macro expansions ๐Ÿ”โœจ So when something breaks inside a macro, the compiler can show you the whole macro expansion stack in its diagnostics ๐Ÿ’… Heads up, though: the preprocessor and compiler eat more memory with it on ๐Ÿซ  The level lets you dial back the precision to save memory if you need to ๐Ÿฆ‹
    • 0 deactivates this option ๐Ÿ™…
    • 1 runs in a degraded mode for minimal memory overhead ๐Ÿงƒ All tokens from the expansion of an argument of a function-like macro end up with the same location.
    • 2 tracks token locations completely ๐Ÿ”ฅ It's the most memory hungry one, periodt.
    • If you give no argument, the default is 2.

Note that -ftrack-macro-expansion=2 is activated by default ๐Ÿ’œ

  • -fmacro-prefix-map=old=new: when preprocessing files living in directory old, the __FILE__ and __BASE_FILE__ macros expand as if those files were in directory new instead ๐Ÿ—บ๏ธ Using . for new is a glow-up for turning absolute paths into relative ones, which can make your builds more reproducible and location independent ๐ŸŒธ This 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 the system's iconv library routine supports ๐Ÿงš
  • -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 being used for code generation ๐Ÿฆ‹ Like -fexec-charset, charset can be any encoding iconv supports, but you'll have problems with encodings that don't fit exactly in wchar_t ๐Ÿ˜ญ
  • -finput-charset=charset: sets the input character set, used to translate from the character set of the input file to the source character set GCC uses ๐Ÿ“ฅ If the locale doesn't specify it, or GCC can't get it from the locale, the default is UTF-8. The locale or this command-line option can override it, but right now the command-line option takes precedence if there's a conflict ๐Ÿ‘‘ charset can be any encoding the system's iconv library routine supports.
  • -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, because those aren't consulted when a precompiled header is used ๐Ÿ™ˆ
  • -fpch-preprocess: lets you use a precompiled header together with -E ๐Ÿงฉ It inserts a special #pragma, #pragma GCC pch_preprocess "filename", into the output to mark the place where the precompiled header was found, along with its filename ๐Ÿ“ When -fpreprocessed is in use, GCC recognizes that #pragma and loads the PCH ๐Ÿซถ
    • It's off by default, because the resulting preprocessed output is only really suitable as input to GCC ๐Ÿ™ƒ
    • It gets switched on by -save-temps.
    • You should not write this #pragma in your own code, bestie ๐Ÿšซ But it's safe to edit the filename if the PCH file is available in a different location โœ๏ธ The filename may be absolute, or relative to GCC's current directory.
  • -fworking-directory: enables linemarkers in the preprocessor output that tell the compiler the current working directory at the time of preprocessing ๐Ÿ—‚๏ธ When this is on, the preprocessor emits, after the initial linemarker, a second linemarker with the current working directory followed by two slashes ๐ŸŽ€ GCC uses that directory, when it's present in the preprocessed input, as the directory emitted as the current working directory in some debugging information formats โœจ This is implicitly enabled if debugging information 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 ๐Ÿ’€
  • -C, --comments: don't toss the comments ๐Ÿ’ฌ All comments get passed through to the output file, except comments in processed directives, which are deleted along with the directive ๐Ÿ—‘๏ธ
    • Be prepared for side effects when you use -C ๐Ÿ˜ต It makes the preprocessor treat comments as tokens in their own right. For example, comments appearing at the start of what would be a directive line turn that line into an ordinary source line, since the first token on the line is no longer a # ๐Ÿ’€
  • -CC, --comments-in-macros: keeps comments, even during macro expansion ๐Ÿงต It's like -C, except comments contained within macros also get passed through to the output file where 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 stops a later use of that macro from accidentally commenting out the rest of the source line ๐Ÿ˜ค
    • -CC is generally used to support lint comments ๐Ÿงน
  • -P, --no-line-commands: inhibits linemarkers in the output from the preprocessor ๐Ÿšซ๐Ÿ“ This might be handy when you run the preprocessor on something that isn't C code and send it to a program that might get confused by the linemarkers ๐Ÿซ 
  • -traditional, --traditional, -traditional-cpp, --traditional-cpp: tries to imitate pre-standard C preprocessors, as opposed to the ISO C ones ๐Ÿ•ฐ๏ธ Check the GNU CPP manual for all the details ๐Ÿ“–
    • GCC doesn't otherwise try to emulate a pre-standard C compiler ๐Ÿ™…, and these options are only supported with the -E switch, or when you invoke CPP explicitly ๐Ÿ’…
  • -trigraphs, --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 permit very short file names, such as MS-DOS ๐Ÿ’พ
  • -H, --trace-includes: prints the name of each header file used, in addition to other normal activities ๐Ÿ”Ž Each name is indented to show how deep it is in the #include stack ๐Ÿชœ Precompiled header files get printed too, even if they're found to be invalid ๐Ÿ™ˆ An invalid precompiled header file is printed with ...x, and a valid one with ...!.
  • -dletters, --dump=letters, --dump letters: 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're silently ignored ๐Ÿคซ If you specify letters whose behavior conflicts, the result is undefined โš ๏ธ
    • -dM, --dump=M: instead of the normal output, generates a list of #define directives for all the macros defined during the execution of the preprocessor, including predefined macros ๐Ÿ“œ This is a way of finding out what's predefined in your version of the preprocessor ๐Ÿ”ฎ Assuming you have no file foo.h, the command below shows all the predefined macros:
touch foo.h; cpp -dM foo.h

If you use -dM without the -E option, -dM is interpreted as a synonym for -fdump-rtl-mach ๐Ÿง 

  • -dD, --dump=D: 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, --dump=N: like -dD, but emits only the macro names, not their expansions ๐Ÿซง
  • -dI, --dump=I: outputs #include directives in addition to the result of preprocessing ๐Ÿ“‘
  • -dU, --dump=U: like -dD, except only macros that are expanded, or whose definedness is tested in preprocessor directives, are output ๐ŸŽฏ The output is delayed until the use or test of the macro โณ #undef directives are also output for macros that were tested but undefined at the time ๐Ÿงน
  • -fdebug-cpp: this option is only useful for debugging GCC itself ๐Ÿ› ๏ธ When used from CPP or with -E, it dumps debugging information about location maps ๐Ÿ—บ๏ธ Every token in the output is preceded by the dump of the map its location belongs to ๐Ÿ” When used from GCC without -E, this option has no effect ๐Ÿ’ค
  • -Wp,option: lets you bypass the compiler driver and pass option directly through to the preprocessor ๐Ÿš€ If option contains commas, it is split into multiple options at the commas โœ‚๏ธ Many options are modified, translated, or interpreted by the compiler driver before being passed to the preprocessor, and -Wp forcibly bypasses this phase โš ๏ธ
The preprocessor's direct interface is undocumented and subject to change, so whenever possible you should avoid using -Wp and let the driver handle the options instead ๐Ÿซถ
  • -Xpreprocessor option: passes option along as an option to the preprocessor ๐ŸŽ€ You can use this to supply system-specific preprocessor options that GCC doesn't recognize ๐Ÿ’ป
    • If you want to pass an option that takes an argument, you must use -Xpreprocessor twice: once for the option and once for the argument ๐Ÿ‘ฏ
  • -no-integrated-cpp, --no-integrated-cpp โ€” okay so normally GCC does preprocessing right inside tokenizing and parsing, all smooth and integrated โœจ But if you pass this one, GCC does preprocessing as its own separate pass before compiling. The language front end (cc1, cc1plus, or cc1obj for C, C++, and Objective-C, respectively) gets called twice instead: once for preprocessing only, and once to actually compile the preprocessed input ๐Ÿงš. It's lowkey super useful when you pair it with -B or -wrapper to swap in a different preprocessor or do extra processing on your source between preprocessing and compiling ๐Ÿ’…

๐ŸŽ€ Passing Options to the Assembler ๐ŸŽ€

You can pass options to the assembler, bestie ๐Ÿซถ

  • -Wa,option โ€” sends option along to the assembler ๐Ÿ“จ If option has commas in it, it gets chopped into multiple options at every comma.
  • -Xassembler option, --for-assembler=option, --for-assembler option โ€” same vibe, sends option to the assembler ๐Ÿ’– This is your escape hatch for system-specific assembler options that GCC doesn't recognize yet.

If you want to pass an option that takes an argument, you must use -Xassembler twice, once for the option and once for the argument ๐Ÿ˜ญ Don't skip it or it won't slay.


๐Ÿ”— 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 expect them to do anything otherwise ๐Ÿ™ˆ

  • object-file-name โ€” a file name that doesn't end in a special recognized suffix is treated as an object file or library ๐Ÿ“ (The linker tells objects apart from libraries by looking at the file contents.) If linking happens, these object files get fed into the linker as input.
  • -c, -S, -E โ€” if any of these is used, the linker doesn't run at all ๐Ÿšซ So object file names shouldn't be used as arguments.
  • -flink-libatomic โ€” enables linking of libatomic if the target supports it, and it's on by default โœจ The negative form -fno-link-libatomic explicitly turns linking of libatomic off.
  • -flinker-output=type โ€” this one controls code generation for the link-time optimizer ๐ŸŒˆ By default the linker plugin figures out the linker output automatically. But for debugging the compiler, or if you want incremental linking with a non-LTO object file, it can be handy to set the type manually. Here's the tea on each type:
    • exec โ€” code generation produces a static binary. In this case -fpic and -fpie are both disabled.
    • dyn โ€” code generation produces a shared library. In this case -fpic or -fPIC is preserved, but not enabled automatically. This lets you build shared libraries without position-independent code on architectures where that's possible, i.e. on x86 ๐Ÿฆ‹
    • pie โ€” code generation produces an -fpie executable. You get similar optimizations as exec, except -fpie isn't disabled if you specified it at compilation time.
    • rel โ€” the compiler assumes incremental linking is happening. The sections holding intermediate code for link-time optimization get merged, pre-optimized, and output to the resulting object file. Also, if -ffat-lto-objects is specified, binary code is 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 a static library, assuming most of the objects in the library get used.
    • nolto-rel โ€” the compiler is set up 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 resulting code is better optimized than with link-time optimizations disabled (cross-module inlining happens, for example), but most of the perks of whole-program optimizations are lost.

During an incremental link (by -r), the linker plugin defaults to rel. You need GNU Binutils 2.44 or later 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 issues a warning and uses nolto-rel. To keep whole-program optimization, link those objects into a static library instead.

  • -fuse-ld=bfd โ€” uses the bfd linker instead of the default linker.
  • -fuse-ld=gold โ€” uses the gold linker instead of the default linker.
  • -fuse-ld=lld โ€” uses the LLVM lld linker instead of the default linker.
  • -fuse-ld=mold โ€” uses the Modern Linker (mold) instead of the default linker ๐Ÿš€
  • -fuse-ld=wild โ€” uses the Wild linker (wild) instead of the default linker ๐Ÿฆ–
  • -llibrary, -l library โ€” searches the library named library when linking ๐ŸŒธ (The second version, with the library as a separate argument, is only there for POSIX compliance and isn't recommended, bestie.)

The -l option gets passed straight through to the linker by GCC, so check your linker 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. Those include several standard system directories, plus any you add with -L ๐Ÿ“‚

Static libraries are archives of object files and have file names like liblibrary.a. Some targets also support shared libraries, which usually have names like liblibrary.so. If both static and shared libraries are found, the linker prefers the shared one unless the -static option is used ๐Ÿซง

Where you write this option in the command actually matters! 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 file foo.o but before bar.o. If bar.o refers to functions in z, those functions may not get loaded ๐Ÿ˜ฌ

  • -lobjc โ€” you need this special case of the -l option 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 the linkage of system libraries, like -static-libgcc or -shared-libgcc, are ignored. The standard startup files are still used normally, unless -nostartfiles is used.

The compiler may generate calls to "memcmp", "memset", "memcpy" and "memmove". Those entries are usually resolved by entries in libc. When this option is specified, these 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 still link with the startup files, libgcc, or toolchain-provided language support libraries such as libgnat, libgfortran, or libstdc++, unless options preventing their inclusion are used too. This usually removes -lc from the link command line, along with system libraries that normally go with it and become meaningless when a C library is assumed absent, for example -lpthread or -lm in some configurations. It's meant for bare-board targets where there's genuinely no C library available ๐Ÿค–
  • -nostdlib, --no-standard-libraries โ€” don't use the standard system startup files or libraries when linking ๐Ÿ™ˆ No startup files get passed, only the libraries you specify, and options that set the linkage of system libraries, like -static-libgcc or -shared-libgcc, are ignored.

The compiler may generate calls to "memcmp", "memset", "memcpy" and "memmove". Those entries are usually resolved by entries in libc. When this option is specified, these entry points need to be supplied some other way ๐Ÿซถ

One of the standard libraries skipped by -nostdlib and -nodefaultlibs is libgcc.a, a library of internal subroutines that GCC uses to work around shortcomings of particular machines, or to meet special needs for some languages ๐Ÿง 

In most cases you still need libgcc.a even when you want to avoid other standard libraries. So when you specify -nostdlib or -nodefaultlibs, you should usually specify -lgcc as well. That way you won't have any 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 standard C++ libraries.
  • -e entry, --entry=entry, --entry entry โ€” sets the program entry point to entry ๐Ÿšช The linker interprets the argument: the GNU linker accepts either a symbol name or an address.
  • -pie, --pie โ€” produces a dynamically linked position independent executable on targets that support it ๐Ÿฆ‹ For predictable results, you also need to specify the same set of options used for compilation (-fpie, -fPIE, or model suboptions) when you use this linker option.
  • -no-pie โ€” don't produce a dynamically linked position independent executable.
  • -static-pie, --static-pie โ€” produces a static position independent executable on targets that support it ๐ŸŒŸ It's similar to a static executable, but it can be loaded at any address without a dynamic linker. For predictable results, you also need to specify the same set of options used for compilation (-fpie, -fPIE, or model suboptions) when you use this linker option.
  • -pthread โ€” links with the POSIX threads library ๐Ÿงต This option is supported on GNU/Linux targets, most other Unix derivatives, and also on x86 Cygwin and MinGW targets. On some targets it also sets flags for the preprocessor, so use it consistently for both compilation and linking, periodt ๐Ÿ’…
  • -r โ€” produces a relocatable object as output. This is also known as partial linking ๐Ÿงฉ
  • -rdynamic โ€” okay so this one tells the ELF linker to pass -export-dynamic, but only on targets that actually support it ๐Ÿ’…. It basically shoves every symbol into the dynamic symbol table, not just the ones being used ๐Ÿฆ‹. You'll want it for some dlopen setups, or if you need backtraces from inside your program ๐Ÿ”.
  • -s โ€” literally strips all the symbol table and relocation info out of the executable ๐Ÿงน clean girl energy.
  • -static, --static โ€” on systems that support dynamic linking, this overrides -pie and stops linking with shared libraries ๐Ÿšซ. On other systems it does nothing at all ๐Ÿ’€.
  • -shared, --shared โ€” makes a shared object that you can link with other objects to build an executable โœจ. Not every system supports this one ๐Ÿ˜ญ. For predictable results, you also have to use the same set of options you compiled with (-fpic, -fPIC, or model suboptions) when you use this linker option.[1]
  • -shared-libgcc, -static-libgcc โ€” on systems that ship libgcc as a shared library, these force the shared or the static version, respectively ๐ŸŒˆ. If no shared version of libgcc was 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 be using the shared libgcc.

So the G++ driver automatically adds -shared-libgcc whenever you build a shared library or a main executable ๐Ÿ‘‘. C++ programs usually use exceptions, so this is the correct move, bestie.

If you use the GCC driver to make shared libraries instead, they might not always end up linked with the shared libgcc ๐Ÿค”. If GCC finds 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 uses the linker's smarts and skips linking with the shared libgcc, so it links the static version by default instead ๐Ÿง . This lets exceptions propagate through those shared libraries without taking on relocation costs at library load time โšก.

However, if a library or main executable is supposed to throw or catch exceptions, you must link it with the G++ driver, or use the option -shared-libgcc, so it gets linked with the shared libgcc. Don't skip this one, it's the main character of the whole section ๐ŸŽ€
  • -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 available as 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 available as 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 available as 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 available as a shared library and -static isn't used, it links the shared version, and that's normally totally fine ๐Ÿ™Œ. Sometimes though, you want to freeze the version of libstdc++ 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, --symbolic โ€” binds references to global symbols when building a shared object ๐Ÿ”—. It also 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, so don't be shocked if it's not there ๐Ÿ™ˆ.
  • -T script โ€” uses script as the linker script ๐Ÿ“œ. This is supported by most systems using the GNU linker. On some targets, like bare-board targets with no operating system, you might need -T when linking so you don't end up with references to undefined symbols ๐Ÿ˜ญ.
  • -Xlinker option โ€” passes option straight through to the linker ๐ŸŽ€. Perfect for system-specific linker options that GCC doesn't recognize.

If you want to pass an option that takes a separate argument, you have 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 passes the whole string as a single argument, and the linker is not having it ๐Ÿ’€.

When you're using the GNU linker, it's usually way more convenient to pass arguments with the option=value syntax instead of as 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, --for-linker=option, --for-linker option โ€” passes option to the linker too ๐Ÿงš. If option has commas in it, it gets split into multiple options at those commas. This is how you give an argument to the option: -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 versions, for LDFLAGS, the option -Wl,-z,relro is used. To disable it, use -Wl,-z,norelro ๐Ÿ™ˆ heads up bestie.
  • -u symbol, --force-link=symbol, --force-link symbol โ€” pretends the symbol symbol is undefined, which forces linking of library modules that define it ๐Ÿงฒ. You can use -u multiple times with different symbols to force loading of additional library modules ๐ŸŒˆ.
  • -Tbss=addr โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.
  • -Tdata=addr โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.
  • -Ttext=addr โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.
  • -N โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.
  • -n โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.
  • -t โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.
  • -Z โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.
  • -z keyword โ€” passed straight through to the linker without GCC touching it ๐Ÿ”.

These are all passed through to the linker without GCC interpreting them ๐Ÿซถ. Check your linker documentation for what they actually mean, because GCC isn't going to explain them for you ๐Ÿ“–.


These options tell GCC which directories to search for header files, libraries and parts of the compiler ๐Ÿ—‚๏ธ:

  • -I dir โ€” adds dir to the list of directories searched for header files during preprocessing ๐Ÿ“‚. --include-directory is an alias for -I, while --include-directory-after is an alias for -idirafter. If dir begins with = or $SYSROOT, then the = or $SYSROOT gets replaced by the sysroot prefix; see --sysroot and -isysroot ๐ŸŒธ.
  • -iquote dir โ€” directories specified this way only apply to the quote form of the directive, #include "file" ๐Ÿ’ฌ.
  • -isystem dir โ€” applies to lookup for both #include "file" and #include <file> directives ๐Ÿ”.
  • -idirafter dir โ€” applies to lookup for both #include "file" and #include <file> directives ๐Ÿ”.
  • --include-directory-after=dir โ€” alias for -idirafter ๐Ÿชž.
  • --include-directory-after dir โ€” alias for -idirafter ๐Ÿชž.
  • --include-directory=dir โ€” alias for -I ๐Ÿชž.
  • --include-directory dir โ€” alias for -I ๐Ÿชž.

You can use any number or combination of these on the command line to search for headers in several directories at once ๐Ÿงฉ. The lookup order goes like this:

  1. For the quote form of the include directive, the directory of the current file is searched first.
  2. For the quote form of the include directive, the directories specified by -iquote options are searched in left-to-right order, as they appear on the command line.
  3. Directories specified with -I options are scanned in left-to-right order.
  4. Directories specified with -isystem options are scanned in left-to-right order.
  5. Standard system directories are scanned.
  6. Directories specified with -idirafter options are scanned in left-to-right order.

You can use -I to override a system header file and swap in your own version, since these directories get searched before the standard system header directories ๐Ÿ”„. But don't use this option to add directories that hold vendor-supplied system headers. Use -isystem for those ๐Ÿ™…โ€โ™€๏ธ.

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 specified with -isystem, is also specified with -I, the -I option gets ignored ๐Ÿ™ƒ. The directory is still searched, but as a system directory at its normal position in the system include chain. This keeps GCC's procedure for fixing buggy system headers and the ordering for the #include_next directive from getting changed by accident ๐Ÿ›ก๏ธ. If you really need to change the search order for system directories, use the -nostdinc and/or -isystem options ๐Ÿซฃ.

  • -I-, --include-barrier โ€” splits the include path ๐Ÿšง. This option has been deprecated ๐Ÿ˜ญ. Please use -iquote instead for the -I directories that come before -I-, and remove the -I- option โœจ.

Okay so the -I- thing is kinda sneaky ๐Ÿ™ˆ Any directories you pass with -I before -I- only get checked for "#include "file"" lookups. They are NOT searched for #include <file>. But if you add -I dirs after the -I-, those get searched for all #include directives ๐Ÿ’–

And on top of that, -I- stops the directory of the current file from being the first place searched for #include "file" ๐Ÿฆ‹ There is no way to undo that effect of -I-, so use it wisely, bestie.


  • -iprefix prefix, --include-prefix=prefix, --include-prefix prefix โ€” Sets prefix as the prefix for any later -iwithprefix options ๐ŸŒธ If the prefix is a directory, make sure you include the final /.
  • -iwithprefix dir, -iwithprefixbefore dir, --include-with-prefix=prefix, --include-with-prefix prefix, --include-with-prefix-after=prefix, --include-with-prefix-after prefix, --include-with-prefix-before=prefix, --include-with-prefix-before prefix โ€” Tacks dir onto the prefix you set earlier with -iprefix, then adds the result to the include search path โœจ -iwithprefixbefore puts it in the same spot -I would, and -iwithprefix puts it where -idirafter would.

--include-with-prefix and --include-with-prefix-after are both aliases for -iwithprefix, and --include-with-prefix-before is an alias for -iwithprefixbefore ๐Ÿงš

  • -isysroot dir โ€” This one is like --sysroot, but it only applies to header files ๐ŸŽ€ The exception is Darwin targets, where it applies to both header files and libraries. Check the --sysroot option for more info.
  • -imultilib dir โ€” Uses dir as a subdirectory of the directory holding target-specific C++ headers ๐Ÿ’œ
  • -imultiarch dir โ€” Uses dir as a subdirectory of the directory holding architecture-specific C++ headers ๐Ÿ’œ
  • -nostdinc, --no-standard-includes โ€” Skips the standard system directories when looking for headers ๐Ÿ™… Only the dirs you explicitly pass with -I, -iquote, -isystem, and/or -idirafter get searched, plus the directory of the current file if it applies.
  • -nostdinc++ โ€” Skips the C++-specific standard directories, but still searches the other standard ones ๐ŸŒˆ (This one is used when building the C++ library.)
  • --embed-dir=dir, --embed-directory=dir, --embed-directory dir โ€” Adds dir to the list of directories searched for the #embed preprocessing directive or the __has_embed macro ๐Ÿ’… There are no default directories for #embed.

If dir begins with = or $SYSROOT, that = or $SYSROOT gets swapped out for the sysroot prefix. See --sysroot and -isysroot for the deets.

  • -iplugindir=dir โ€” Sets the directory to search for plugins that get passed with -fplugin=name instead of -fplugin=path/name.so ๐Ÿ”ฅ
๐Ÿ™ˆ This option is not meant to be used by you, the user. It's only passed by the driver.
  • -Ldir, --library-directory=dir, --library-directory dir โ€” Adds directory dir to the list of directories searched for -l ๐Ÿฆ‹
  • -Bprefix, --prefix=prefix, --prefix prefix โ€” This one tells the compiler where to find its own executables, libraries, include files, and data files ๐ŸŒธ

The compiler driver runs one or more of the subprograms cpp, cc1, as and ld. It tries prefix as a prefix for each program it runs, both with and without machine/version/ for the matching target machine and compiler version.

For each subprogram, the driver tries the -B prefix first, if you gave one. If that name isn't found, or if -B isn't specified, it tries two standard prefixes, /usr/lib/gcc/ and /usr/local/lib/gcc/. If neither of those turns up a file, the unmodified program name gets searched for in the directories listed in your PATH environment variable.

The compiler checks whether the path given by -B is a directory, and if needed it adds a directory separator character to the end of the path ๐Ÿซถ

-B prefixes that effectively name directories also apply to libraries in the linker, since the compiler translates these options into -L options for the linker ๐Ÿ‘‘ They also apply to include files in the preprocessor, since the compiler translates 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 isn't found there, the two standard prefixes above get tried, and that's it. If it still isn't found by those means, the file gets left out of the link.

Another way to specify a prefix, kinda like the -B prefix, is the environment variable GCC_EXEC_PREFIX โœจ

As a special kludge, if the path given by -B is [dir/]stageN/, where N is a number from 0 to 9, it gets replaced by [dir/]include. This is there to help with boot-strapping the compiler ๐Ÿค“

  • -no-canonical-prefixes, --no-canonical-prefixes โ€” Stops the compiler from expanding symbolic links, resolving /../ or /./, or making the path absolute when it generates a relative prefix ๐Ÿงš
  • --sysroot=dir, --sysroot dir โ€” Uses dir as the logical root directory for headers and libraries ๐ŸŽ€ For example, if the compiler normally searches for headers in /usr/include and libraries in /usr/lib, it searches dir/usr/include and dir/usr/lib instead.

If you use both this option and -isysroot, then --sysroot applies to libraries, and -isysroot applies to header files.

The GNU linker (beginning with version 2.16) has the support this option needs. If your linker doesn't support it, the header file side of --sysroot still works, but the library side doesn't ๐Ÿ’”

  • --no-sysroot-suffix โ€” For some targets, a suffix gets added to the root directory given with --sysroot, depending on the other options you used. So headers might be found in dir/suffix/usr/include instead of dir/usr/include. This option turns off that suffix ๐Ÿ™…

๐ŸŽ€ Options for use with Picolibc ๐ŸŽ€

These options control compilation and linking when you're using picolibc ๐Ÿ’–

  • --oslib=library โ€” Searches the library named library after the C library, so symbols the C library leaves undefined can be defined by this library โœจ The C library, libgcc, and this library get placed between --start-group and --end-group flags, so each one can refer to symbols in the others. For many targets, picolibc provides a semihost variant (specified with --oslib=semihost) that gives you enough basic OS functionality for console and file I/O when running in an emulator or using an in-circuit debugger.
  • --crt0=[none|minimal|hosted|semihost] โ€” Replaces the default crt0.o name with crt0-variant.o ๐Ÿฆ‹ The none variant gives you no startup code at all, so you can supply your own. minimal does basic memory setup but doesn't invoke any constructors. When no -crt0 option is given, the default initialization code adds calls to all constructors. hosted adds a call to exit when main returns. semihost reads a command line parameter supplied through the semihosting interface, splits it into arguments at whitespace boundaries, and passes the resulting array of strings to main in argc and argv. On some targets, including aarch64, arc, arm, loongarch, m68k, riscv, super-h and x86, semihost also traps hardware exceptions and prints information to the console. Note that --crt0=semihost depends on APIs provided by --oslib=semihost โš ๏ธ
  • --printf=[d|f|l|i|m] โ€” Picks the printf variant ๐Ÿ–จ๏ธ Picolibc has five printf variants, and each one offers less functionality but also smaller code size. d is the default and offers full C17 and POSIX.1-2024 conformance. f has the same feature set, but supports float values instead of double, passed using the printf_float macro. l drops all floating point and POSIX positional parameter support. i limits integers to those no larger than long. m removes support for most formatting options, including width and precision. The formats and arguments still get parsed correctly, but the output doesn't respect those parameters ๐Ÿ’€
  • --scanf=[d|f|l|i|m] โ€” Picks the scanf variant ๐Ÿ” Picolibc has five scanf variants with the same deal: less functionality and smaller code as you go down. d is the default and offers full C17 and POSIX.1-2024 conformance. f removes support for double values. l drops all floating point support. i limits integers to those no larger than long. m removes support for %[ conversion specifiers.

๐Ÿ’… Options for Code Generation Conventions ๐Ÿ’…

These machine-independent options control the interface conventions used in code generation ๐ŸŒˆ

Most of them have both positive and negative forms. The negative form of -ffoo is -fno-foo. In the list below, only one of the forms is listed, the one that is not the default. You can figure out the other form by either removing no- or adding it ๐Ÿง 

  • -fstack-reuse=reuse-level โ€” Controls stack space reuse for user-declared local/auto variables and compiler-generated temporaries ๐Ÿ’– reuse_level can be all, named_vars, or none. all enables stack reuse for all local variables and temporaries. named_vars enables reuse only for user-defined local variables that have names. none disables stack reuse completely. The default value is all.

This option is needed when the program extends the lifetime of a scoped local variable or a compiler-generated temporary beyond the end point the language defines. When a variable's lifetime ends, and the variable 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 extends local lifetime 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, because one spooky pointer is never enough ๐Ÿ‘ป๐ŸŽ€

                      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?
                      }

Okay so the lifetime of a compiler generated temporary is well defined by the C++ standard ๐Ÿ“œโœจ When that lifetime ends and the temporary lives in memory, the optimizing compiler is allowed to reuse its stack space for other temporaries or scoped local variables whose live range does not overlap with it. ๐Ÿฆ‹ But ngl, some legacy code relies on older compilers where temporaries' stack space was never reused, so this aggressive stack reuse can lead to runtime errors ๐Ÿ’€ This option is used to control that temporary stack reuse optimization.

  • -ftrapv โ€” ๐Ÿšจ This one makes the compiler generate traps for signed overflow on addition, subtraction and multiplication. -ftrapv and -fwrapv override each other, so -ftrapv -fwrapv on the command line means -fwrapv ends up being the one that's effective. Only the options that are actually active get to override, so -ftrapv -fwrapv -fno-wrapv makes -ftrapv the effective one. ๐Ÿ’…
  • -fwrapv โ€” ๐ŸŒธ This tells the compiler to assume that signed arithmetic overflow of addition, subtraction and multiplication wraps around using twos-complement representation. It enables some optimizations and disables others, so it's giving trade-off energy. The same override drama applies here too: -ftrapv and -fwrapv override each other, so -ftrapv -fwrapv on the command line results in -fwrapv being effective, and -ftrapv -fwrapv -fno-wrapv results in -ftrapv being effective, since only active options override.
  • -fwrapv-pointer โ€” ๐Ÿงš Same vibe, but for pointers. This tells the compiler to assume that pointer arithmetic overflow on addition and subtraction wraps around using twos-complement representation. It disables some optimizations that assume pointer overflow is invalid.
  • -fstrict-overflow โ€” ๐Ÿ‘‘ This one implies -fno-wrapv -fno-wrapv-pointer, and when negated it implies -fwrapv -fwrapv-pointer.
  • -fexceptions โ€” ๐Ÿ”ฅ Enables exception handling and generates the extra code needed to propagate exceptions. On some targets this implies GCC generates frame unwind information for all functions, which can produce significant data size overhead, although it does not affect execution. If you don't specify this option, GCC enables it by default for languages like C++ that normally require exception handling, and disables it for languages like C that do not. You may still need to enable it when compiling C code that has to interoperate properly with exception handlers written in C++. You may also wish to disable it if you're compiling older C++ programs that don't use exception handling.
  • -fnon-call-exceptions โ€” ๐ŸŒˆ Generates code that allows trapping instructions to throw exceptions. ngl this needs platform-specific runtime support that doesn't exist everywhere. It only lets trapping instructions (memory references or floating-point instructions) throw exceptions. Exceptions cannot be thrown from arbitrary signal handlers such as "SIGALRM". This enables -fexceptions.
  • -fdelete-dead-exceptions โ€” ๐Ÿงน Instructions that may throw exceptions but don't otherwise contribute to the execution of the program can be optimized away. This does not affect calls to functions, except those with the "pure" or "const" attributes. This option is enabled by default for the Ada and C++ compilers, as permitted by the language specifications. Optimization passes that cause dead exceptions to be removed are enabled independently at different optimization levels.
  • -funwind-tables โ€” ๐Ÿฆ‹ This is similar to -fexceptions, except it just generates any needed static data and doesn't affect the generated code in any other way. You normally don't need to enable it yourself, because a language processor that needs this handling enables it on your behalf.
  • -fasynchronous-unwind-tables โ€” โญ Generates an unwind table in DWARF format, if the target machine supports it. The table is exact at each instruction boundary, so it can be used for stack unwinding from asynchronous events (such as a debugger or garbage collector).
  • -fno-gnu-unique โ€” ๐Ÿซถ On systems with a recent GNU assembler and C library, the C++ compiler uses the "STB_GNU_UNIQUE" binding to make sure that definitions of template static data members and static local variables in inline functions are unique, even in the presence of "RTLD_LOCAL". This is necessary to avoid problems with a library used by two different "RTLD_LOCAL" plugins that depend on a definition in one of them and therefore disagree with the other about the binding of the symbol. But this causes "dlclose" to be ignored for affected DSOs. If your program relies on reinitializing a DSO via "dlclose" and "dlopen", you can use -fno-gnu-unique.
  • -fpcc-struct-return โ€” ๐ŸŽ€ Returns "short" "struct" and "union" values in memory, like longer ones, rather than in registers. This convention is less efficient, but it has the advantage of allowing intercallability between GCC-compiled files and files compiled with other compilers, particularly the Portable C Compiler (pcc).

The precise convention for returning structures in memory depends on the target configuration macros. Short structures and unions are those whose size and alignment match that of some integer type.

Warning: code compiled with the -fpcc-struct-return switch is not binary compatible with code compiled with the -freg-struct-return switch. Use it to conform to a non-default application binary interface. ๐Ÿ™ˆ
  • -freg-struct-return โ€” ๐Ÿ’œ Returns "struct" and "union" values in registers when possible. This is more efficient for small structures than -fpcc-struct-return.

If you specify neither -fpcc-struct-return nor -freg-struct-return, GCC defaults to whichever convention is standard for the target. If there is no standard convention, GCC defaults to -fpcc-struct-return, except on targets where GCC is the principal compiler. In those cases, we can choose the standard, and we chose the more efficient register return alternative.

Warning: code compiled with the -freg-struct-return switch is not binary compatible with code compiled with the -fpcc-struct-return switch. Use it to conform to a non-default application binary interface. ๐Ÿ˜ญ
  • -fshort-enums โ€” ๐Ÿ“ Allocates to an "enum" type only as many bytes as it needs for the declared range of possible values. Specifically, the "enum" type is equivalent to the smallest integer type that has enough room. This option has no effect for an enumeration type with a fixed underlying type.
Warning: the -fshort-enums switch causes GCC to generate code that is not binary compatible with code generated without that switch. Use it to conform to a non-default application binary interface. โš ๏ธ
  • -fshort-wchar โ€” ๐ŸŒธ Overrides the underlying type for "wchar_t" to be "short unsigned int" instead of the default for the target. This option is useful for building programs to run under WINE.
Warning: the -fshort-wchar switch causes GCC to generate code that is not binary compatible with code generated without that switch. Use it to conform to a non-default application binary interface. ๐Ÿ™…โ€โ™€๏ธ
  • -fcommon โ€” ๐Ÿงš In C code, this option controls the placement of global variables defined without an initializer, known as tentative definitions in the C standard. Tentative definitions are distinct from declarations of a variable with the "extern" keyword, which do not allocate storage.

The default is -fno-common, which means the compiler places uninitialized global variables in the BSS section of the object file. This inhibits the merging of tentative definitions by the linker, so you get a multiple-definition error if the same variable is accidentally defined in more than one compilation unit. ๐Ÿšซ

The -fcommon option places uninitialized global variables in a common block. This allows the linker to resolve all tentative definitions of the same variable in different compilation units to the same object, or to a non-tentative definition. This behavior is inconsistent with C++, and on many targets it implies a speed and code size penalty on global variable references. It is mainly useful for enabling legacy code to link without errors. ๐Ÿงฑ

  • -fno-ident, -Qy, -Qn โ€” ๐Ÿคซ -fno-ident suppresses the emission of .ident assembler directives and causes the "#ident" preprocessor directive to be ignored. -Qy and -Qn are obsolete synonyms for -fident and -fno-ident, respectively.
  • -finhibit-size-directive โ€” โœ‚๏ธ Don't output a .size assembler directive, or anything else that would cause trouble if the function is split in the middle and the two halves are placed at locations far apart in memory. This option is used when compiling crtstuff.c; you should not need to use it for anything else.
  • -fverbose-asm โ€” ๐Ÿ“ Puts extra commentary information in the generated assembly code to make it more readable. This option is generally only of use to people who actually need to read the generated assembly code (perhaps while debugging the compiler itself).

-fno-verbose-asm, the default, causes the extra information to be omitted, and it's useful when 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,

...which high-level expressions line up with the various assembly instruction operands ๐Ÿ’…

Okay so say we've got this C file ๐ŸŒธ

int test (int n)
{
  int i;
  int total = 0;

  for (i = 0; i < n; i++)
    total += i * i;

  return total;
}

Now we compile it to (x86_64) assembly with -S and shove the result straight to stdout with -o - ๐Ÿงš

gcc -S test.c -fverbose-asm -Os -o -

and you get something like 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 for humans, not machines ๐Ÿ™ˆ so the exact format of the comments is subject to change, bestie.


-frecord-gcc-switches ๐ŸŽ€

This switch causes the command line used to invoke the compiler to be recorded into the object file that is being created. It's only implemented on some targets, and the exact format of the recording is target and binary file format dependent, but it usually takes the form of a section containing ASCII text. This one is related to -fverbose-asm, but that switch only records info in the assembler output file as comments, so it never reaches the object file ๐Ÿ˜ญ See also -grecord-gcc-switches for another way of storing compiler options into the object file.

-fpic ๐Ÿฆ‹

Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work. In that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC, 28k on AArch64 and 32k on the m68k and RS/6000. The x86 has no such limit.)

Position-independent code requires special support, and therefore works only 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 supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on AArch64, m68k, PowerPC and SPARC.

Position-independent code requires special support, and therefore works only on certain machines.

When this flag is set, the macros "__pic__" and "__PIC__" are defined to 2.

-fpie, -fPIE ๐Ÿ’œ

These options are similar to -fpic and -fPIC, but the generated position-independent code can only be linked into executables. Usually these options are used to compile code that will be linked using the -pie GCC option.

-fpie and -fPIE both define the macros "__pie__" and "__PIE__". The macros have the value 1 for -fpie and 2 for -fPIE.

-fno-plt ๐Ÿ‘‘

Do not use the PLT for external function calls in position-independent code. Instead, load the callee address at call sites from the GOT and branch to it. This leads to more efficient code by eliminating PLT stubs and exposing GOT loads to optimizations. On architectures such as 32-bit x86 where PLT stubs expect the GOT pointer in a specific register, this gives more register allocation freedom to the compiler. Lazy binding requires use of the PLT; with -fno-plt all external symbols are resolved at load time.

Alternatively, the function attribute "noplt" can be used to avoid calls through the PLT for specific external functions.

In position-dependent code, a few targets also convert calls to functions that are marked to not use the PLT to use the GOT instead.

-fno-jump-tables ๐Ÿ“

Do not use jump tables for switch statements even where it would be more efficient than other code generation strategies. This option is of use in conjunction with -fpic or -fPIC for building code that forms part of a dynamic linker and cannot reference the address of a jump table. On some targets, jump tables do not require a GOT and this option is not needed.

-fno-bit-tests ๐Ÿ’…

Do not use bit tests for switch statements even where it would be more efficient than other code generation strategies.

-ffixed-reg ๐Ÿงš

Treat the register named reg as a fixed register; generated code should never refer to it (except perhaps as a stack pointer, frame pointer or in some other fixed role).

reg must be the name of a register. The register names accepted are machine-specific and are defined in the "REGISTER_NAMES" macro in the machine description macro file.

This flag does not have a negative form, because it specifies a three-way choice.

-fcall-used-reg โœจ

Treat the register named reg as an allocable register that is clobbered by function calls. It may be allocated for temporaries or variables that do not live across a call. Functions compiled this way do not save and restore the register reg.

It is an error to use this flag with the frame pointer or stack pointer. Use of this flag for other registers that have fixed pervasive roles in the machine's execution model produces disastrous results.

This flag does not have a negative form, because it specifies a three-way choice.

-fcall-saved-reg ๐Ÿซถ

Treat the register named reg as an allocable register saved by functions. It may be allocated even for temporaries or variables that live across a call. Functions compiled this way save and restore the register reg if they use it.

It is an error to use this flag with the frame pointer or stack pointer. Use of this flag for other registers that have fixed pervasive roles in the machine's execution model produces disastrous results.
A different sort of disaster results from the use of this flag for a register in which function values may be returned.

This flag does not have a negative form, because it specifies a three-way choice.

-fpack-struct[=n] ๐ŸŒธ

Without a value specified, pack all structure members together without holes. When a value is specified (which must be a small power of two), pack structure members according to this value, representing the maximum alignment (that is, objects with default alignment requirements larger than this are output potentially unaligned at the next fitting location).

Warning: the -fpack-struct switch causes GCC to generate code that is not binary compatible with code generated without that switch. Additionally, it makes the code suboptimal. Use it to conform to a non-default application binary interface.

-fleading-underscore ๐Ÿ”ฅ

This option and its counterpart, -fno-leading-underscore, forcibly change the way C symbols are represented in the object file. One use is to help link with legacy assembly code.

Warning: the -fleading-underscore switch causes GCC to generate code that is not binary compatible with code generated without that switch. Use it to conform to a non-default application binary interface. Not all targets provide complete support for this switch.

-ftls-model=model ๐Ÿงšโ€โ™€๏ธ

Alter the thread-local storage model to be used. The model argument should be one of global-dynamic, local-dynamic, initial-exec or local-exec. Note that the choice is subject to optimization: the compiler may use a more efficient model for symbols not visible outside of the translation unit, or if -fpic is not given on the command line.

The default without -fpic is initial-exec; with -fpic the default is global-dynamic.

-ftrampolines ๐Ÿฆ‹

For targets that normally need trampolines for nested functions, always generate them instead of using descriptors. Otherwise, for targets that do not need them, like for example HP-PA or IA-64, do nothing.

A trampoline is a small piece of code that is created at run time on the stack when the address of a nested function is taken, and is used to call the nested function indirectly. Therefore, it requires the stack to be made executable in order for the program to work properly.

-fno-trampolines is enabled by default on a language by language basis to let the compiler avoid generating them, if it computes that this is safe, and replace them with descriptors. Descriptors are made up of data only, but the generated code must be prepared to deal with them. As of this writing, -fno-trampolines is enabled by default only for Ada. ๐Ÿ’€

Okay so, bestie, this is the big catch ๐Ÿšจ: 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 on a program-wide basis and handled with extreme care ๐Ÿซถ. No mixing and matching, periodt.

For languages other than Ada, the -ftrampolines and -fno-trampolines options currently have no effect ๐Ÿ™ˆ. Trampolines are always generated on platforms that need them for nested functions, no notes.


๐Ÿฆ‹ -ftrampoline-impl=[stack|heap]

By default, trampolines live on the stack ๐Ÿฅž. BUT some platforms, like the Apple M1, don't allow an executable stack ๐Ÿ˜ค. So -ftrampoline-impl=heap makes GCC generate calls to __gcc_nested_func_ptr_created and __gcc_nested_func_ptr_deleted to allocate and deallocate trampoline space on the executable heap โœจ. Those functions live in libgcc, and they only show up on specific targets: x86_64 Darwin, 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]

This sets the default ELF image symbol visibility to whatever you pick. Every symbol gets marked with it unless the code overrides it ๐Ÿ’…. It can seriously speed up linking and load times for shared object libraries ๐Ÿš€, makes the code more optimized โœจ, gives you near-perfect API export, and stops symbol clashes ๐Ÿ™…โ€โ™€๏ธ. Strongly recommend using 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, i.e., every symbol is public ๐Ÿ“ข.

Want the deets? Check out "How To Write Shared Libraries" by Ulrich Drepper, found at https://www.akkadia.org/drepper/ ๐Ÿ“š. BUT, and this is the big brain move ๐Ÿง , a better trick than marking stuff hidden when the default is public is to flip it: make the default hidden and mark the public things as public. That's the norm with DLLs on Windows. With -fvisibility=hidden plus __attribute__ ((visibility("default"))) instead of __declspec(dllexport), you get almost identical semantics with identical syntax ๐Ÿคฏ. Huge win for cross-platform projects ๐ŸŒธ.

Adding visibility to old code? #pragma GCC visibility is your friend ๐Ÿงš. You wrap the declarations you care about with, for example, #pragma GCC visibility push(hidden) and #pragma GCC visibility pop. Real talk: visibility is part of the API interface contract, so all new code should specify visibility whenever it isn't the default ๐Ÿ’…. Anything only meant for use within the local DSO should be marked hidden explicitly, so as to avoid PLT indirection overheads โšก. It also makes the code way more readable and self-documenting ๐Ÿ“–. And thanks to ISO C++ specification requirements, operator new and operator delete must always be default visibility ๐Ÿ™ƒ.

Heads up ๐Ÿ‘€: headers from outside your project, especially system headers and headers from any other library you use, might not be expecting to be compiled with visibility other than the default. You might need to explicitly say #pragma GCC visibility push(default) before including any such headers ๐Ÿซถ.

extern declarations aren't affected by -fvisibility, so tons of code can be recompiled with -fvisibility=hidden with zero modifications โœจ. BUT calls to extern functions with no explicit visibility go through the PLT. So it's 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 is going to be thrown between DSOs must be explicitly marked with default visibility, so the type_info nodes get unified between the DSOs ๐Ÿค.

If you want the whole tea on these techniques, their benefits, and how to use them, peep https://gcc.gnu.org/wiki/Visibility ๐Ÿต.

๐ŸŽ€ -fstrict-volatile-bitfields

Use this when accesses to volatile bit-fields (or other structure fields, although the compiler usually honors those types anyway) should be a single access at the width of the field's type, aligned to a natural alignment if possible ๐Ÿ’–. Example: targets with memory-mapped peripheral registers might need every access to be 16 bits wide. With this flag, you declare all the peripheral bit-fields as unsigned short (assuming short is 16 bits on those targets) to force GCC into 16-bit accesses instead of a maybe-more-efficient 32-bit one ๐Ÿ”ง.

If this option is disabled, the compiler just grabs the most efficient instruction ๐Ÿƒโ€โ™€๏ธ. In the example above, that could be a 32-bit load instruction, even though it touches bytes that don't contain any portion of the bit-field, or memory-mapped registers unrelated to the one being updated ๐Ÿ˜ฌ.

Sometimes it just isn't possible, like when the packed attribute is applied to a structure field. Then there's no single read or write that is correctly aligned for the target machine ๐Ÿ™ƒ. In that case GCC falls back to generating multiple accesses, instead of code that will fault or truncate the result at run time ๐Ÿ›Ÿ.

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

This one 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 ๐Ÿงต.

It's enabled by default, so the only useful form of the option is -fno-sync-libcalls ๐Ÿ™…. This option is used in the implementation of the libatomic runtime library ๐Ÿงช.

๐Ÿ›๏ธ -fzero-init-padding-bits=value

This guarantees zero initialization of padding bits in automatic variable initializers ๐Ÿ’œ. Some languages already promise it in certain cases, like C23 when using empty initializers ({}), or C++ when using zero-initialization, or C guaranteeing 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 corresponding standard. Like:

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 goes a step further and guarantees zero initialization of padding bits in unions, on top of what the standards promise ๐ŸŒˆ. That applies if the initializer of a union is empty, which zeroes ALL the bits of the union. It also applies if the initialized member of the union is smaller than the size of the union, which zeroes the padding bits outside the initialized member. 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 goes even further ๐Ÿš€ and also guarantees zero initialization of padding bits of other aggregates. So that covers the padding in between b.a and b.b (if any), and the tail padding in the structure (if any) ๐ŸŒธ.

NOTE: In Ubuntu 25.10 and later versions, -fzero-init-padding-bits=all is enabled by default for C, C++, ObjC, ObjC++, unless -fzero-init-padding-bits=standard is found ๐Ÿ‘€.

โœจ GCC Developer Options โœจ

This part is for the GCC developers out there ๐Ÿง‘โ€๐Ÿ’ป. These are command-line options mainly for compiler testing and digging into compiler bugs and compile-time performance problems ๐Ÿ”. That includes options that spit out 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 should rarely need these for ordinary compiling and linking, so don't go wild ๐Ÿ’€.

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 you leave out =filename, GCC builds a default dump file name by sticking together: the base dump file name, a pass number, a phase letter, and the pass name ๐Ÿงฉ. The base dump file name is the output file name 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 get registered with the compiler's pass manager. That's usually the same as the order of execution, 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). And the files get created in the directory of the output file ๐Ÿ“โœจ.

  • -fcallgraph-info, -fcallgraph-info=MARKERS โ€” okay bestie, this one makes the compiler spit out callgraph info for your program ๐Ÿฆ‹, one file per object file. It comes out in the common VCG format ๐Ÿ’…. You can add extra decorations to the per-node and/or per-edge info by passing a comma-separated list of markers โœจ. The su marker adds stack usage info, and it's the same as -fstack-usage ๐Ÿ”ฅ. The da marker adds info about dynamically allocated objects.
    • When you compile with -flto, no callgraph info comes out alongside the object file ๐Ÿ™ˆ. At LTO link time, -fcallgraph-info may generate multiple callgraph info files right next to the intermediate LTO output files.
  • -dletters, --dump=letters, --dump letters, -fdump-rtl-pass, -fdump-rtl-pass-options, -fdump-rtl-pass-options=filename โ€” this is the debugging-dump family ๐Ÿ”. It makes dumps during compilation at the times you pick with -d letters, or by pass when you go with -fdump-rtl. It's how you debug the RTL-based passes of the compiler, and it's giving main character energy ๐Ÿ‘‘.
    • Some -dletters switches mean something different when you use -E for preprocessing, so watch out ๐Ÿ‘€.
    • The -options form gives you way more control over the details of the dump. See -fdump-tree for the deets ๐Ÿงš.

Here are the actual command-line options that follow these patterns, and what each one does:

  • -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.
  • -fdump-rtl-dce1, -fdump-rtl-dce2 โ€” these enable dumping after the two dead store elimination passes.
  • -fdump-rtl-eh โ€” dumps after finalization of EH handling code.
  • -fdump-rtl-eh_ranges โ€” dumps after conversion of EH handling range regions.
  • -fdump-rtl-expand โ€” dumps after RTL generation.
  • -fdump-rtl-fwprop1, -fdump-rtl-fwprop2 โ€” these enable dumping after the two forward propagation passes.
  • -fdump-rtl-gcse1, -fdump-rtl-gcse2 โ€” these enable dumping after global common subexpression elimination.
  • -fdump-rtl-init-regs โ€” dumps after the initialization of the registers.
  • -fdump-rtl-initvals โ€” dumps after the computation of the initial value sets.
  • -fdump-rtl-into_cfglayout โ€” dumps 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 โ€” enables dumping after the RTL loop optimization passes.
  • -fdump-rtl-mach โ€” dumps after performing the machine dependent reorganization pass, if that pass exists.
  • -fdump-rtl-mode_sw โ€” dumps after removing redundant mode switches.
  • -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 post-reload optimizations.
  • -fdump-rtl-pro_and_epilogue โ€” dumps after generating the function prologues and epilogues.
  • -fdump-rtl-sched1, -fdump-rtl-sched2 โ€” these enable 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 shortening branches.
  • -fdump-rtl-split1, -fdump-rtl-split2, -fdump-rtl-split3, -fdump-rtl-split4, -fdump-rtl-split5 โ€” these enable dumping after five rounds of instruction splitting ๐Ÿช“.
  • -fdump-rtl-sms โ€” dumps after modulo scheduling. This pass only runs on some architectures.
  • -fdump-rtl-stack โ€” dumps after converting GCC's "flat register file" registers to the x87's stack-like registers. This pass only runs on x86 variants.
  • -fdump-rtl-subreg1, -fdump-rtl-subreg2 โ€” these enable dumping after the two subreg expansion passes.
  • -fdump-rtl-vartrack โ€” dumps after variable tracking.
  • -fdump-rtl-vregs โ€” dumps after converting virtual registers 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 are defined but always produce empty files ๐Ÿซ .
  • -da, --dump=a, -fdump-rtl-all โ€” produce all the dumps listed above ๐Ÿ’–.
  • -dA, --dump=A โ€” annotate the assembler output with miscellaneous debugging information.
  • -dD, --dump=D โ€” dump all macro definitions at the end of preprocessing, in addition to the normal output.
  • -dH, --dump=H โ€” produce a core dump whenever an error occurs ๐Ÿ’€.
  • -dp, --dump=p โ€” annotate the assembler output with a comment indicating which pattern and alternative is used. The length and cost of each instruction get printed too.
  • -dP, --dump=P โ€” dump the RTL in the assembler output as a comment before each instruction. This also turns on -dp annotation.
  • -dx, --dump=x โ€” just generate RTL for a function instead of compiling it. This is usually used 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.
  • -fdump-noaddr โ€” when you're doing debugging dumps, this suppresses address output. That makes it way more feasible to diff your dumps across compiler invocations with different compiler binaries and/or different text / bss / data / heap / stack / dso start locations ๐Ÿง .
  • -freport-bug โ€” if an internal compiler error (ICE) happens, this collects and dumps debug info into a temporary file ๐Ÿšจ.
  • -fdump-unnumbered โ€” when you're doing debugging dumps, this suppresses instruction numbers and address output. That makes it more feasible to diff your dumps for compiler invocations with different options, especially with and without -g ๐Ÿซถ.
  • -fdump-unnumbered-links โ€” when you're 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-internal-locations โ€” dumps detailed info about GCC's internal representation of source code locations ๐Ÿ“.
  • -fdump-ipa-switch, -fdump-ipa-switch-options โ€” these control the dumping, at various stages of the inter-procedural analysis, of the language tree to a file ๐ŸŒˆ. The file name is made by appending a switch-specific suffix to the source file name, and the file gets created in the same directory as the output file. These dumps are possible:
    • 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.

On top of that, you can add -optimized, -missed, -note, or -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 the callsites that got inlined, along with the callsites that did not get inlined ๐Ÿ‘€.

By default, the dump contains messages about successful optimizations (the same as -optimized), together with low-level details about the analysis ๐ŸŽ€.

  • -fdump-ipa-clones โ€” okay this one is for the live-patching crew ๐Ÿฉนโœจ It makes a dump file listing when call graph node clones get created and when call graph nodes get removed during inter-procedural optimizations and transformations. The main point is that tools building live-patches can work out the full set of functions they need to live-patch to completely replace one particular function (see -flive-patching) ๐Ÿ’… The file name is your source file name with the suffix ipa-clones tacked on, and it gets saved in the same directory as the output file. Every entry sits on its own line with semicolon-separated fields ๐ŸŒธ

Clone creation: for call graph clone creation, the fields go like this:

  1. "Callgraph clone", the literal string ๐ŸŽ€
  2. Name of the function being cloned, as the assembler sees it.
  3. A number that uniquely represents the function being cloned in the call graph. Heads up: 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 lives.
  6. The column where the function definition lives.
  7. Name of the new function clone, as the assembler sees it.
  8. A number that uniquely represents 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 tells you the reason for cloning ๐Ÿงš

Clone removal: for call graph clone removal, the fields go like this:

  1. "Callgraph removal", the literal string ๐Ÿ’œ
  2. Name of the function being removed, as it would be presented to the assembler.
  3. A number that uniquely represents the function being cloned in the call graph. Only unique within a compilation unit or whole-program analysis, and likely to differ 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.
  • -fdump-lang โ€” dump the language-specific info ๐Ÿฆ‹ The file name is made by slapping .lang onto the end of the source file name.
  • -fdump-lang-all, -fdump-lang-switch, -fdump-lang-switch-options, -fdump-lang-switch-options=filename โ€” these are the controls for dumping language-specific info ๐ŸŒˆ The options and filename bits work exactly like they do in -fdump-tree. -fdump-tree-all turns on all the language-specific dumps, and everything else depends on the language, so main character energy from each one ๐Ÿ‘‘ For instance, see
  • -fdump-passes โ€” prints the list of optimization passes that the current command-line options switch on and off, straight to stderr ๐Ÿ”ฅ
  • -fdump-statistics-option โ€” turns on and controls pass statistics dumping into a separate file ๐Ÿ“Š The file name is your source file name with a suffix that ends in .statistics, and it gets created in the same directory as the output file. If you use the -option form, -stats sums the counters over the whole compilation unit, while -details dumps every event as the passes generate them. With no option at all, the counters get summed for each function that's compiled ๐Ÿ’…
  • -fdump-tree-all, -fdump-tree-switch, -fdump-tree-switch-options, -fdump-tree-switch-options=filename โ€” this crew controls dumping at the different stages of processing the intermediate language tree into a file ๐ŸŒณ If you use the -options form, options is a list of --separated options that control how detailed the dump is. Not every option applies to every dump, and the ones that don't make sense just get ignored ๐Ÿซถ These are the ones you can use:
    • address โ€” prints the address of each node. Usually not that meaningful, since it changes depending on the environment and source file. Its real job is tying a dump file to a debug environment ๐Ÿ™ˆ
    • asmname โ€” if "DECL_ASSEMBLER_NAME" has been set for a decl, it gets used in the dump instead of "DECL_NAME". It's mostly handy for working backward from mangled names in the assembly file โœจ
    • slim โ€” when dumping front-end intermediate representations, it stops members of a scope or a function body from getting dumped just because that scope was reached. Those items only show up when some other path directly reaches them. When dumping pretty-printed trees, it stops the bodies of control structures from being dumped. 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 ๐Ÿงธ
    • details โ€” enables more detailed dumps (not every dump option honors this one). It also includes info from the optimization passes ๐Ÿ”
    • stats โ€” enables dumping various statistics about the pass (again, not honored by every dump option) ๐Ÿ“ˆ
    • blocks โ€” enables showing basic block boundaries (disabled in raw dumps) ๐Ÿงฑ
    • graph โ€” for each of the other indicated dump files (-fdump-rtl-pass), dumps a control flow graph representation to file.passid.pass.dot, which is suitable for viewing with GraphViz. Each function in the file gets pretty-printed as a subgraph, so GraphViz can render them all in one single plot ๐Ÿ—บ๏ธ RTL is always dumped in slim form.
    • vops โ€” enables showing virtual operands for every statement ๐ŸŽ€
    • lineno โ€” enables showing line numbers for statements ๐Ÿ“
    • uid โ€” enables showing the unique ID ("DECL_UID") for each variable ๐Ÿ†”
    • verbose โ€” enables showing the tree dump for each statement ๐Ÿ“œ
    • eh โ€” enables showing the EH region number that's holding each statement ๐Ÿ›Ÿ
    • scev โ€” enables showing scalar evolution analysis details ๐Ÿงฎ
    • optimized โ€” enables showing optimization info (only available in certain passes) โœ…
    • missed โ€” enables showing missed optimization info (only available in certain passes) ๐Ÿ˜ญ
    • note โ€” enables other detailed optimization info (only available in certain passes) ๐Ÿ“
    • folding โ€” enables dumping info about match-and-simplify (match.pd) patterns when they get applied ๐Ÿช„
    • all โ€” turns on all the options, except raw, slim, verbose and lineno ๐Ÿ’–
    • optall โ€” turns on all the optimization options, which means optimized, missed, and note ๐ŸŒŸ

Finding the dump you want: if you wanna figure out what tree dumps exist or find the dump for a pass you care about, follow these steps ๐Ÿ•ต๏ธโ€โ™€๏ธ

  1. Invoke GCC with -fdump-passes, then look in the stderr output for a code that matches the pass you're into. For example, the codes "tree-evrp", "tree-vrp1", and "tree-vrp2" all correspond to the three Value Range Propagation passes. The number at the end tells you apart the different invocations of the same pass ๐Ÿ”ข
  2. To make the dump file get created, append the pass code to the -fdump- option prefix and invoke GCC with it. For example, to enable the dump from the Early Value Range Propagation pass, invoke GCC with the -fdump-tree-evrp option. You can optionally name the dump file yourself. 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 that the numeric codes aren't stable and can change from one version of GCC to another ๐Ÿ™ˆ
  • -fopt-info, -fopt-info-options, -fopt-info-options=filename โ€” controls optimization dumps coming 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 get emitted, 2. options describing how verbose the dump is, and 3. options describing which optimizations get included ๐Ÿงฉ You can mix the options from each group freely since they don't overlap. But if there's a conflict, the later option on the command line wins ๐Ÿ‘‘

Message kinds: these control which kinds of messages get emitted:

  • optimized โ€” prints info when an optimization gets applied successfully. It's up to each pass to decide what info is relevant. For example, the vectorizer passes print the source location of loops that get vectorized ๐Ÿš€
  • missed โ€” prints info about missed optimizations. Each pass controls what info goes into 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 ๐ŸŒˆ

Verbosity: this one controls how verbose the dump is:

  • internals โ€” by default only "high-level" messages get emitted. This turns on extra, more detailed messages that are probably only interesting to GCC developers ๐Ÿค“

Groups: these keywords describe a whole group of optimizations, so you don't have to list them one by one:

  • ipa โ€” enables dumps from all interprocedural optimizations ๐Ÿฆ‹
  • loop โ€” enables dumps from all loop optimizations ๐Ÿ”
  • inline โ€” enables dumps from all inlining optimizations ๐Ÿซง
  • omp โ€” okay so this one turns on dumps from all the OMP (Offloading and Multi Processing) optimizations ๐Ÿงš
  • vec โ€” dumps from all the vectorization optimizations ๐Ÿ’œ
  • optall โ€” dumps from every single optimization ๐Ÿ‘‘ It's a superset of the groups above, so it's basically the whole buffet ๐Ÿ’…

If options is left out, it defaults to optimized-optall. That means you get messages about the successful optimizations from all the passes, minus anything tagged as "internals" ๐Ÿซถ

If you give it a filename, all the applicable dumps get concatenated into that file. If you don't, the dump goes to stderr instead ๐Ÿฆ‹ You can use more than one -fopt-info option, but only one of them can carry a filename. If you slip in extra filenames, every one after the first gets ignored ๐Ÿ™ˆ

โš ๏ธ Heads up, bestie: the output filename gets overwritten when you have multiple translation units. If you want one combined output from several translation units, use stderr instead ๐Ÿ˜ญ

Here's the vibe in action. This one dumps the optimization info straight to stderr:

gcc -O3 -fopt-info

This one writes 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 opportunities from the vectorization passes to stderr. Fun fact: -fopt-info-vec-missed is the same thing as -fopt-info-missed-vec ๐ŸŒˆ The order of the group names and message types after -fopt-info doesn't matter, so do whatever feels right โœจ

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, we've got a little conflict situation ๐Ÿ’€

gcc -fopt-info-vec-missed=vec.miss -fopt-info-loop-optimized=loop.opt

Two output filenames, vec.miss and loop.opt, are fighting, since only one output file is allowed. So only the first option takes effect and the rest get ignored. You'll only end up with vec.miss, which holds the vectorizer's dumps about missed opportunities ๐ŸŽ€

  • -fsave-optimization-record โ€” okay, this one writes a SRCFILE.opt-record.json.gz file that details what optimizations were performed, for every optimization that supports -fopt-info ๐Ÿ“
๐Ÿงช This option is experimental, and the format of the data inside the compressed JSON file is subject to change. Use it with caution, bestie.

It's roughly a machine-readable version of -fopt-info-all. You get a collection of messages with the source file, line number and column number, plus some extra goodies for each message:

  • the execution count of the code being optimized, plus metadata on whether it came from actual profile data or just an estimate, so consumers can sort messages by how hot the code is ๐Ÿ”ฅ
  • the function name of the code being optimized, where that applies ๐Ÿ’…
  • the "inlining chain" for the code being optimized, so when a function gets inlined into a bunch of different places (which might themselves be inlined), you can tell the copies apart ๐Ÿฆ‹
  • objects identifying the parts of the message that refer to expressions, statements or symbol-table nodes, which category each one belongs to, and, when it's available, its source code location ๐Ÿงš
  • the GCC pass that emitted the message, and
  • the location in GCC's own code where the message was emitted ๐Ÿค“

Also, some messages are logically nested inside other messages, which reflects the implementation details of the optimization passes ๐ŸŒธ

  • -fsched-verbose=n โ€” on targets that use instruction scheduling, this controls how much debugging output the scheduler dumps into the dump files ๐ŸŽ€
    • For n greater than zero, it outputs the same info as -fdump-rtl-sched1 and -fdump-rtl-sched2.
    • For n greater than one, it also outputs basic block probabilities, detailed ready list information and unit/insn info.
    • For n greater than two, it includes the RTL at the abort point, plus control-flow and regions info.
    • And for n over four, -fsched-verbose also includes dependence info ๐Ÿ’€
  • -fenable-kind-pass, -fdisable-kind-pass=range-list โ€” this is a whole family of options for explicitly disabling or enabling optimization passes ๐Ÿงช They're meant for debugging GCC itself, so regular compiler users should stick to the normal options for enabling and disabling passes instead ๐Ÿซถ
    • -fdisable-ipa-pass โ€” disables the IPA pass pass, where pass is the pass name. If the same pass gets statically invoked in the compiler more than once, append a sequential number to the pass name, starting from 1 ๐ŸŒˆ
    • -fdisable-rtl-pass, -fdisable-rtl-pass=range-list โ€” disables the RTL pass pass, where pass is the pass name. Same numbering deal as above: if it's invoked multiple times, append a sequential number starting from 1 โœจ
      • range-list is a comma-separated list of function ranges or assembler names. Each range is a number pair separated by a colon, and the range is inclusive at both ends. If the range is trivial, the number pair can be simplified to a single number ๐Ÿงš
      • If the function's call graph node uid falls inside one of the specified 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 by using -fdump-passes ๐Ÿ”
    • -fdisable-tree-pass, -fdisable-tree-pass=range-list โ€” disables the tree pass pass. Check out -fdisable-rtl for how the option arguments work ๐Ÿ’œ
    • -fenable-ipa-pass โ€” enables the IPA pass pass, where pass is the pass name. Same numbering rule applies: if it's invoked multiple times, append a sequential number starting from 1 ๐Ÿฆ‹
    • -fenable-rtl-pass, -fenable-rtl-pass=range-list โ€” enables the RTL pass pass. See -fdisable-rtl for the option argument description and examples ๐ŸŒธ
    • -fenable-tree-pass, -fenable-tree-pass=range-list โ€” enables the tree pass pass. See -fdisable-rtl for the description of the option arguments ๐Ÿ‘‘

Here are some examples of these options in action, lowkey iconic ๐Ÿ’…

# 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 ๐Ÿ‘€
  • -frandom-seed=string โ€” this gives GCC a seed to use instead of random numbers when it generates certain symbol names that have to be different in every compiled file ๐ŸŽฒ It's also used to stamp coverage data files and the object files that produce them. Use -frandom-seed if you want reproducibly identical object files ๐Ÿ’–

The string can be a number (decimal, octal or hex), or an arbitrary string, which gets converted to a number by computing CRC32 ๐Ÿงฎ The string should be different for every file you compile ๐Ÿซถ

  • -save-temps, --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 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 corresponding 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 -o isn't used, -save-temps=obj acts like -save-temps=cwd ๐Ÿ’œ
  • -specs=file, --specs=file, --specs file โ€” processes file after the compiler reads in the standard specs file, so you can override the defaults the gcc driver uses when picking which switches to pass to cc1, cc1plus, as, ld, etc. ๐ŸŽ€ You can specify more than one -specs=file, and they're processed in order, from left to right ๐ŸŒˆ
  • -time[=file] โ€” reports the CPU time taken by each subprocess in the compilation sequence โฑ๏ธ For C source files, that's the compiler proper and the assembler (plus the linker, if linking happens) ๐Ÿ”ฅ

Without an output file, 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 behalf of the program. Both numbers are in seconds โณ

With an output file, the output gets appended to the named file, and it 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 displayed too, so 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 name is determined by appending .gkd to the dump base name, see -dumpbase ๐Ÿ’…
  • -fcompare-debug[=opts] โ€” okay so if compiling goes through with no errors, GCC 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 from both compilations and prints an error if they don't match. Verification ate, no crumbs ๐Ÿ”

Leave off the equal sign and the default -gtoggle kicks in ๐ŸŽ€

The GCC_COMPARE_DEBUG environment variable can turn this on for you. If it's defined, non-empty and nonzero, it implicitly enables -fcompare-debug. If GCC_COMPARE_DEBUG is defined to a string that starts with a dash, that string is used for opts. Otherwise you get the default -gtoggle ๐Ÿงš

-fcompare-debug= (with the equal sign but no opts) is the same as -fno-compare-debug. That turns off the dumping of the final representation and the second compilation, and it means even GCC_COMPARE_DEBUG can't take effect โœ‹

Want full coverage while you test with -fcompare-debug? Set GCC_COMPARE_DEBUG to -fcompare-debug-not-overridden. GCC rejects that as an invalid option in any actual compilation (rather than preprocessing, assembly or linking) ๐Ÿ™ˆ Only want a warning instead? Setting GCC_COMPARE_DEBUG to -w%n-fcompare-debug not overridden will do ๐Ÿฆ‹

  • -fcompare-debug-second โ€” this one gets passed to the compiler automatically for the second compilation that -fcompare-debug asks for. 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 ๐ŸŒธ During the second compilation, dump files and preserved temporary files get the extra ".gk" extension 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. Honestly that makes it pretty much only useful for debugging the compiler itself ๐Ÿ’€

  • -gtoggle โ€” this flips debug info generation. If leaving out this option generates debug info, it turns generation off. Otherwise it turns debug info on at level 2 ๐Ÿ’œ Where you put it on the command line doesn't matter. It takes effect after all other options are processed, and it only does its thing once, no matter how many times you write it. It's mainly meant to be used with -fcompare-debug.
  • -fvar-tracking-assignments-toggle โ€” toggles -fvar-tracking-assignments, the same way -gtoggle toggles -g โœจ
  • -Q โ€” main character energy for help output ๐Ÿ‘‘ When it's used on the command line before --help=, -Q acts as a modifier to the help output.

Otherwise, this option makes the compiler print the name of each function as it gets compiled, and print some statistics about each pass when that pass finishes ๐Ÿ“Š

  • -ftime-report โ€” makes the compiler print some stats to stderr about the time each pass consumed when it finishes โฑ๏ธ

If you asked for SARIF diagnostics output with -fdiagnostics-format=sarif-file or -fdiagnostics-format=sarif-stderr, then the -ftime-report info gets emitted as JSON inside the SARIF output instead. The precise format of that JSON is subject to change. The values might not exactly match the ones sent to stderr, since they get written out at a slightly different place inside 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 goes to stderr in the same format as n minus 10 ๐Ÿง 
  • -flto-report โ€” prints a report with internal details on how the link-time optimizer is working. What's in this report changes from version to version ๐Ÿคทโ€โ™€๏ธ It's meant for GCC developers who are processing object files in LTO mode (via -flto). It's disabled by default.
  • -flto-report-wpa โ€” like -flto-report, but it only prints for the WPA phase of link-time optimization ๐Ÿฆ‹
  • -fmem-report โ€” makes the compiler print some statistics about permanent memory allocation when it finishes ๐Ÿงฎ
  • -fmem-report-wpa โ€” makes the compiler print some statistics about permanent memory allocation for the WPA phase only ๐ŸŒˆ
  • -fpre-ipa-mem-report, -fpost-ipa-mem-report โ€” makes the compiler print some statistics about permanent memory allocation either before or after interprocedural optimization. Pick your vibe ๐Ÿ’…
  • -fmultiflags โ€” this one's giving build-system drama in the best way ๐ŸŽ€ It enables multilib-aware "TFLAGS" to be used for building target libraries with options that differ 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", this lets target libraries be built for portable baseline environments, while the compiler defaults stay more demanding. That's useful because users can easily override the compiler's defaults to build their own programs when those defaults aren't ideal for their target environment. Rebuilding the runtime libraries is usually not as easy or desirable, so this is the easier route ๐Ÿซถ

Unlike "TFLAGS", using specs lets different flags get selected for different multilibs. To pull that off, build with make TFLAGS=-fmultiflags, after configuring with --with-specs=%{fmultiflags:...}.

This option gets discarded by the driver once it's done processing the driver self spec ๐Ÿ—‘๏ธ

It's also handy for checking that "TFLAGS" are being used to build all 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 โ€” makes the compiler print some statistics about the consistency of the (estimated) profile and the effect of individual passes ๐Ÿ“ˆ
  • -fstack-usage โ€” makes the compiler output stack usage info for your program, per function. The dump filename 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's not 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 qualifier "static" means the function manipulates 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 get made in the function. The second field is that fixed number of bytes ๐Ÿงฑ

The qualifier "dynamic" means the function manipulates the stack dynamically. On top of the static allocation above, stack adjustments get made in the body of the function, for example to push and pop arguments around function calls. If the qualifier "bounded" is also present, 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 those adjustments isn't bounded at compile time, and the second field only represents the bounded part ๐Ÿฅฒ

  • -fstats โ€” emits statistics about front-end processing at the end of compilation. This is only supported by the C++ front end, and the info is generally only useful to the G++ development team ๐Ÿค“
  • -fdbg-cnt-list โ€” prints the name and the counter upper bound for all debug counters ๐Ÿ“‹
  • -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 name of the counter 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) returns true only for the second, third, fourth, tenth and eleventh invocation. For dbg_cnt(tail_call), true is returned for the first 10 invocations โœจ

  • -print-autofdo-gcov-version, --print-autofdo-gcov-version โ€” prints the current version of GCOV being used by the AutoFDO infrastructure ๐Ÿ–จ๏ธ
  • -print-file-name=library, --print-file-name=library, --print-file-name library โ€” prints the full absolute name of the library file library that would be used when linking, and then stops there. With this option, GCC doesn't compile or link anything. It just prints the file name ๐Ÿ“‚
  • -print-multi-directory, --print-multi-directory โ€” prints the directory name that matches the multilib selected by any other switches on the command line. That directory is supposed to exist in GCC_EXEC_PREFIX ๐Ÿ—‚๏ธ
  • -print-multi-lib, --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 -, with no spaces between multiple switches. This is supposed to make shell processing easier ๐Ÿš
  • -print-multi-os-directory, --print-multi-os-directory โ€” prints the path to OS libraries for the selected multilib, relative to some lib subdirectory. If OS libraries live in the lib subdirectory and no multilibs are used, this is usually just .. If OS libraries live in libsuffix sibling directories, it prints something like ../lib64, ../lib or ../lib32. If OS libraries live in lib/subdir subdirectories, it prints something like amd64, sparcv9 or ev6 ๐ŸŒ
  • -print-multiarch, --print-multiarch โ€” prints the path to OS libraries for the selected multiarch, relative to some lib subdirectory ๐Ÿงฉ
  • -print-prog-name=program, --print-prog-name=program, --print-prog-name program โ€” like -print-file-name, but it searches for a program such as cpp instead of a library ๐Ÿ”ง
  • -print-libgcc-file-name, --print-libgcc-file-name โ€” same as -print-file-name=libgcc.a ๐Ÿ’–

This is useful when you use -nostdlib or -nodefaultlibs but you still want to link with libgcc.a. You can do:

gcc -nostdlib <files>... `gcc -print-libgcc-file-name`
  • -print-search-dirs, --print-search-dirs โ€” prints the name of the configured installation directory and a list of the program and library directories gcc searches, and then stops. It doesn't do anything else ๐Ÿ—บ๏ธ

Okay so this bit is SO useful when gcc starts throwing the classic installation problem, cannot exec cpp0: No such file or directory. ๐Ÿ˜ญ๐Ÿซถ To fix it, you either need to put cpp0 and the other compiler components exactly where gcc expects to find them, or you can set the environment variable GCC_EXEC_PREFIX to the directory where you installed them. And don't forget the trailing /! ๐Ÿ’…

  • -print-sysroot, --print-sysroot โ€” prints the target sysroot directory the compiler is using while it compiles ๐ŸŒธ. That's whatever was set at configure time or with --sysroot, maybe with an extra suffix that depends on your compilation options. If there's no target sysroot, it prints nothing at all, bestie.
  • -print-sysroot-headers-suffix, --print-sysroot-headers-suffix โ€” prints the suffix tacked onto the target sysroot when searching for headers ๐Ÿงš. If the compiler wasn't configured with a suffix, it errors out instead. Either way it stops there and doesn't do anything else.
  • -dumpmachine โ€” prints the compiler's target machine, like i686-pc-linux-gnu, and then it's done ๐Ÿฆ‹.
  • -dumpversion โ€” prints the compiler version, like 3.0, 6.3.0 or 7, and then stops โœจ. This is the version used in filesystem paths and specs. Depending on how the compiler was configured, it could be one number (major), two numbers separated by a dot (major and minor), or three numbers separated by dots (major, minor and patchlevel).
  • -dumpfullversion โ€” prints the full compiler version and nothing else ๐Ÿ’œ. The output is always three numbers separated by dots: major, minor and patchlevel.
  • -dumpspecs โ€” prints the compiler's built-in specs and then stops ๐ŸŒˆ. (This is only used when GCC itself is being built, so it's kinda niche.)
  • --param name=value, --param=name=value โ€” okay so GCC, by convention, uses parameters you can pass on the command line instead of hard-wired constants for compiler limits and heuristics ๐Ÿ”ฅ. Lots of them are optimization stuff, like GCC not inlining functions that have more than a certain number of instructions. The static analyzer uses them to limit complexity, link-time optimization uses them to control partitioning, and so on. Others control internal stuff like memory allocation and garbage collection strategy, and some control target-specific behavior. The --param option gives you one uniform way to set all of them, but the names and meanings are tied to the compiler's internals and can change without notice in future releases.
Don't depend on parameter settings for your program to compile correctly! ๐Ÿ’€ They're exposed for developers debugging compilation problems or, in some cases, as workarounds for compiler bugs.

โœจ Machine-Dependent Options โœจ

Each target machine GCC supports can have its own options ๐ŸŒธ. For example, you can compile for a particular processor variant or ABI, or control optimizations specific to that machine. By convention, machine-specific option names start with -m.

Some configurations of the compiler also support extra target-specific options, usually so they can play nice with other compilers on the same platform ๐Ÿซถ.

๐Ÿฆ‹ AArch64 Options ๐Ÿฆ‹

These options are defined for AArch64 implementations:

  • -mabi=name โ€” picks the data model you want code generated for ๐Ÿ‘‘. ilp32 is the SysV-like one where int, long int and pointers are all 32 bits, and lp64 is the SysV-like one where int is 32 bits but long int and pointers are 64 bits. The default depends on your 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 against a compatible set of libraries. Also, the ilp32 model is deprecated, so be careful with it ๐Ÿ˜ญ.
  • -mbig-endian โ€” generates big-endian code ๐Ÿ“. This is the default when GCC is configured for an aarch64_be-*-* target.
  • -mlittle-endian โ€” generates little-endian code ๐Ÿ’–. This is the default when GCC is configured for an aarch64-*-* target but not an aarch64_be-*-* one.
  • -menable-sysreg-checking โ€” gives you an error message if you try to access a system register that isn't available on the target architecture ๐Ÿ™ˆ.
  • -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.
  • -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, so you get it for free ๐Ÿ’….
  • -mcmodel=large โ€” generates code for the large code model ๐Ÿ”ฅ. It makes NO assumptions about addresses and section sizes. Programs can only be statically linked. Also, -mcmodel=large is incompatible with -mabi=ilp32, -fpic and -fPIC, so don't mix them up ๐Ÿ™…โ€โ™€๏ธ.
  • -mtp=name โ€” specifies the system register to use 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 are also accepted. The default is tpidr_el0. It's recommended to compile all code that's meant to interoperate with the same value of this option, so you don't end up accessing a different thread pointer from the wrong exception level ๐Ÿ˜ฌ.
  • -mstrict-align, -mno-strict-align โ€” avoids or allows generating 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 โ€” omits or keeps the frame pointer in leaf functions ๐Ÿฆ‹. The former behavior (omitting it) is the default.
  • -mstack-protector-guard=guard, -mstack-protector-guard-reg=reg, -mstack-protector-guard-offset=offset โ€” generates 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 further specify which system register to use as the base register for reading the canary, and what offset from that base register to use. 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 of TLS variables ๐Ÿ’œ. This is the default.
  • -mtls-dialect=traditional โ€” uses traditional TLS as the thread-local storage mechanism for dynamic accesses of TLS variables ๐ŸŽ€.
  • -mtls-size=size โ€” specifies the bit size of immediate TLS offsets โญ. Valid values are 12, 24, 32 and 48. This option requires binutils 2.26 or newer.
  • -mfix-cortex-a53-835769, -mno-fix-cortex-a53-835769 โ€” enables or disables the workaround for ARM Cortex-A53 erratum number 835769 ๐Ÿฉน. This 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 ARM Cortex-A53 erratum number 843419 ๐Ÿฉน. This erratum workaround is made at link time, so this flag only passes the corresponding flag to the linker. It's also ignored if an architecture or cpu is specified on the command line that doesn't need the workaround.
  • -mlow-precision-recip-sqrt, -mno-low-precision-recip-sqrt โ€” enables or disables the reciprocal square root approximation ๐Ÿงฎ. This option only has an effect if -ffast-math or -funsafe-math-optimizations is used as well. Enabling it reduces precision of reciprocal square root results to about 16 bits for single precision and to 32 bits for double precision.
  • -mlow-precision-sqrt, -mno-low-precision-sqrt โ€” enables or disables the square root approximation ๐ŸŒˆ. This option only has an effect if -ffast-math or -funsafe-math-optimizations is used as well. Enabling it reduces precision of square root results to about 16 bits for single precision and to 32 bits for double precision. If enabled, it implies -mlow-precision-recip-sqrt.
  • -mlow-precision-div, -mno-low-precision-div โ€” enables or disables the division approximation โž—. This option only has an effect if -ffast-math or -funsafe-math-optimizations is used as well. Enabling it reduces precision of division results to about 16 bits for single precision and to 32 bits for double precision.
  • -mtrack-speculation, -mno-track-speculation โ€” enables or disables generation of extra code that tracks speculative execution through conditional branches ๐Ÿ•ต๏ธโ€โ™€๏ธ. The tracking state can then be used by the compiler when expanding calls to "__builtin_speculation_safe_value", so it can generate 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 not, they fall back to the load/store-exclusive instructions that are present in the base ARMv8.0 ISA.

This option is only applicable when 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 โœจ.

๐Ÿ’– -mmax-vectorization / -mno-max-vectorization

okay so these two flip a switch that makes the vectorizer's cost model override itself, so vectorization always looks profitable ๐Ÿ˜ญโœจ -mmax-vectorization turns that on and -mno-max-vectorization turns it back off. You can pair it with -mautovec-preference for super precise control over which ISA gets used for auto-vectorization ๐ŸŽ€. And heads up, bestie: unlike -fno-vect-cost-model or -fvect-cost-model=unlimited, this one does not turn off the cost comparison between different vector modes ๐Ÿงš.


โœจ -mautovec-preference=name

this one forces an ISA selection strategy for auto-vectorization ๐Ÿ’… The possible values of name are:

  • default โ€” just use the default heuristics, nothing fancy ๐ŸŒธ
  • asimd-only โ€” Advanced SIMD only, no SVE allowed ๐Ÿฆ‹
  • sve-only โ€” SVE only, no Advanced SIMD allowed ๐Ÿ”ฅ
  • prefer-asimd โ€” use both Advanced SIMD and SVE, but go with Advanced SIMD when the costs are deemed equal ๐Ÿ“
  • prefer-sve โ€” use both Advanced SIMD and SVE, but go with SVE when the costs are deemed equal ๐Ÿ’œ
For the best performance, it is highly recommended to use -mcpu or -mtune instead โš ๏ธ This parameter should only be used for code exploration, so don't go rewriting your whole build around it ๐Ÿ™ˆ

๐ŸŒˆ -march=name

okay this is the big one ๐Ÿ‘‘ -march specifies the name of the target architecture and, optionally, one or more feature modifiers. It has the form -march=arch{+[no]feature}* โœจ

Here's the lineup of permissible arch values and the features each one enables by default:

  • 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.5-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 recognize the host architecture, this option just has no effect. When -march=native is given and no other -mcpu or -mtune is given, GCC picks the host CPU to tune for and selects the architecture features from it. Basically, -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 one wins ๐ŸŽ€

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

-mtune is where you tell GCC which target processor to tune the performance of your code for ๐Ÿ’… 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, hip12, 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, ampere1c, cobalt-100, apple-m1, apple-m2, apple-m3,
apple-m4, c1-nano, c1-pro, c1-premium c1-ultra and native.

some of these are big.LITTLE combos, and those values tell GCC to tune for a big.LITTLE system ๐Ÿงธ They are 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 โœจ

neoverse-512tvb is giving "all Neoverse cores with SVE and a total vector bandwidth of 512 bits per cycle" ๐Ÿฆ‹ In other words, it tunes for Neoverse cores that can execute 4 128-bit Advanced SIMD arithmetic instructions a cycle, and an equivalent number of SVE arithmetic instructions per cycle (2 for 256-bit SVE, 4 for 128-bit SVE). It's more general than tuning for a specific core like Neoverse V1, but more specific than the default tuning ๐ŸŒˆ

On native AArch64 GNU/Linux systems, native tunes performance to the host system. If the compiler can't recognize the host processor, that has no effect ๐Ÿซถ

If 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 ๐Ÿ™…โ€โ™€๏ธ

๐Ÿ‘‘ -mcpu=name

-mcpu is the name of the target processor, optionally suffixed by one or more feature modifiers ๐Ÿ’– It has the form -mcpu=cpu{+[no]feature}*, where the permissible values for cpu are the same as the ones for -mtune. The permissible values for feature are documented in the sub-section on aarch64-feature-modifiers,,-march and -mcpu Feature Modifiers. Same rule as before, bestie: if feature modifiers conflict, the right-most one is used ๐ŸŽ€

GCC uses name to figure out what kind of instructions it can emit when generating assembly code (as if by -march), and to figure out which target processor to tune for performance (as if by -mtune) ๐Ÿงš If you use this option together with -march or -mtune, those options take precedence over the matching part of -mcpu โœจ

-mcpu=neoverse-512tvb is special, lowkey ๐ŸŒธ It doesn't refer to one specific core. Instead it refers to all Neoverse cores that (a) implement SVE and (b) have a total vector bandwidth of 512 bits a cycle. Unless you override it with -march, -mcpu=neoverse-512tvb generates code that can run on a Neoverse V1 core, since Neoverse V1 is the first Neoverse core with these properties ๐Ÿฆ‹ And unless you override it with -mtune, -mcpu=neoverse-512tvb tunes code in the same way as -mtune=neoverse-512tvb ๐Ÿ’…


๐Ÿฆ‹ -moverride=string

this one 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 be consistent across releases, so please don't build anything important on it ๐Ÿ˜ญ

This option is only intended to be useful when developing GCC ๐Ÿ› ๏ธ

๐Ÿงš -mpc-relative-literal-loads / -mno-pc-relative-literal-loads

these enable or disable PC-relative literal loads ๐Ÿ’œ With this option, literal pools are accessed using a single instruction and emitted after each function. The catch is that this limits the maximum size of functions to 1MB ๐Ÿ™ˆ It's enabled by default for -mcmodel=tiny.


๐ŸŽ€ -msign-return-address=scope

this selects the function scope where return address signing gets applied ๐Ÿ” The permissible values are:

  • none โ€” disables return address signing
  • non-leaf โ€” enables pointer signing for functions which are not leaf functions
  • all โ€” enables pointer signing for all functions

The default value is none. Heads up, this option has been deprecated by -mbranch-protection, so use that one instead ๐Ÿ™…โ€โ™€๏ธ


๐Ÿ”ฅ -mbranch-protection=features

this picks the branch protection features to use ๐Ÿ›ก๏ธ features can have one of the following forms:

  • none โ€” the default, turns off all types of branch protection ๐Ÿซฅ
  • standard โ€” turns on all types of branch protection features. If a feature has additional tuning options, then standard sets it to its standard level ๐Ÿ’–
  • pac-ret โ€” turns on return address signing to its standard level: signing functions that save the return address to memory (non-leaf functions practically always do this) using the A-key ๐Ÿฆ‹
  • pac-ret+leaf โ€” extends the pac-ret signing to include leaf functions ๐ŸŒˆ
  • pac-ret+b-key or pac-ret+leaf+b-key โ€” can be used to sign 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

this enables compiler hardening against straight line speculation (SLS) ๐Ÿซถ opts is a comma-separated list of the following options:

  • retbr
  • blr

In addition, -mharden-sls=all enables all SLS hardening, while -mharden-sls=none disables all SLS hardening โœจ


๐Ÿซถ -mearly-ra=scope

this decides when to enable an early register allocation pass ๐Ÿง  The 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, such as SME2's strided LD1 and ST1 ๐ŸŒธ

The possible values of scope are:

  • 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 / -mno-early-ldp-fusion

these enable or disable the copy of the AArch64 load/store pair fusion pass that runs before register allocation ๐Ÿ“ It's enabled by default at -O and above ๐Ÿ’–


๐Ÿ’œ -mlate-ldp-fusion / -mno-late-ldp-fusion

same vibe as the early one, but these enable or disable the copy of the AArch64 load/store pair fusion pass that runs after register allocation ๐ŸŒˆ It's also enabled by default at -O and above ๐Ÿซถ

  • -msve-vector-bits=bits โ€” okay so this one sets how many bits live in an SVE vector register ๐Ÿฆ‹ It only does anything when SVE is turned on ๐Ÿ’…

GCC makes two flavors of SVE code, bestie. There's "vector-length agnostic" output that's happy with any size of vector register โœจ, and "vector-length specific" output that lets GCC assume a vector length when that helps with optimization ๐Ÿ”ฅ. The possible values of bits are scalable, 128, 256, 512, 1024, and 2048. scalable gets you vector-length agnostic output. At present, -msve-vector-bits=128 also gives you vector-length agnostic output on big-endian targets ๐ŸŒˆ. Every other value makes vector-length specific code. These values might change in future releases, so ngl the only one you should rely 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 ๐ŸŒธ

๐ŸŒˆ -march and -mcpu Feature Modifiers ๐ŸŒˆ

Okay, feature modifiers used with -march and -mcpu can be any of the ones below, and each one also has an inverse you write as nofeature ๐Ÿ’–

  • crc โ€” Enable the CRC extension. It's on by default for -march=armv8.1-a ๐Ÿงš
  • crypto โ€” Enable the Crypto extension. This also turns on Advanced SIMD and floating-point instructions ๐Ÿ”
  • fp โ€” Enable floating-point instructions. It's on by default for literally every possible value of -march and -mcpu ๐Ÿ’…
  • simd โ€” Enable Advanced SIMD instructions. This also turns on floating-point instructions. It's on by default for every possible value of -march and -mcpu โœจ
  • sve โ€” Enable Scalable Vector Extension instructions. This also turns on Advanced SIMD and floating-point instructions ๐Ÿฆ‹
  • lse โ€” Enable Large System Extension instructions. It's on by default for -march=armv8.1-a ๐Ÿ”ฅ
  • rdma โ€” Enable Round Double Multiply Accumulate instructions. It's on by default for -march=armv8.1-a ๐ŸŒธ
  • fp16 โ€” Enable the FP16 extension. This also turns on floating-point instructions ๐Ÿ“
  • fp16fml โ€” Enable the FP16 fmla extension. This also turns on the FP16 extensions and floating-point instructions. It's enabled by default for -march=armv8.4-a. Using this with architectures before Armv8.2-A isn't supported ๐Ÿ™ˆ
  • rcpc โ€” Enable the RCpc extension. This lets you use the LDAPR instructions for load-acquire atomic semantics, and it passes the setting on to the assembler so inline asm statements can use RCpc instructions ๐Ÿ’œ
  • dotprod โ€” Enable the Dot Product extension. This also turns on Advanced SIMD instructions ๐ŸŒˆ
  • aes โ€” Enable the Armv8-a aes and pmull crypto extension. This also turns on Advanced SIMD instructions ๐Ÿ”
  • sha2 โ€” Enable the Armv8-a sha2 crypto extension. This also turns on Advanced SIMD instructions โญ
  • sha3 โ€” Enable the sha512 and sha3 crypto extension. This also turns on Advanced SIMD instructions. Using this with architectures before Armv8.2-A isn't supported ๐Ÿซถ
  • sm4 โ€” Enable the sm3 and sm4 crypto extension. This also turns on Advanced SIMD instructions. Using this with architectures before Armv8.2-A isn't supported ๐Ÿงš
  • profile โ€” Enable the Statistical Profiling extension. This only turns it on at the assembler level and doesn't change code generation ๐Ÿ“Š
  • rng โ€” Enable the Armv8.5-a Random Number instructions. This only turns the extension on at the assembler level and doesn't affect code generation ๐ŸŽฒ
  • memtag โ€” Enable the Armv8.5-a Memory Tagging Extensions. Using this with architectures before Armv8.5-A isn't supported ๐Ÿท๏ธ
  • sb โ€” Enable the Armv8-a Speculation Barrier instruction. This only turns it on at the assembler level and doesn't affect code generation. It's enabled by default for -march=armv8.5-a ๐Ÿ›ก๏ธ
  • ssbs โ€” Enable the Armv8-a Speculative Store Bypass Safe instruction. This only turns it on at the assembler level and doesn't affect code generation. It's enabled by default for -march=armv8.5-a ๐Ÿ™Œ
  • predres โ€” Enable the Armv8-a Execution and Data Prediction Restriction instructions. This only turns them on at the assembler level and doesn't affect code generation. It's enabled by default for -march=armv8.5-a ๐Ÿ’€
  • sve2 โ€” Enable the Armv8-a Scalable Vector Extension 2. This also turns on SVE instructions ๐Ÿฆ‹
  • sve2-bitperm โ€” Enable SVE2 bitperm instructions. This also turns on SVE2 instructions ๐ŸŒธ
  • sve2-sm4 โ€” Enable SVE2 sm4 instructions. This also turns on SVE2 instructions ๐Ÿ“
  • sve2-aes โ€” Enable SVE2 aes instructions. This also turns on SVE2 instructions ๐Ÿ”ฅ
  • sve2-sha3 โ€” Enable SVE2 sha3 instructions. This also turns on SVE2 instructions ๐Ÿ‘‘
  • sve2p1 โ€” Enable SVE2.1 instructions. This also turns on SVE2 instructions ๐ŸŒˆ
  • tme โ€” Enable the Transactional Memory Extension ๐Ÿง 
  • i8mm โ€” Enable 8-bit Integer Matrix Multiply instructions. This also turns on Advanced SIMD and floating-point instructions. It's enabled by default for -march=armv8.6-a. Using this with architectures before Armv8.2-A isn't supported ๐Ÿ”ข
  • f32mm โ€” Enable 32-bit Floating point Matrix Multiply instructions. This also turns on SVE instructions. Using this with architectures before Armv8.2-A isn't supported ๐Ÿงฎ
  • f64mm โ€” Enable 64-bit Floating point Matrix Multiply instructions. This also turns on SVE instructions. Using this with architectures before Armv8.2-A isn't supported ๐Ÿงฎ
  • bf16 โ€” Enable brain half-precision floating-point instructions. This also turns on Advanced SIMD and floating-point instructions. It's enabled by default for -march=armv8.6-a. Using this with architectures before Armv8.2-A isn't supported ๐Ÿง 
  • ls64 โ€” Enable the 64-byte atomic load and store instructions for accelerators ๐Ÿš€
  • mops โ€” Enable the instructions that speed up memory operations like "memcpy", "memmove", "memset". It's enabled by default for -march=armv8.8-a ๐Ÿ’จ
  • flagm โ€” Enable the Flag Manipulation instructions Extension ๐Ÿšฉ
  • flagm2 โ€” Enable the FlagM2 flag conversion instructions ๐Ÿ”
  • pauth โ€” Enable the Pointer Authentication Extension ๐Ÿ”‘
  • cssc โ€” Enable the Common Short Sequence Compression instructions ๐Ÿ—œ๏ธ
  • cmpbr โ€” Enable the shorter compare and branch instructions, "cbb", "cbh" and "cb" โšก
  • sme โ€” Enable the Scalable Matrix Extension ๐ŸงŠ
  • sme-i16i64 โ€” Enable the FEAT_SME_I16I64 extension to SME. This also turns on SME instructions ๐ŸงŠ
  • sme-f64f64 โ€” Enable the FEAT_SME_F64F64 extension to SME. This also turns on SME instructions ๐ŸงŠ
  • sme2 โ€” Enable the Scalable Matrix Extension 2. This also turns on SME instructions ๐ŸงŠ
  • sme-f8f16 โ€” Enable the FEAT_SME_F8F16 extension to SME. This also turns on SME2 and FP8 instructions ๐ŸงŠ
  • sme-f8f32 โ€” Enable the FEAT_SME_F8F32 extension to SME. This also turns on SME2 and FP8 instructions ๐ŸงŠ
  • sme-b16b16 โ€” Enable the FEAT_SME_B16B16 extension to SME. This also turns on SME2 and SVE_B16B16 instructions ๐ŸงŠ
  • sme-f16f16 โ€” Enable the FEAT_SME_F16F16 extension to SME. This also turns on SME2 instructions ๐ŸงŠ
  • sme2p1 โ€” Enable the Scalable Matrix Extension version 2.1. This also turns on SME2 instructions ๐ŸงŠ
  • fcma โ€” Enable the complex number SIMD extensions ๐Ÿ”ข
  • jscvt โ€” Enable the "fjcvtzs" JavaScript conversion instruction ๐Ÿ“œ
  • frintts โ€” Enable floating-point round to integral value instructions ๐ŸŽฏ
  • wfxt โ€” Enable the "wfet" and "wfit" instructions โณ
  • xs โ€” Enable the XS memory attribute extension ๐Ÿงฑ
  • lse128 โ€” Enable the LSE128 128-bit atomic instructions extension. This also turns on LSE instructions ๐Ÿ”
  • d128 โ€” Enable support for 128-bit system register read/write instructions. This also turns on the LSE128 extension ๐Ÿ—๏ธ
  • gcs โ€” Enable support for the Armv9.4-a Guarded Control Stack extension ๐Ÿฐ
  • the โ€” Enable support for the Armv8.9-a/9.4-a translation hardening extension ๐Ÿ› ๏ธ
  • rcpc2 โ€” Enable the RCpc2 extension ๐Ÿ’œ
  • rcpc3 โ€” Enable the RCpc3 (Release Consistency) extension ๐Ÿ’œ
  • fp8 โ€” Enable the fp8 (8-bit floating point) extension ๐ŸŒŸ
  • fp8fma โ€” Enable the fp8 (8-bit floating point) multiply accumulate extension โœ–๏ธ
  • ssve-fp8fma โ€” Enable the fp8 (8-bit floating point) multiply accumulate extension in streaming mode ๐ŸŒŠ
  • fp8dot4 โ€” Enable the fp8 (8-bit floating point) to single-precision 4-way dot product extension ๐ŸŽ€
  • ssve-fp8dot4 โ€” Enable the fp8 (8-bit floating point) to single-precision 4-way dot product extension in streaming mode ๐ŸŒŠ
  • fp8dot2 โ€” Enable the fp8 (8-bit floating point) to half-precision 2-way dot product extension ๐ŸŽ€
  • ssve-fp8dot2 โ€” Enable the fp8 (8-bit floating point) to half-precision 2-way dot product extension in streaming mode ๐ŸŒŠ
  • faminmax โ€” Enable the Floating Point Absolute Maximum/Minimum extension ๐Ÿ“
  • lut โ€” Enable the Lookup Table extension ๐Ÿ—บ๏ธ
  • sme-lutv2 โ€” Enable the SME Lookup Table v2 (LUTv2) extension ๐Ÿ—บ๏ธ
  • cpa โ€” Enable the Checked Pointer Arithmetic instructions ๐Ÿงท
  • sve-b16b16 โ€” Enable the SVE non-widening brain floating-point ("bf16") extension. This only has an effect when "sve2" or "sme2" are also enabled ๐Ÿง 
  • sve-bfscale โ€” Enable the SVE_BFSCALE extension โš–๏ธ

Feature crypto implies aes, sha2, and simd, which implies fp. Conversely, nofp implies nosimd, which implies nocrypto, noaes and nosha2. So they all come as a little package deal ๐ŸŽ€

๐ŸŒธ Adapteva Epiphany Options ๐ŸŒธ

These -m options are defined for Adapteva Epiphany ๐Ÿฆ‹

  • -mhalf-reg-file, -mno-half-reg-file โ€” Don't allocate any register in the range "r32"..."r63". That lets code run on hardware variants that don't have these registers ๐Ÿ’…
  • -mprefer-short-insn-regs, -mno-prefer-short-insn-regs โ€” Preferentially allocate registers that allow short instruction generation. This can bump up the instruction count, so it might shrink or grow overall code size ๐Ÿคทโ€โ™€๏ธ
  • -mbranch-cost=num โ€” Set the cost of branches to roughly num "simple" instructions. This cost is only a heuristic and isn't guaranteed to give consistent results across releases ๐Ÿงฎ
  • -mcmove, -mno-cmove โ€” Enable the generation of conditional moves โœจ
  • -mnops=num โ€” Emit num NOPs before every other generated instruction ๐Ÿซง
  • -mno-soft-cmpsf, -msoft-cmpsf โ€” okay so this pair is about single-precision floating-point comparisons ๐Ÿฆ‹ With -mno-soft-cmpsf, the compiler emits an fsub instruction and checks the flags instead. It's faster than a software comparison, BUT it can get wrong answers when NaNs are involved, or when two different small numbers subtract to zero, which is so not it ๐Ÿ’€ The default is -msoft-cmpsf, which uses slower software comparisons that are IEEE-compliant. Safe and sound, just not the speed demon ๐Ÿซถ
  • -mstack-offset=num โ€” this sets the gap between the top of the stack and the stack pointer ๐Ÿงš A value of 8 means the eight bytes from sp+0 through sp+7 can be used by leaf functions without stack allocation ๐Ÿ“ Heads up though: values other than 8 or 16 are untested and unlikely to work ๐Ÿ™ˆ This option also changes the ABI, so compiling a program with a different stack offset than the one your libraries were built with generally doesn't work ๐Ÿ˜ญ It's useful if you're testing whether a different offset gives you better code. But if you actually want working programs with a different offset, the move is to configure the toolchain with the appropriate --with-stack-offset=num option ๐ŸŽ€
  • -mno-round-nearest, -mround-nearest โ€” -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 every call might be beyond the offset range of the b / bl instructions ๐Ÿงธ So the function address gets loaded into a register before the (otherwise direct) call. This is the default, bestie.
  • -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. Also note that -mlong-calls is equivalent to -mno-short-calls, and -mno-long-calls is equivalent to -mshort-calls. It's giving mirror energy ๐Ÿชž
  • -msmall16, -mno-small16 โ€” this assumes addresses can be loaded as 16-bit unsigned values ๐Ÿ’… It doesn't apply to function addresses where -mlong-calls semantics are in effect.
  • -mfp-mode=mode โ€” this sets the prevailing mode of the floating-point unit, which decides the floating-point mode that's provided and expected at function call and return โœจ If you match it to the mode you mostly need at function start, your programs can get smaller and faster by skipping unnecessary mode switches ๐Ÿ”ฅ The mode can be one of these:
    • 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 one's perfect for libraries or compilation units you might drop into different programs with different FPU modes. The convenience of a single object file outweighs the extra size and speed overhead of mode switching, compared with a more specific pick ๐Ÿ‘‘
    • truncate โ€” the mode used for floating-point calculations with truncating (round towards zero) rounding. That includes conversion from floating point to integer.
    • round-nearest โ€” the mode used for floating-point calculations with round-to-nearest-or-even rounding.
    • int โ€” the mode used to do integer calculations in the FPU, like integer multiply or integer multiply-and-accumulate.

The default is -mfp-mode=caller.

  • -mmay-round-for-trunc, -mno-may-round-for-trunc โ€” this lets floating point to integer truncation get swapped out for rounding, so you can save on mode switching ๐ŸŒˆ It's disabled by default.
  • -mfp-iarith, -mno-fp-iarith โ€” this turns on using the floating-point unit for integer add and subtract ๐Ÿงฎ It's disabled by default.
  • -msplit-lohi, -mno-split-lohi, -mpost-inc, -mno-post-inc, -mpost-modify, -mno-post-modify โ€” these are code generation tweaks ๐Ÿ“ They control, 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.
  • -mno-vect-double โ€” this changes the preferred SIMD mode to SImode ๐ŸŽ€ The default is -mvect-double, which uses DImode as the preferred SIMD mode.
  • -max-vect-align=num โ€” this is the maximum alignment for SIMD vector mode types. num can be 4 or 8, and the default is 8 ๐Ÿซถ Note that this is an ABI change, even though lots of 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, -mno-split-vecmove-early โ€” this splits vector moves into single word moves before reload ๐Ÿง  In theory it can give better register allocation, but so far the reverse seems to be generally the case ๐Ÿ˜ญ
  • -m1reg-reg โ€” this 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 use 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, so they're for the GPU girlies ๐ŸŽฎ

  • -march=gpu, -mtune=gpu โ€” this sets the architecture type or tuning for gpu ๐Ÿ’– The supported values for gpu are:
    • gfx900 โ€” compile for GCN5 Vega 10 devices (gfx900).
    • gfx902 โ€” compile for GCN5 Vega gfx902 devices. (Experimental)
    • gfx904 โ€” compile for GCN5 Vega gfx904 devices. (Experimental)
    • gfx906 โ€” compile for GCN5 Vega 20 devices (gfx906).
    • gfx908 โ€” compile for CDNA1 Instinct MI100 series devices (gfx908).
    • gfx909 โ€” compile for GCN5 Vega gfx909 devices. (Experimental)
    • gfx90a โ€” compile for CDNA2 Instinct MI200 series devices (gfx90a).
    • gfx90c โ€” compile for GCN5 Vega 7 devices (gfx90c).
    • gfx942 โ€” compile for CDNA3 Instinct MI300 series devices (gfx942). (Experimental)
    • gfx950 โ€” compile for CDNA3 gfx950 devices. (Experimental)
    • gfx9-generic โ€” compile generic code for Vega devices, executable on the following subset of GFX9 devices: gfx900, gfx902, gfx904, gfx906, gfx909 and gfx90c.
    • gfx9-4-generic โ€” compile generic code for CDNA3 devices, executable on the following subset of GFX9 devices: gfx942 and gfx950. (Experimental)
    • gfx1030 โ€” compile for RDNA2 gfx1030 devices (GFX10 series).
    • gfx1031 โ€” compile for RDNA2 gfx1031 devices (GFX10 series). (Experimental)
    • gfx1032 โ€” compile for RDNA2 gfx1032 devices (GFX10 series). (Experimental)
    • gfx1033 โ€” compile for RDNA2 gfx1033 devices (GFX10 series). (Experimental)
    • gfx1034 โ€” compile for RDNA2 gfx1034 devices (GFX10 series). (Experimental)
    • gfx1035 โ€” compile for RDNA2 gfx1035 devices (GFX10 series). (Experimental)
    • gfx1036 โ€” compile for RDNA2 gfx1036 devices (GFX10 series).
    • gfx10-3-generic โ€” compile generic code for GFX10-3 devices, executable on gfx1030, gfx1031, gfx1032, gfx1033, gfx1034, gfx1035, and gfx1036.
    • gfx1100 โ€” compile for RDNA3 gfx1100 devices (GFX11 series).
    • gfx1101 โ€” compile for RDNA3 gfx1101 devices (GFX11 series). (Experimental)
    • gfx1102 โ€” compile for RDNA3 gfx1102 devices (GFX11 series). (Experimental)
    • gfx1103 โ€” compile for RDNA3 gfx1103 devices (GFX11 series).
    • gfx1150 โ€” compile for RDNA3 gfx1150 devices (GFX11 series). (Experimental)
    • gfx1151 โ€” compile for RDNA3 gfx1151 devices (GFX11 series). (Experimental)
    • gfx1152 โ€” compile for RDNA3 gfx1152 devices (GFX11 series). (Experimental)
    • gfx1153 โ€” compile for RDNA3 gfx1153 devices (GFX11 series). (Experimental)
    • gfx11-generic โ€” compile generic code for GFX11 devices, executable on gfx1100, gfx1101, gfx1102, gfx1103, gfx1150, gfx1151, gfx1152, and gfx1153.
  • -mgang-private-size=bytes โ€” this sets how much local data-share (LDS) memory gets reserved for gang-private variables ๐Ÿ’œ The default is 512.
  • -msram-ecc=on, -msram-ecc=off, -msram-ecc=any โ€” these compile binaries for devices with the SRAM-ECC feature enabled, disabled, or either mode ๐Ÿ” Some devices let you turn this on per-process, and your compiled code has to match the device's mode. The default is any, for devices that support it.
  • -mxnack=on, -mxnack=off, -mxnack=any โ€” these compile binaries for devices with the XNACK feature enabled, disabled, or either mode ๐ŸŒธ Some devices always require XNACK, and some let the user configure it. The compiled code must match the device mode. The default is -mxnack=any on devices that support Unified Shared Memory, and -mxnack=no otherwise.
  • -Wopenacc-dims, -Wno-openacc-dims โ€” these control warnings about invalid OpenACC dimensions โš ๏ธ

๐Ÿงš ARC Options โœจ

The following options control the architecture variant that code is being compiled for ๐Ÿฆ–

  • -mbarrel-shifter โ€” this generates instructions supported by the barrel shifter. It's the default unless -mcpu=ARC601 or -mcpu=ARCEM is in effect.
  • -mjli-always โ€” this forces a function call using the jli_s instruction ๐Ÿ’… It's only valid for the ARCv2 architecture.
  • -mcpu=cpu โ€” this sets the architecture type, register usage, and instruction scheduling parameters for cpu ๐Ÿ‘‘ There are shortcut alias options too, for backward compatibility and convenience. The supported values for cpu are:
    • arc600 โ€” compile for ARC600. Aliases: -mA6, -mARC600.
    • arc601 โ€” compile for ARC601. Alias: -mARC601.
    • arc700 โ€” compile for ARC700. Aliases: -mA7, -mARC700. This is the default when configured with --with-cpu=arc700.
    • arcem โ€” compile for ARC EM.
    • archs โ€” compile for ARC HS.
    • em โ€” compile for ARC EM CPU with no hardware extensions.
    • em4 โ€” compile for ARC EM4 CPU.
    • em4_dmips โ€” compile for ARC EM4 DMIPS CPU.
  • em4_fpus โ€” ๐ŸŒธ Compile for the ARC EM4 DMIPS CPU with the single-precision floating-point extension.
  • em4_fpuda โ€” ๐Ÿ’… Compile for the ARC EM4 DMIPS CPU with single-precision floating-point and double assist instructions.
  • hs โ€” โœจ Compile for the ARC HS CPU with no hardware extensions except the atomic instructions. Minimal, but still atomic ๐Ÿ’œ.
  • hs34 โ€” ๐Ÿฆ‹ Compile for the ARC HS34 CPU.
  • hs38 โ€” ๐ŸŽ€ Compile for the ARC HS38 CPU.
  • hs38_linux โ€” ๐Ÿ‘‘ Compile for the ARC HS38 CPU with all hardware extensions on. Full glam mode.
  • hs4x โ€” ๐ŸŒˆ Compile for the ARC HS4x CPU.
  • hs4xd โ€” ๐Ÿ”ฅ Compile for the ARC HS4xD CPU.
  • hs4x_rel31 โ€” โญ Compile for the ARC HS4x CPU release 3.10a.
  • arc600_norm โ€” ๐Ÿ“ Compile for the ARC 600 CPU with "norm" instructions enabled.
  • arc600_mul32x16 โ€” ๐Ÿงš Compile for the ARC 600 CPU with "norm" and 32x16-bit multiply instructions enabled.
  • arc600_mul64 โ€” ๐Ÿ’– Compile for the ARC 600 CPU with "norm" and "mul64"-family instructions enabled.
  • arc601_norm โ€” ๐ŸŒธ Compile for the ARC 601 CPU with "norm" instructions enabled.
  • arc601_mul32x16 โ€” ๐Ÿฆ‹ Compile for the ARC 601 CPU with "norm" and 32x16-bit multiply instructions enabled.
  • arc601_mul64 โ€” ๐Ÿ’œ Compile for the ARC 601 CPU with "norm" and "mul64"-family instructions enabled.
  • nps400 โ€” ๐Ÿซถ Compile for ARC 700 on the NPS400 chip.
  • em_mini โ€” ๐ŸŒ™ Compile for the ARC EM minimalist configuration, which features a reduced register set. Less is more, bestie ๐Ÿค.

  • -mdpfp, -mdpfp-compact โ€” ๐Ÿ’… Generate double-precision FPX instructions, tuned for the compact implementation.
  • -mdpfp-fast โ€” ๐Ÿ”ฅ Generate double-precision FPX instructions, tuned for the fast implementation.
  • -mno-dpfp-lrsr, -mdpfp-lrsr โ€” ๐ŸŽ€ Control whether "lr" and "sr" instructions use FPX extension aux registers. This is enabled by default, so you're already set โœจ.
  • -mea โ€” ๐Ÿ‘‘ Generate extended arithmetic instructions. Currently only "divaw", "adds", "subs", and "sat16" are supported. This is only valid for -mcpu=ARC700, so don't try it elsewhere ๐Ÿ™ˆ.
  • -mmul32x16 โ€” ๐Ÿ’– Generate 32x16-bit multiply and multiply-accumulate instructions.
  • -mmul64 โ€” ๐Ÿงš Generate "mul64" and "mulu64" instructions. Only valid for -mcpu=ARC600, so keep it in its lane ๐Ÿ’….
  • -mnorm โ€” ๐ŸŒˆ Generate "norm" instructions. This is the default if -mcpu=ARC700 is in effect, so it's basically automatic.
  • -mspfp, -mspfp-compact โ€” ๐Ÿฆ‹ Generate single-precision FPX instructions, tuned for the compact implementation.
  • -mspfp-fast โ€” โญ Generate single-precision FPX instructions, tuned for the fast implementation.
  • -msimd โ€” ๐Ÿ”ฅ Enable generation of ARC SIMD instructions via target-specific builtins. Only valid for -mcpu=ARC700. Main character SIMD energy ๐Ÿ’….
  • -msoft-float โ€” ๐Ÿ™ˆ This option is ignored. It's only here for compatibility, bestie. 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. Not the one you're looking for ๐Ÿ’€.
  • -mswap โ€” ๐Ÿ“ Generate "swap" instructions.
  • -matomic โ€” โœจ This enables use of the locked load/store conditional extension to implement atomic memory built-in functions. It's not available for ARC 6xx or ARC EM cores, so skip it there ๐Ÿซถ.
  • -mdiv-rem โ€” ๐Ÿ’œ Enable "div" and "rem" instructions for ARCv2 cores.
  • -mcode-density, -mno-code-density โ€” ๐ŸŒธ Enable code density instructions for ARC EM. This option is on by default for ARC HS, so you only need the flag if you're on EM ๐Ÿงš.
  • -mll64 โ€” ๐Ÿฆ‹ Enable double load/store operations for ARC HS cores.
  • -mtp-regno=regno โ€” ๐Ÿ‘‘ Specify the thread pointer register number.
  • -mbitops โ€” ๐Ÿ”ฅ Enable use of NPS400 bit operations.
  • -mcmem โ€” ๐ŸŒˆ Enable use of the NPS400 xld/xst extension.
  • -mmpy-option=multo โ€” ๐Ÿ’– Compile ARCv2 code with a multiplier design option. You can specify the option using either a string or a numeric value for multo. wlh1 is the default value. The recognized values are:
    • 0 or none โ€” No multiplier available.
    • 1 or w โ€” 16x16 multiplier, fully pipelined. The following instructions are enabled: "mpyw" and "mpyuw".
    • 2 or wlh1 โ€” 32x32 multiplier, fully pipelined (1 stage). The following instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 3 or wlh2 โ€” 32x32 multiplier, fully pipelined (2 stages). The following instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 4 or wlh3 โ€” Two 16x16 multipliers, blocking, sequential. The following instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 5 or wlh4 โ€” One 16x16 multiplier, blocking, sequential. The following instructions are additionally enabled: "mpy", "mpyu", "mpym", "mpymu", and "mpy_s".
    • 6 or wlh5 โ€” One 32x4 multiplier, blocking, sequential. The following 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. ๐ŸŽ€

This option is only available for ARCv2 cores. โญ

  • -mfpu=fpu โ€” ๐Ÿฆ‹ Enables support for specific floating-point hardware extensions for ARCv2 cores. The supported values for fpu are:
    • fpus โ€” Enables support for single-precision floating-point hardware extensions.
    • fpud โ€” Enables support for double-precision floating-point hardware extensions. The single-precision floating-point extension is also enabled. Not available for ARC EM.
    • fpuda โ€” Enables support for double-precision floating-point hardware extensions using double-precision assist instructions. The single-precision floating-point extension is also enabled. This option is only available for ARC EM.
    • fpuda_div โ€” Enables support for 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 option is only available for ARC EM.
    • fpuda_fma โ€” Enables support for 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 option is only available for ARC EM.
    • fpuda_all โ€” Enables support for double-precision floating-point hardware extensions using double-precision assist instructions. All single-precision floating-point hardware extensions are also enabled. This option is only available for ARC EM.
    • fpus_div โ€” Enables support for single-precision floating-point, square-root and divide hardware extensions.
    • fpud_div โ€” Enables support for double-precision floating-point, square-root and divide hardware extensions. This option includes option fpus_div. Not available for ARC EM.
    • fpus_fma โ€” Enables support for single-precision floating-point and fused multiply and add hardware extensions.
    • fpud_fma โ€” Enables support for double-precision floating-point and fused multiply and add hardware extensions. This option includes option fpus_fma. Not available for ARC EM.
    • fpus_all โ€” Enables support for all single-precision floating-point hardware extensions.
    • fpud_all โ€” Enables support for all single- and double-precision floating-point hardware extensions. Not available for ARC EM.
  • -mirq-ctrl-saved=register-range, blink, lp_count โ€” ๐Ÿ›ก๏ธ Specifies the general-purpose registers that the processor automatically saves and restores on interrupt entry and exit. The register-range is written as two registers separated by a dash. The range always starts with "r0", and the upper limit is the "fp" register. blink and lp_count are optional. This option is only valid for ARC EM and ARC HS cores. ๐Ÿ’œ
  • -mrgf-banked-regs=number โ€” ๐ŸŒธ Specifies the number of registers replicated in the second register bank on entry to a fast interrupt. Fast interrupts are interrupts with the highest priority level, P0. These interrupts save only the PC and STATUS32 registers, so they avoid memory transactions during interrupt entry and exit sequences. Use this option when you're using fast interrupts on an ARC V2 family processor. Permitted values are 4, 8, 16, and 32. โšก
  • -mlpc-width=width โ€” ๐Ÿงฎ Specify the width of the "lp_count" register. Valid values for width are 8, 16, 20, 24, 28, and 32 bits. The default width is fixed to 32 bits. If the width is less than 32, the compiler does not attempt to transform loops in your program to use the zero-delay loop mechanism unless it is known that the "lp_count" register can hold the required loop-counter value. Depending on the width specified, the compiler and run-time library might continue to use the loop mechanism for various needs. This option defines the macro __ARC_LPC_WIDTH__ with the value of width. ๐Ÿฆ‹
  • -mrf16 โ€” ๐Ÿ“ This option instructs the compiler to generate code for a 16-entry register file. This option defines the __ARC_RF16__ preprocessor macro. โœจ
  • -mbranch-index โ€” ๐Ÿงš Enable use of "bi" or "bih" instructions to implement jump tables. ๐ŸŒˆ

These next ones get passed down to the assembler and also define preprocessor macro symbols, so they're doing double duty ๐Ÿ’….

  • -mlock โ€” ๐Ÿ”’ Passed down to the assembler to enable the locked load/store conditional extension. It also sets the preprocessor symbol __Xlock.
  • -mswape โ€” ๐Ÿ“ Passed down to the assembler to enable the swap byte ordering extension instruction. It also sets the preprocessor symbol __Xswape.
  • -mxy โ€” ๐Ÿ’– Passed down to the assembler to enable the XY memory extension. It also sets the preprocessor symbol __Xxy.

And this one's all about how the assembly code gets annotated ๐Ÿ“.

  • -misize โ€” ๐Ÿซง Annotate assembler instructions with estimated addresses.

Next up, the options that get passed through to the linker ๐Ÿ”—:

  • -marclinux, -mno-arclinux โ€” okay so this one passes stuff through to the linker and tells it to use the "arclinux" emulation ๐Ÿ’… It's on by default in toolchains built for arc-linux-uclibc and arceb-linux-uclibc targets, but only when profiling is not requested ๐ŸŒธ
  • -marclinux_prof, -mno-arclinux_prof โ€” same vibe, but for the "arclinux_prof" emulation ๐Ÿ”ฎ This one is on by default for those same arc-linux-uclibc and arceb-linux-uclibc targets, except this time it's when profiling is requested โœจ

Next up, these options control the semantics of generated code ๐Ÿง 

  • -mlong-calls โ€” calls get generated as register indirect calls, which gives you access to the full 32-bit address range ๐Ÿš€ main character energy, no limits ๐Ÿ‘‘
  • -mmedium-calls, -mno-medium-calls โ€” ngl this one is a whole mood ๐Ÿฆ‹ It stops calls from using less than the 25-bit addressing range, which is the offset an unconditional branch-and-link instruction gets. Conditional execution of function calls gets suppressed so you can use that 25-bit range, instead of the 21-bit range you'd get with conditional branch-and-link ๐Ÿ’– This is the default for toolchains built for arc-linux-uclibc and arceb-linux-uclibc targets ๐ŸŒˆ
  • -G num โ€” externally-visible data goes into a small data section, as long as it's no bigger than num bytes ๐Ÿ“ The default for num is 4 for any ARC configuration, or 8 when you've got double load/store operations ๐Ÿ’œ
  • -mno-sdata โ€” don't generate sdata references ๐Ÿ™…โ€โ™€๏ธ This is the default for toolchains built for arc-linux-uclibc and arceb-linux-uclibc targets ๐Ÿซถ
  • -mvolatile-cache, -mno-volatile-cache โ€” this controls how volatile references get accessed ๐Ÿงš The default is -mvolatile-cache, which uses ordinary cached memory accesses for volatile references. If you want cache bypass for volatile references instead, go with -mno-volatile-cache ๐ŸŒธ

Next, these options fine tune code generation ๐Ÿ’…

  • -mauto-modify-reg โ€” turns on the use of pre/post modify with register displacement โœจ
  • -mno-brcc, -mbrcc โ€” this one controls a target-specific pass in arc_reorg that generates compare-and-branch (brcc) instructions ๐Ÿ”ฅ It's enabled by default. Heads up, it has no effect on generation of these instructions when they're driven by the combiner pass ๐Ÿ™ˆ
  • -mcase-vector-pcrel โ€” uses PC-relative switch case tables so case table shortening can happen ๐Ÿฆ‹ This is the default for -Os ๐Ÿ’–
  • -mno-cond-exec โ€” disables the ARCompact-specific pass that generates conditional execution instructions ๐Ÿ’€ Okay real talk, the target-independent pass for conditional execution is often lacking, because of delay slot scheduling and interactions between operand numbers, literal sizes, instruction lengths, and conditional execution support. 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, but it costs extra compilation time, which is why there's an option to switch it off ๐Ÿ“ If call instructions are 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 ๐ŸŒˆ
  • -mindexed-loads โ€” enables the use of indexed loads ๐ŸŽ€ This can be problematic though, because some optimizers then assume indexed stores exist, and that's not the case ๐Ÿ˜ญ
  • -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 you're optimizing for size with -Os, prologues and epilogues that have to save or restore a big pile of registers often get shortened by calling a special function in libgcc, which is called a millicode call ๐Ÿงš But these calls can cause performance issues, and/or linking issues when you link in a nonstandard way, so 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, so don't try it elsewhere ๐Ÿ™…โ€โ™€๏ธ
  • -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 get used opportunistically ๐ŸŒท
    • 2 โ€” on top of that, alignment of loops and of code after barriers gets dropped ๐Ÿƒ
    • 3 โ€” on top of that, optional data alignment gets dropped, and the option Os is enabled ๐Ÿ‘‘

This defaults to 3 when -Os is in effect. Otherwise, when this isn't set, the behavior 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 ARC600 CPU ๐Ÿงธ
    • ARC601 โ€” tunes for ARC601 CPU ๐Ÿงธ
    • ARC700 โ€” tunes for ARC700 CPU with standard multiplier block โœจ
    • ARC700-xmac โ€” tunes for ARC700 CPU with XMAC block ๐Ÿ”ฅ
    • ARC725D โ€” tunes for ARC725D CPU ๐ŸŒˆ
    • ARC750D โ€” tunes for ARC750D CPU ๐Ÿ’–
    • core3 โ€” tunes for 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 ๐Ÿ’œ

๐Ÿฆ‹ ARM Options ๐Ÿฆ‹

These -m options are defined for the ARM port ๐ŸŒธ

  • -mabi=name โ€” generates code for the specified ABI ๐ŸŽ€ Permissible values are: apcs-gnu, atpcs, aapcs and aapcs-linux โœจ
  • -mapcs-frame โ€” generates a stack frame that's compliant with the ARM Procedure Call Standard for all functions, even if it's not strictly necessary for the code to run correctly ๐Ÿงฑ If you specify -fomit-frame-pointer with this option, stack frames don't get generated for leaf functions ๐Ÿƒ The default is -mno-apcs-frame. Heads up, this option is deprecated ๐Ÿ˜ญ
  • -mapcs โ€” a synonym for -mapcs-frame, and it's deprecated too ๐Ÿ™ˆ
  • -mthumb-interwork โ€” generates 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, since slightly larger code gets generated when -mthumb-interwork is specified ๐Ÿ“ In AAPCS configurations this option is meaningless ๐Ÿซ 
  • -msched-prolog, -mno-sched-prolog โ€” allows or prevents reordering of instructions in the function prologue, or merging those instructions with the instructions in the function's body ๐Ÿง  With -mno-sched-prolog, all functions start with a recognizable set of instructions (or one of a choice from a small set of different function prologues), and that info can be used to locate the start of functions inside an executable piece of code ๐Ÿ” The default is -msched-prolog ๐Ÿ’–
  • -mfloat-abi=name โ€” specifies which floating-point ABI to use ๐Ÿงฎ Permissible values are: soft, softfp and hard ๐ŸŒˆ
    • soft makes GCC generate output containing library calls for floating-point operations ๐Ÿ“š
    • softfp allows code generation using hardware floating-point instructions, but still uses the soft-float calling conventions ๐Ÿ› ๏ธ
    • hard allows generation of floating-point instructions and uses FPU-specific calling conventions โšก

The default depends on the specific target configuration ๐ŸŽฏ > Note that the hard-float and soft-float ABIs are not link-compatible. You have to compile your entire program with the same ABI, and link with a compatible set of libraries, or it's gonna be a whole disaster ๐Ÿ’€

  • -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 code for a processor running in little-endian mode ๐Ÿ’œ This is the default for all standard configurations ๐ŸŒธ
  • -mbig-endian โ€” generates code for a processor running in big-endian mode. The default is to compile code for a little-endian processor, so you have to ask for this one explicitly ๐ŸŒŠ
  • -mbe8, -mbe32 โ€” when linking a big-endian image, these let you select between BE8 and BE32 formats ๐Ÿงฉ The option has no effect for little-endian images and is ignored ๐Ÿ™…โ€โ™€๏ธ The default depends on the selected target architecture. For ARMv6 and later architectures the default is BE8, and for older architectures the default is BE32 ๐Ÿ•ฐ๏ธ Heads up, BE32 format has been deprecated by ARM ๐Ÿ˜ญ
  • -march=name[+extension...] โ€” specifies the name of the target ARM architecture ๐Ÿน GCC uses this name to figure out what kind of instructions it can emit when generating assembly code ๐Ÿง‘โ€๐Ÿ’ป You can use this option together with -mcpu=, or instead of it ๐Ÿซถ

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 ๐Ÿงพ

Additionally, the following architectures, which lack support for the Thumb execution state, are recognized but support is deprecated: armv4 ๐Ÿฅ€

Lots of the architectures support extensions ๐Ÿงฉ You can add these by appending +extension to the architecture name. Extension options get processed in order, and capabilities accumulate. An extension will also enable any necessary base extensions it depends on. For example, the +crypto extension will always enable the +simd extension ๐Ÿ” The exception to the additive construction is for extensions prefixed with +no...: these disable the specified option and any other extensions that may depend on the presence of that extension ๐Ÿšซ

For example, -march=armv7-a+simd+nofp+vfpv4 is equivalent to writing -march=armv7-a+vfpv4, since the +simd option is entirely disabled by the +nofp option that follows it ๐Ÿซ โœจ

Okay bestie, most of these extension names are super generic ๐Ÿฅน but what they actually do depends on which architecture you put them on โœจ Like, +simd works on both armv7-a and armv8-a, but on armv7-a it turns on the original ARMv7-A Advanced SIMD (Neon) extensions, and on armv8-a you get the ARMv8-A version ๐Ÿ’…

Here's the tea ๐Ÿซ– The list below shows which extensions each architecture supports. If an architecture isn't mentioned, it straight up supports zero extensions ๐Ÿ’€

  • armv5te, armv6, armv6j, armv6k, armv6kz, armv6t2, armv6z, armv6zk ๐Ÿงš these all share the same two extensions:
    • +fp โ€” The VFPv2 floating-point instructions. +vfpv2 works as an alias for this one, so you can use either.
    • +nofp โ€” Turns the floating-point instructions off.
  • 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. +vfpv3-d16 is an alias for this one. Heads up, bestie: the base ARMv7-M architecture doesn't support floating-point, but it's compatible with both ARMv7-A and ARMv7-R.
    • +nofp โ€” Turns the floating-point instructions off.
  • armv7-a ๐Ÿ‘‘ the main character of the ARMv7 family, with the most extensions ๐Ÿ”ฅ
    • +mp โ€” The multiprocessing extension.
    • +sec โ€” The security extension.
    • +fp โ€” The VFPv3 floating-point instructions, with 16 double-precision registers. +vfpv3-d16 is an alias for this one.
    • +simd โ€” The Advanced SIMD (Neon) v1 and the VFPv3 floating-point instructions. +neon and +neon-vfpv3 can both be used as aliases for this one.
    • +vfpv3 โ€” The VFPv3 floating-point instructions, but with 32 double-precision registers.
    • +vfpv3-d16-fp16 โ€” The VFPv3 floating-point instructions with 16 double-precision registers, plus the half-precision floating-point conversion operations.
    • +vfpv3-fp16 โ€” The VFPv3 floating-point instructions with 32 double-precision registers, plus 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, plus 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. Floating-point stays on, though ๐Ÿ™Œ
    • +nofp โ€” Disables the floating-point and Advanced SIMD instructions.
  • armv7ve ๐Ÿฆ‹ the extended version of ARMv7-A, with support for virtualization.
    • +fp โ€” The VFPv4 floating-point instructions, with 16 double-precision registers. +vfpv4-d16 is an alias for this one.
    • +simd โ€” The Advanced SIMD (Neon) v2 and the VFPv4 floating-point instructions. +neon-vfpv4 is 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, plus the half-precision floating-point conversion operations.
    • +vfpv3-fp16 โ€” The VFPv3 floating-point instructions with 32 double-precision registers, plus 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. +neon-vfpv3 is an alias for this one.
    • +neon-fp16 โ€” The Advanced SIMD (Neon) v1 and the VFPv3 floating-point instructions, plus the half-precision floating-point conversion operations.
    • +nosimd โ€” Disables the Advanced SIMD instructions. Floating-point stays on ๐Ÿซถ
    • +nofp โ€” Disables the floating-point and Advanced SIMD instructions.
  • 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.
    • +nofp โ€” Disables the floating-point, Advanced SIMD and cryptographic instructions.
    • +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 also turns on the Advanced SIMD and floating-point instructions.
    • +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, armv8.3-a ๐Ÿ’œ these two share the same list:
    • +fp16 โ€” The half-precision floating-point data processing instructions. This also turns on the Advanced SIMD and floating-point instructions.
    • +fp16fml โ€” The half-precision floating-point fmla extension. This also turns on 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 turns on the Advanced SIMD and floating-point instructions.
    • +dotprod โ€” Enables the Dot Product extension. This also turns on the 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 turns on the Advanced SIMD and floating-point instructions.
    • +bf16 โ€” The Brain half-precision floating-point instructions. This also turns on the Advanced SIMD and floating-point instructions.
  • armv8.4-a ๐Ÿ”ฅ
    • +fp16 โ€” The half-precision floating-point data processing instructions. This also turns on the Advanced SIMD and floating-point instructions, plus the Dot Product extension and the half-precision floating-point fmla extension.
    • +simd โ€” The ARMv8.3-A Advanced SIMD and floating-point instructions, plus the Dot Product extension.
    • +crypto โ€” The cryptographic instructions. This also turns on the Advanced SIMD and floating-point instructions, plus 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 turns on the Advanced SIMD and floating-point instructions.
    • +bf16 โ€” The Brain half-precision floating-point instructions. This also turns on the Advanced SIMD and floating-point instructions.
  • armv8.5-a ๐ŸŒธ
    • +fp16 โ€” The half-precision floating-point data processing instructions. This also turns on the Advanced SIMD and floating-point instructions, plus the Dot Product extension and the half-precision floating-point fmla extension.
    • +simd โ€” The ARMv8.3-A Advanced SIMD and floating-point instructions, plus the Dot Product extension.
    • +crypto โ€” The cryptographic instructions. This also turns on the Advanced SIMD and floating-point instructions, plus 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 turns on the Advanced SIMD and floating-point instructions.
    • +bf16 โ€” The Brain half-precision floating-point instructions. This also turns on the Advanced SIMD and floating-point instructions.
  • armv8.6-a ๐Ÿงš
    • +fp16 โ€” The half-precision floating-point data processing instructions. This also turns on the Advanced SIMD and floating-point instructions, plus the Dot Product extension and the half-precision floating-point fmla extension.
    • +simd โ€” The ARMv8.3-A Advanced SIMD and floating-point instructions, plus the Dot Product extension.
  • +crypto โ€” ๐Ÿ” The cryptographic instructions, and this one also flips on the Advanced SIMD and floating-point instructions plus the Dot Product extension โœจ
  • +nocrypto โ€” ๐Ÿ™… Disable the cryptographic extension
  • +nofp โ€” ๐Ÿ’€ Disable the floating-point, Advanced SIMD and cryptographic instructions. Everything spicy, gone
  • +i8mm โ€” ๐Ÿงฎ The 8-bit Integer Matrix Multiply instructions. This one also enables Advanced SIMD and floating-point instructions
  • +bf16 โ€” ๐Ÿง  The Brain half-precision floating-point instructions. This one also enables Advanced SIMD and floating-point instructions

๐Ÿฆ‹ armv7-r

  • +fp.sp โ€” ๐ŸŽ€ The single-precision VFPv3 floating-point instructions. +vfpv3xd works as an alias for this extension too
  • +fp โ€” ๐Ÿ’œ The VFPv3 floating-point instructions with 16 double-precision registers. +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 โ€” ๐Ÿ™… Disable the floating-point extension
  • +idiv โ€” โž— The ARM-state integer division instructions
  • +noidiv โ€” ๐Ÿซ  Disable 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 โ€” ๐Ÿ™… Disable 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 โ€” ๐Ÿ™… Disable the floating-point extension
  • +cdecp0, +cdecp1, ... , +cdecp7 โ€” ๐Ÿ”ง Enable the Custom Datapath Extension (CDE) on selected coprocessors, according to the numbers given in the options in the range 0 to 7
  • +pacbti โ€” ๐Ÿ›ก๏ธ Enable the Pointer Authentication and Branch Target Identification Extension

๐Ÿ‘‘ armv8-m.main

  • +dsp โ€” ๐ŸŽถ The DSP instructions
  • +nodsp โ€” ๐Ÿ™… Disable the DSP extension
  • +fp โ€” โญ The single-precision floating-point instructions
  • +fp.dp โ€” โญ The single- and double-precision floating-point instructions
  • +nofp โ€” ๐Ÿ™… Disable the floating-point extension
  • +cdecp0, +cdecp1, ... , +cdecp7 โ€” ๐Ÿ”ง Enable 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 โ€” ๐Ÿ™… Disable the cryptographic instructions
  • +nofp โ€” ๐Ÿ’€ Disable the floating-point, Advanced SIMD and cryptographic instructions

-march=native causes the compiler to auto-detect the architecture of the build computer. โœจ At present, this feature is only supported on GNU/Linux, and not all architectures are recognized. If the auto-detect is unsuccessful, the option has no effect ๐Ÿ™ˆ

-march=unset causes the compiler to ignore any -march=... options that appear earlier on the command line and behave as if the option was never passed. ๐Ÿงน This is useful to avoid warnings about conflicting CPU and architecture options when the two produce different architecture specifications.


๐Ÿ’– -mtune=name

This option specifies the name of the target ARM processor that GCC should tune the performance of the code for. ๐ŸŽฏ For some ARM implementations, you can get better performance by using this option. 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, xgene1.

Okay, and it gets even better ๐Ÿญ This option can also tell GCC to tune the performance of the code for a big.LITTLE system. Permissible 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, cortex-a76.cortex-a55.

-mtune=generic-arch tells GCC to tune the performance for a blend of processors within architecture arch. ๐ŸŒˆ The goal is code that runs well on the most popular processors right now, balancing optimizations that benefit some CPUs in the range while avoiding performance pitfalls of the other CPUs. The effects of this option may change in future GCC versions as CPU models come and go ๐Ÿ•ฐ๏ธ

-mtune permits the same extension options as -mcpu, but the extension options do not affect the tuning of the generated code ๐Ÿคท

-mtune=native causes the compiler to auto-detect the CPU of the build computer. โœจ At present, this feature is only supported on GNU/Linux, and not all architectures are recognized. If the auto-detect is unsuccessful, the option has no effect ๐Ÿ™ˆ

๐ŸŽ€ -mcpu=name[+extension...]

This specifies the name of the target ARM processor. ๐Ÿ‘‘ GCC uses this name to work out the target ARM architecture (as if you'd specified -march) and the ARM processor type to tune for performance (as if you'd specified -mtune). If you use this option together with -march or -mtune, those options take precedence over the matching part of this option ๐Ÿ’…

Lots of the supported CPUs have optional architectural extensions. ๐Ÿงฉ When that's the case, the extensions are normally enabled by default. If implementations that lack the extension exist, you can use the extension syntax to disable the ones that have been left out. For floating-point and Advanced SIMD (Neon) instructions, you also have to think about the settings of -mfloat-abi and -mfpu: floating-point and Advanced SIMD instructions will only be used if -mfloat-abi is not set to soft, and any setting of -mfpu other than auto will override the available floating-point and SIMD extension instructions. ๐Ÿซถ

For example, cortex-a9 comes in three major configurations: integer only, with just a floating-point unit, or with floating-point and Advanced SIMD. The default is to enable all the instructions, but the extensions +nosimd and +nofp can be used to disable just the SIMD or both the SIMD and floating-point instructions, respectively ๐Ÿฆ‹

Permissible names for this option are the same as those for -mtune.

The following extension options are common to the listed CPUs ๐Ÿ“

  • +nodsp โ€” ๐Ÿ™… Disable the DSP instructions on cortex-m33, cortex-m35p, cortex-m52, cortex-m55 and cortex-m85. It also disables the M-Profile Vector Extension (MVE) integer and single precision floating-point instructions on cortex-m52, cortex-m55 and cortex-m85
  • +nopacbti โ€” ๐Ÿ™… Disable the Pointer Authentication and Branch Target Identification Extension on cortex-m52 and cortex-m85
  • +nomve โ€” ๐Ÿ™… Disable the M-Profile Vector Extension (MVE) integer and single precision floating-point instructions on cortex-m52, cortex-m55 and cortex-m85
  • +nomve.fp โ€” ๐Ÿ™… Disable the M-Profile Vector Extension (MVE) single precision floating-point instructions on cortex-m52, cortex-m55 and cortex-m85
  • +cdecp0, +cdecp1, ... , +cdecp7 โ€” ๐Ÿ”ง Enable the Custom Datapath Extension (CDE) on selected coprocessors, according to 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. It also disables the floating-point and SIMD instructions 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 ๐Ÿ˜ญ
  • +nofp.dp โ€” okay so this one switches OFF the double-precision bit of the floating-point instructions on cortex-r5, cortex-r7, cortex-r8, cortex-r52, cortex-r52plus and cortex-m7 ๐Ÿ’…
  • +nosimd โ€” this one turns off the SIMD instructions (but keeps the floating-point ones) on generic-armv7-a, cortex-a5, cortex-a7 and cortex-a9 ๐ŸŒธ
  • +crypto โ€” bestie, this one 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, neon-vfpv4. They mean the same thing as the extensions to -march=armv7-a โœจ.

-mcpu=generic-arch is also allowed, and it's the same as -march=arch -mtune=generic-arch. Check out -mtune for more deets ๐Ÿงš.

-mcpu=native tells the compiler to auto-detect the CPU of the build computer ๐Ÿ‘‘. Right now this only works on GNU/Linux, and not every architecture gets recognized. If the auto-detect doesn't work out, the option does nothing ๐Ÿ™ˆ.

-mcpu=unset makes the compiler ignore any -mcpu=... options that show up earlier on the command line, so it acts like that option was never passed at all ๐Ÿซถ. This is super useful for avoiding warnings about conflicting CPU and architecture options when the two give different architecture specs ๐Ÿ’œ.


-mfpu=name ๐ŸŽ€

This picks 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. Also, neon is an alias for neon-vfpv3, and vfp is an alias for vfpv2 ๐ŸŒˆ.

The setting 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 ๐Ÿ’….

Heads up, bestie: if the selected floating-point hardware includes the NEON extension (e.g. -mfpu=neon), floating-point operations are NOT generated by GCC's auto-vectorization pass unless -funsafe-math-optimizations is also specified ๐Ÿ˜ญ. This is because NEON hardware doesn't fully implement the IEEE 754 standard for floating-point arithmetic (in particular, denormal values are treated as zero), so using NEON instructions may lead to a loss of precision ๐Ÿ’€.

You can also set the fpu name at function level by using the target("fpu=") function attributes or pragmas ๐Ÿ“.

-mfp16-format=name ๐ŸŒธ

This specifies the format of the "__fp16" half-precision floating-point type ๐Ÿฆ‹. The permissible names are none, ieee, and alternative. The default is none, which means the "__fp16" type is not defined at all ๐Ÿ™ˆ.

-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 โœจ. The 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 ๐Ÿ‘‘.

Bigger numbers can produce faster, more efficient code, but they can also make your program bigger ๐Ÿงš. Different values are potentially incompatible ๐Ÿ’…. Code compiled with one value can't necessarily be expected to work with code or libraries compiled with another value, if they swap info using structures or unions ๐Ÿ˜ญ.

This option is deprecated, so maybe don't build your life around it ๐Ÿ’€.

-mabort-on-noreturn ๐ŸŒˆ

This generates a call to the function abort at the end of a noreturn function ๐Ÿ”ฅ. It runs if the function tries to return โญ.

-mlong-calls, -mno-long-calls ๐Ÿ’œ

These tell the compiler to make 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 lives outside the 64-megabyte addressing range of the offset-based version of the subroutine call instruction ๐ŸŒธ.

Even with this switch on, not every function call gets turned into a long call ๐Ÿ™ˆ. The heuristic is that static functions, functions with the short_call attribute, functions inside the scope of a #pragma no_long_calls directive, and functions whose definitions have already been compiled within the current compilation unit are NOT turned into long calls ๐Ÿ’…. The exceptions are that weak function definitions, functions with the long_call attribute or the section attribute, and functions inside the scope of a #pragma long_calls directive are ALWAYS turned into long calls ๐Ÿ˜ญ.

This feature is not enabled by default โœจ. Specifying -mno-long-calls restores the default behavior, and so does putting the function calls inside the scope of a #pragma long_calls_off directive ๐Ÿซถ. Heads up that these switches have no effect on how the compiler generates code for handling function calls via function pointers ๐Ÿ’€.

-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 this register with an appropriate value before execution begins ๐ŸŽ€.

-mpic-register=reg ๐Ÿ“

This specifies the register to be used for PIC addressing ๐Ÿ’–. For the standard PIC base case, the default is any suitable register the compiler picks. For the single PIC base case, the default is R9 if the target is EABI based or stack-checking is enabled, and otherwise the default is R10 ๐ŸŒˆ.

-mpic-data-is-text-relative โญ

This assumes the displacement between the text and data segments is fixed at static link time ๐Ÿ”ฅ. That lets you use PC-relative addressing operations to access data known to be in the data segment ๐Ÿฆ‹. For non-VxWorks RTP targets, this option is enabled by default. When it's disabled on those targets, it enables -msingle-pic-base by default ๐Ÿ’œ.

-mpoke-function-name ๐ŸŒธ

This writes the name of each function into the text section, directly before the function prologue ๐Ÿ™ˆ. The generated code looks kinda 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 doing a stack backtrace, code can inspect the value of pc stored at fp + 0 ๐Ÿซถ. If the trace function then looks at location pc - 12 and the top 8 bits are set, then we know there's a function name embedded right before this location, with length ((pc[-3]) & 0xff000000) ๐Ÿ”ฅ.

-mthumb, -marm ๐Ÿฆ‹

These let you pick between generating code that runs in ARM state and code that runs in Thumb state ๐Ÿ’…. The default for most configurations is ARM state, but you can change that by configuring GCC with the --with-mode=state configure option ๐Ÿ‘‘.

You can also override ARM and Thumb mode for each function by using the target("thumb") and target("arm") function attributes or pragmas โœจ.

-mtpcs-frame ๐ŸŽ€

This generates a stack frame that's compliant with 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 โญ

This generates a stack frame that's compliant with the Thumb Procedure Call Standard for all leaf functions ๐Ÿงš. (A leaf function is one that doesn't call any other functions.) The default is -mno-apcs-leaf-frame ๐Ÿ™ˆ.

-mcallee-super-interworking ๐Ÿ’œ

This gives all externally visible functions in the file being compiled an ARM instruction set header, which switches to Thumb mode before executing the rest of the function ๐ŸŒธ. That way these functions can be called from non-interworking code ๐Ÿซถ. This option is not valid in AAPCS configurations, because interworking is already enabled by default ๐Ÿ’€.

-mcaller-super-interworking ๐Ÿ”ฅ

This lets calls via function pointers (including virtual functions) execute correctly, whether or not the target code was compiled for interworking ๐Ÿฆ‹. There's a small overhead in the cost of executing a function pointer when this option is enabled ๐Ÿ˜ญ. This option is not valid in AAPCS configurations, because interworking is already enabled by default ๐Ÿ‘‘.

-mtp=name ๐ŸŒˆ

This specifies the access model for the thread local storage pointer โœจ. The model soft generates calls to __aeabi_read_tp. The other accepted models are tpidrurw, tpidruro and tpidrprw, which fetch the thread pointer directly from the corresponding system register (supported from the arm6k architecture and later) ๐Ÿ’…. These system registers are accessed through the CP15 co-processor interface, and the argument cp15 is also accepted as a convenience alias of tpidruro ๐ŸŽ€.

  • soft โ€” calls __aeabi_read_tp
  • tpidrurw, tpidruro, tpidrprw โ€” read the thread pointer straight from the system register (arm6k and later) ๐Ÿฆ‹
  • cp15 โ€” a convenience alias of tpidruro ๐Ÿ’–
  • auto โ€” uses the best available method for the selected processor ๐Ÿงš

The default setting is auto ๐Ÿซถ.

-mtls-dialect=dialect ๐Ÿ’œ

This specifies the dialect to use for accessing thread local storage ๐ŸŒธ. Two dialects are supported: 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 gives better performance for shared libraries ๐Ÿ‘‘. The GNU descriptor scheme is compatible with the original scheme, but it does require new assembler, linker and library support ๐Ÿ˜ญ. Initial and local exec TLS models are unaffected by this option and always use the original scheme โœจ.

-mword-relocations ๐Ÿ”ฅ

This only generates absolute relocations on word-sized values (i.e. R_ARM_ABS32) ๐Ÿฆ‹. It's enabled by default on targets (uClinux, SymbianOS) where the runtime loader imposes this restriction, and when -fpic or -fPIC is specified ๐ŸŒˆ.

Watch out: this option conflicts with -mslow-flash-data ๐Ÿ’€.

-mfix-cortex-m3-ldrd Some Cortex-M3 cores can corrupt data when ldrd instructions have overlapping destination and base registers. This option stops GCC from generating those instructions, and it's switched on 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 Turns the mitigation for an erratum on Cortex-A57 and Cortex-A72 that messes with the AES crypto instructions on or off. It's on by default when either -mcpu=cortex-a57 or -mcpu=cortex-a72 is specified. ๐Ÿ”๐Ÿ’…

-munaligned-access -mno-unaligned-access Lets you turn reading and writing of 16- and 32-bit values from addresses that aren't 16- or 32-bit aligned on (or off). By default it's off for all pre-ARMv6, all ARMv6-M, and ARMv8-M Baseline architectures, and on for all other architectures. 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. If unaligned access is enabled, the preprocessor symbol __ARM_FEATURE_UNALIGNED is also defined. ๐Ÿท๏ธ

-mslow-flash-data Assumes loading data from flash is slower than fetching instructions, so literal loads get minimized for better performance. This 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. Right now it's off by default, which means divided syntax. This option has no impact on Thumb2, but that might change in future GCC releases. Divided syntax should be considered deprecated, so bye bye ๐Ÿ‘‹๐Ÿ’€

-mrestrict-it Restricts IT block generation so it follows the ARMv8-A rules. IT blocks can only contain a single 16-bit instruction from a select set of instructions. This is on by default for ARMv8-A Thumb mode. ๐ŸŽ€

-mpure-code Stops constant data from being placed in code sections. Also, when compiling for ELF object format, it gives all text sections the ELF processor-specific section attribute SHF_ARM_PURECODE. This is only available when generating non-PIC code for M-profile targets. ๐ŸŒธ

-mcmse Generates secure code according to the "ARMv8-M Security Extensions: Requirements on Development Tools Engineering Specification", which you can find at <https://developer.arm.com/documentation/ecm0359818/latest/>. Very official, very important ๐Ÿ“œ๐Ÿ”’

-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 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. The supported locations are global for a global canary, or tls for a canary accessible through the TLS register. The option -mstack-protector-guard-offset= is for use with -fstack-protector-guard=tls, and it is not for use in user-land code. ๐Ÿง 

-mfdpic -mno-fdpic Selects 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 none of the PIC/PIE-related options are provided. On other targets, it only enables the FDPIC-specific code generation features, so you'll need to explicitly provide the PIC/PIE-related options as needed. ๐Ÿฆ‹

Static linking isn't supported, because it would still involve the dynamic linker when the program self-relocates. If that behavior is acceptable to you, use -static and -Wl,-dynamic-linker options. ๐Ÿ™ˆ

The opposite option, -mno-fdpic, is useful (and required) for building the Linux kernel with the same (arm-*-uclinuxfdpiceabi) toolchain you used to build userland programs. ๐Ÿง

-mbranch-protection=features Enables branch protection features (armv8.1-m.main only). โœจ

features can be one of the following forms:

none generates code without branch protection or return address signing. ๐Ÿšซ

standard generates code with all branch protection features enabled at their standard level. ๐Ÿ’ฏ

pac-ret generates code with return address signing set to its standard level, which means signing all functions that save the return address to memory. ๐Ÿ”

pac-ret+leaf extends pac-ret signing to include leaf functions, even if they don't write the return address to memory. ๐Ÿƒ

bti adds landing-pad instructions at the permitted targets of indirect branch instructions. ๐Ÿ›ฌ

If support for the +pacbti architecture extension isn't enabled (for example, via -march=), then all branch protection and return address signing operations are constrained to use only 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 be turned on at runtime on processors that support the PACBTI extension. ๐Ÿงš

Branch target enforcement using BTI can only be enabled at runtime if all code in the application has been compiled with at least -mbranch-protection=bti.

Any setting other than none is supported only on armv8-m.main or later. ๐Ÿ’œ

The default is to generate code without branch protection or return address signing. ๐ŸŒˆ

-mvectorize-with-neon-quad -mvectorize-with-neon-double Controls whether vectorization uses NEON quad-word or double-word registers. The default is -mvectorize-with-neon-quad. ๐Ÿ“

AVR Options

These options are defined for AVR implementations: ๐Ÿฆ–

-mmcu=mcu Specifies the AVR instruction set architecture (ISA) or device type. The default for this option is avr2. ๐ŸŽฏ

Here's the lineup of AVR devices and ISAs that are supported. Heads up, bestie: a complete device support setup needs startup code crtmcu.o, a device header avr/io*.h, a device library libmcu.a, and a device-specs file specs-mcu (see <https://gcc.gnu.org/wiki/avr-gcc#spec-files>). Only the specs file is provided by the compiler, according to the supported mcus 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>) supplied by the hardware manufacturer. ๐Ÿ“ฆ๐Ÿ’–

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. 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, the glow-up crew โœจ. mcu values: atmega128, atmega128a, atmega128rfa1, atmega128rfr2, atmega1280, atmega1281, atmega1284, atmega1284p, atmega1284rfr2, at90can128, at90usb1286, at90usb1287.
  • avr6 โ€” ๐Ÿฆ‹ "Enhanced" devices with a 3-byte PC, so anything with more than 128 KiB of program memory. mcu values: atmega256rfr2, atmega2560, atmega2561, atmega2564rfr2.
  • avrxmega2 โ€” ๐ŸŒธ "XMEGA" devices with more than 8 KiB and up to 64 KiB of program memory. mcu values: 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. Major main-character energy ๐Ÿ‘‘. mcu values: 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, avr16la14, avr16la20, avr16la28, avr16la32, avr32da28, avr32da28s, avr32da32, avr32da32s, avr32da48, avr32da48s, avr32db28, avr32db32, avr32db48, avr32dd14, avr32dd20, avr32dd28, avr32dd32, avr32du14, avr32du20, avr32du28, avr32du32, avr32ea28, avr32ea32, avr32ea48, avr32eb14, avr32eb20, avr32eb28, avr32eb32, avr32la14, avr32la20, avr32la28, avr32la32, avr32sd20, avr32sd28, avr32sd32.
  • avrxmega4 โ€” ๐Ÿ“ "XMEGA" devices with more than 64 KiB and up to 128 KiB of program memory. mcu values: 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 values: atxmega64a1, atxmega64a1u.
  • avrxmega6 โ€” ๐Ÿ’… "XMEGA" devices with more than 128 KiB of program memory. mcu values: 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 values: atxmega128a1, atxmega128a1u, atxmega128a4u.
  • avrtiny โ€” ๐ŸŽ€ "Reduced Tiny" Tiny core devices with only 16 general purpose registers and 512 B up to 4 KiB of program memory. mcu values: attiny4, attiny5, attiny9, attiny10, attiny102, attiny104, attiny20, attiny40.
  • avr1 โ€” ๐Ÿ™ˆ This ISA is implemented by the minimal AVR core and is only supported for the assembler, so no compiling for it. mcu values: attiny11, attiny12, attiny15, attiny28, at90s1200.
  • -mabsdata โ€” ๐Ÿซถ Assume that all data in static storage can be reached with LDS / STS instructions. This only does anything on reduced Tiny devices like ATtiny40.
    • See also the "absdata" AVR Attributes,variable attribute.
  • -mcvt โ€” ๐ŸŒˆ Use a compact vector table. Some devices support a CVT with only four entries: 0=Reset, 1=NMI, 2=Prio1 IRQ, 3=Prio0 IRQs. This option links the startup code from crtmcu-cvt.o instead of the usual crtmcu.o. Besides giving you the compact table, the startup code sets the bit CPUINT_CTRLA.CPUINT_CVT, which turns on the CVT on the device.
    • When you don't want the startup code to set CPUINT_CTRLA.CPUINT_CVT, you can satisfy the symbol __init_cvt so that the code isn't pulled in from libmcu.a anymore. For example, you can 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 โ€” ๐Ÿง Set the size (in bits) of the double or long double type, respectively. The only possible values for bits are 32 and 64. Whether a given 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 the options.
  • -mgas-isr-prologues โ€” โญ Interrupt service routines (ISRs) may use the __gcc_isr pseudo instruction, which is supported by GNU Binutils. If this option is on, you can still turn the feature off for individual ISRs with the AVR Attributes,,"no_gccisr" function attribute. This feature is turned on by default if optimization is on (but not with -Og, @pxref{Optimize Options}), and if GNU Binutils supports PR21683 (https://sourceware.org/PR21683).
  • -mint8 โ€” ๐Ÿญ Assume int is an 8-bit integer. This changes the sizes of all the types: a char is 1 byte, an int is 1 byte, a long is 2 bytes, and a long long is 4 bytes.
โš ๏ธ Heads up, bestie: 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. The effect is the same as attaching the AVR Attributes,,"OS_task" attribute to main. It's turned on by default if optimization is on.
  • -mno-call-main โ€” ๐ŸŒธ Don't run main by means of:
        XCALL  main
        XJMP   exit
  • -mno-interrupts โ€” ๐Ÿซง The generated code is not compatible with hardware interrupts. The upside is that the code size is smaller.
  • -mrelax โ€” โœจ Try to swap a CALL or JMP instruction for the shorter RCALL or RJMP instruction when that's possible. Setting -mrelax just adds the --mlink-relax option to the assembler's command line and the --relax option to the linker's command line.
    • Jump relaxing is done by the linker, because jump offsets aren't known until the code is located. So the assembler code the compiler generates stays the same, but the instructions in the executable may differ from the ones in the assembler code.
    • Relaxing must be turned on if linker stubs are needed. See the section on "EIND" and linker stubs below.
  • -mpmem-wrap-around โ€” ๐Ÿ” Enable program counter wrap-around in linker relaxation.
  • -mrodata-in-ram, -mno-rodata-in-ram โ€” ๐Ÿง  Put the .rodata sections for read-only data in RAM or in program memory, respectively. For most devices there's no real choice, so this option acts more 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 available. 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 โ€” ๐Ÿ’œ Allow truncation instead of rounding towards zero for fractional fixed-point types.
  • -nodevicelib โ€” ๐Ÿ™…โ€โ™€๏ธ Don't link against AVR-LibC's device specific library libmcu.a.
  • -nodevicespecs โ€” ๐ŸŽ€ Don't add -specs=device-specs/specs-mcu to the compiler driver's command line. You're on the hook for giving the subprocesses (the compiler proper, the assembler, and the linker) the right command line options. That means you have to supply your own private device specs file with -specs=path-to-specs-file. There's no more need for the option -mmcu=mcu.
    • This option can also replace the older way of specifying custom device-specs files. That way needed -B some-path to point to a directory containing a folder named "device-specs", which in turn contained a specs file named "specs-mcu", where mcu was specified by -mmcu=mcu.
  • -Waddr-space-convert โ€” Heads up, this one warns you whenever you convert between address spaces and the result doesn't fit inside the incoming address space. Those conversions can quietly go sideways ๐Ÿ‘€
  • -Wmisspelled-isr โ€” Warns if your ISR name is misspelled, meaning it's missing the __vector prefix. It's on by default, so you get this one for free ๐Ÿ’…

๐ŸŒธ AVR Optimization Options ๐ŸŒธ

okay so these are the pure optimization options ๐Ÿ’– The ones from above, -mgas-isr-prologues, -mmain-is-OS_task, -mno-call-main, and -mrelax, are only almost optimization options. There are rare moments where their different code generation actually matters, so they're not 100% just speed boosts ๐Ÿซถ

  • -maccumulate-args โ€” Collects outgoing function arguments and grabs or releases the stack space for them once, in the function prologue and epilogue. Without this, outgoing arguments get pushed before each call and popped afterwards. Also check out -fdefer-pop over in the Optimize Options section โœจ

Popping arguments after a call can be expensive on AVR, so accumulating the stack space can actually give you smaller executables. Arguments don't need to be removed from the stack after every call anymore ๐Ÿงš

This one can shrink your code for functions that make several calls to functions that take their arguments on the stack, like printf-style functions ๐Ÿ–จ๏ธ

  • -mbranch-cost=cost โ€” Sets the branch costs for conditional branch instructions to cost. Reasonable values are small, non-negative integers. The default branch cost is 0 ๐ŸŒˆ
  • -mcall-prologues โ€” Function prologues and epilogues get expanded as calls to the right subroutines. The result is smaller code size ๐Ÿฆ‹
  • -mfuse-add, -mno-fuse-add, -mfuse-add=level โ€” Optimizes indirect memory accesses on reduced Tiny devices. By default it uses level=1 for -Og and -O1, and level=2 for higher optimization levels. 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 split multi-byte instructions into 8-bit operations. The default is level=3 for -O1, 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 a different aspect of the optimization, so it's giving math nerd energy ๐Ÿค“๐Ÿ’œ
  • -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 in indirect, post-increment, or pre-decrement addressing ๐Ÿ‘‘

Without this option, X can be used just like Y or Z, but that gets emulated with extra instructions. For example, loading a value with X+const addressing (where const is a small non-negative value, const < 64) into a register Rn is 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 byte-offset shift plus a residual shift with a non-byte offset. This is turned on by default for -O2 and higher, including -Os, but not -Oz ๐Ÿ™ˆ Splitting shifts whose constant offset is a multiple of 8 is controlled by -mfuse-move.
  • -msplit-ldst โ€” Splits multi-byte loads and stores into several byte loads and stores. 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. Turned on by default for -O2 and higher ๐Ÿง 

๐Ÿงš "EIND" and Devices With More Than 128 Ki Bytes of Flash ๐Ÿงš

okay this section is a lot, but it's important bestie ๐Ÿ˜ญ

Pointers in the implementation are 16 bits wide. The address of a function or label is stored as a word address, so indirect jumps and calls can target any code address in the range of 64 Ki words ๐Ÿ’œ

To make indirect jumps work on devices with more than 128 Ki bytes of program memory, there's a special function register called EIND. It serves as the most significant part of the target address when EICALL or EIJMP instructions are used โœจ

Indirect jumps and calls on these devices are handled like this by the compiler, and they come with some limitations:

  • The compiler never sets EIND.
  • The compiler uses EIND implicitly in EICALL/EIJMP instructions, or might read EIND directly to emulate an indirect call/jump using a RET instruction.
  • The compiler assumes that EIND never changes during the startup code or during the application. In particular, EIND is not saved or restored in function or interrupt service routine prologues/epilogues. ๐Ÿšซ
  • For indirect calls to functions and computed gotos, the linker generates stubs. Stubs are jump pads, sometimes also called trampolines. So the indirect call or jump actually jumps to a stub, and the stub contains a direct jump to the desired address ๐Ÿฆ‹
  • Linker relaxation must be turned on so the linker generates the stubs correctly in all situations. See the compiler option -mrelax and the linker option --relax. There are corner cases where the linker is supposed to generate stubs but aborts without relaxation and without a helpful error message ๐Ÿ’€
  • The default linker script is arranged for code with EIND = 0. If your code is supposed to work with EIND != 0, you need a custom linker script to place the sections whose names start with .trampolines into the segment that EIND points to ๐Ÿ—บ๏ธ
  • The startup code from libgcc never sets EIND. Heads up that startup code is a blend of code from libgcc and AVR-LibC. For how AVR-LibC affects EIND, check the AVR-LibC user manual at https://avrdudes.github.io/avr-libc/avr-libc-user-manual/ ๐Ÿ“–
  • It's legit for user-specific startup code to set up EIND early, for example through initialization code placed in section .init3. That code runs before the general startup code that initializes RAM and calls constructors, but after the bit of AVR-LibC startup code that sets EIND to the segment where the vector table lives ๐Ÿซถ

Here's an example:

#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 when both of these conditions are met:
    • The address of a label is taken by means of the gs modifier (short for generate stubs), like so:
        LDI r24, lo8(gs(<func>))
        LDI r25, hi8(gs(<func>))
  • The final location of that label is in a code segment outside the segment where the stubs are located.
  • The compiler emits gs modifiers for code labels in these situations:
    • Taking the address of a function or code label.
    • Computed goto.
    • If a prologue-save function is used. See the -mcall-prologues command-line option.
    • Switch/case dispatch tables. If you don't want these dispatch tables, you can specify the -fno-jump-tables command-line option.
    • C and C++ constructors/destructors called during startup/shutdown.
    • If the tools hit a gs() modifier explained 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 the function has to be called through a symbol (func_4 in the example):

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. Alternatively, func_4 can be defined in the linker script โœจ


๐ŸŽ€ Handling of the "RAMPD", "RAMPX", "RAMPY" and "RAMPZ" Special Function Registers ๐ŸŽ€

Some AVR devices support memories bigger than the 64 KiB range that 16-bit pointers can reach. To access memory locations outside that 64 KiB range, the content of a RAMP register gets used as the high part of the address. The X, Y, and Z address registers get concatenated with RAMPX, RAMPY, and RAMPZ special function registers, respectively, to form a wide address. Similarly, RAMPD is used together with direct addressing ๐ŸŒˆ

  • The startup code initializes the RAMP special function registers with zero.
  • If a named address space other than generic or __flash is used (see AVR Named Address Spaces), then RAMPZ is set as needed before the operation.
  • If the device supports RAM larger than 64 KiB and the compiler needs to change RAMPZ to accomplish an operation, RAMPZ is reset to zero after the operation.
  • If the device comes with a specific RAMP register, the ISR prologue/epilogue saves and restores that SFR, and initializes it with zero in case the ISR code might (implicitly) use it.
  • RAM larger than 64 KiB is not supported by GCC for AVR targets. If you use inline assembler to read from locations outside the 16-bit address range and change one of the RAMP registers, you must reset it to zero after the access โš ๏ธ

๐Ÿ’– AVR Built-in Macros ๐Ÿ’–

GCC defines several built-in macros so user code can test for the presence or absence of features. Almost any of the following built-in macros are deduced from device capabilities and are therefore triggered by the -mmcu= command-line option ๐Ÿง 

For even more AVR-specific built-in macros, see AVR Named Address Spaces and AVR Built-in Functions ๐Ÿ”

  • __AVR_ARCH__ โ€” A built-in macro that resolves to a decimal number identifying the architecture. It depends on the -mmcu=mcu option. Possible values are:

2, 25, 3, 31, 35, 4, 5, 51, 6

for mcu="avr2", "avr25", "avr3", "avr31", "avr35", "avr4", "avr5", "avr51", "avr6", respectively, and:

100, 102, 103, 104, 105, 106, 107

Those mcu values each get their own number, respectively ๐Ÿ’…: "avrtiny", "avrxmega2", "avrxmega3", "avrxmega4", "avrxmega5", "avrxmega6", and "avrxmega7" ๐ŸŒธ. If mcu names an actual device, this built-in macro gets set to match. For example, with -mmcu=atmega8 the macro is defined to 4 โœจ.

  • "__AVR_Device__" ๐Ÿ’– Setting -mmcu=device defines this macro, and it reflects the device's name. So -mmcu=atmega8 defines "__AVR_ATmega8__", -mmcu=attiny261a defines "__AVR_ATtiny261A__", and so on ๐Ÿฆ‹.
    • The names follow the scheme "__AVR_Device__", where Device is the device name as it's written in the AVR user manual. The difference between Device in the macro and device in -mmcu=device is that the latter is always lowercase ๐Ÿ™ˆ.
    • If device is only a core architecture like avr51 and not a real device, this macro isn't defined ๐Ÿ’€.
  • "__AVR_DEVICE_NAME__" ๐Ÿ“ Setting -mmcu=device makes this one hold the device's name. With -mmcu=atmega8 it's defined to "atmega8".
    • Same deal here: if device is just a core architecture like avr51, this macro isn't defined.
  • "__AVR_CVT__" ๐ŸŽ€ You're compiling with -mcvt to use 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 for 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. This is 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. This is 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. The definition of 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 has an 8-bit stack pointer, respectively. The definition of these macros is affected by -mmcu=, and for -mmcu=avr2 and -mmcu=avr25 also by -msp8.
  • "__AVR_HAVE_RAMPD__", "__AVR_HAVE_RAMPX__", "__AVR_HAVE_RAMPY__", and "__AVR_HAVE_RAMPZ__" ๐ŸŽ€ The device has the RAMPD, RAMPX, RAMPY, and RAMPZ special function registers, 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, 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 is only 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, etc., may use a different address than an instruction accessing RAM, like LD or STS. This offset depends on the device architecture and has to be subtracted from the RAM address to get the respective I/O address.
  • "__AVR_SHORT_CALLS__" ๐ŸŒˆ The -mshort-calls command-line option is set.
  • "__AVR_PM_BASE_ADDRESS__=addr" ๐Ÿ‘‘ Some devices support reading from flash memory using LD* instructions. The flash memory shows up in the data address space at an offset of "__AVR_PM_BASE_ADDRESS__". If this macro isn't defined, the feature isn't available. If it is defined, the address space is linear and there's no need to put .rodata into RAM. This is handled by the default linker description file, and it's currently available for avrtiny and avrxmega3. Even more convenient, there's no need for address spaces like __flash or features like the progmem attribute and pgm_read_*.
  • "__AVR_HAVE_FLMAP__" ๐Ÿ’œ This macro is defined when the following 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.

This 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, the macro is defined to 1 when the .rodata sections for read-only data are located 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 Attributes,,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__" and "__HAVE_DOUBLE64__" โœจ Defined if the compiler supports 32-bit double or 64-bit double, respectively. The actual layout is specified by -mdouble=.
  • "__DEFAULT_DOUBLE__" ๐Ÿงš The size in bits of double if -mdouble= is not set. 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__", and "__DEFAULT_LONG_DOUBLE__" ๐Ÿ’… Same as above, but for long double instead of double.
  • "__WITH_DOUBLE_COMPARISON__" ๐Ÿซถ 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__", and "__WITH_LIBF7_MATH_SYMBOLS__" ๐ŸŒˆ Reflect the --with-libf7={libgcc|math|math-symbols} configure option (https://gcc.gnu.org/install/configure.html#avr).

โœจ AVR Internal Options โœจ

Okay bestie, these ones are used internally by the compiler, and they're how device specs files talk to the compiler proper ๐Ÿงš. You don't need to set them by hand, and they are not optimization options ๐Ÿ™…. They're documented for completeness, and so you can get a better understanding of device specs (https://gcc.gnu.org/wiki/avr-gcc#spec-files) files.

Using these the wrong way may lead to sub-optimal or wrong code. Don't freestyle these ๐Ÿ˜ญ.
  • -mn-flash=num ๐Ÿ‘‘ Assume the flash memory has a size of num times 64 KiB. This determines which "__flashN" address spaces are available.
  • -mflmap ๐Ÿ’œ The device has the FLMAP bit field, located in the special function register NVMCTRL_CTRLB.
  • -mrmw ๐Ÿ”ฅ Assume the device supports the Read-Modify-Write instructions XCH, LAC, LAS, and LAT.
  • -mshort-calls ๐ŸŒธ Assume RJMP and RCALL can target the whole program memory. This option is used for multilib generation and selection for the devices from architecture avrxmega3.
  • -mskip-bug ๐Ÿ™ˆ Generate code without skips (CPSE, SBRS, SBRC, SBIS, SBIC) over 32-bit instructions.
  • -msp8 ๐Ÿซถ Treat the stack pointer register as an 8-bit register, i.e. assume the high byte of the stack pointer is zero. The compiler uses this option to select and build multilibs for architectures avr2 and avr25. These architectures mix devices with and without SPH.

โœจ Blackfin Options โœจ

  • -mcpu=cpu[-sirevision] ๐Ÿฆ‹ Specifies the name of the target Blackfin processor. Currently, cpu can be one of these:
    • 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 specifies the silicon revision of the target Blackfin processor. Any workarounds available for the targeted silicon revision are enabled. If sirevision is none, no workarounds are enabled. If sirevision is 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. If sirevision is any, "__SILICON_REVISION__" is defined to be 0xffff. If this optional sirevision isn't used, 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.

Heads up: 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. This option has effect only 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 avoids the instructions to save, set up, and restore frame pointers, and makes an extra register available in leaf functions.
  • -mspecld-anomaly and -mno-specld-anomaly ๐Ÿ’– When enabled, the compiler ensures the generated code does not contain speculative loads after jump instructions. If this option is used, "__WORKAROUND_SPECULATIVE_LOADS" is defined.
  • -mcsync-anomaly, -mno-csync-anomaly โ€” okay so when this is on, the compiler makes sure it never slips CSYNC or SSYNC instructions in too soon after conditional branches ๐Ÿซถ Using it also defines __WORKAROUND_SPECULATIVE_SYNCS โœจ
  • -mlow64k, -mno-low64k โ€” when enabled, the compiler gets to assume your whole program fits in the low 64k of memory, so it can take shortcuts ๐ŸŒธ The default is -mno-low64k, which treats the program as arbitrarily large, so no shortcuts for you, bestie ๐Ÿ™ˆ
  • -mstack-check-l1 โ€” does stack checking using info the uClinux kernel stashes in L1 scratchpad memory ๐Ÿงš
  • -mid-shared-library, -mno-id-shared-library โ€” generates code that supports shared libraries through the library ID method. This is the vibe for execute in place and shared libraries in an environment with no virtual memory management ๐Ÿฆ‹ It implies -fPIC, and on a bfin-elf target it also implies -msim. The default is -mno-id-shared-library, which doesn't assume ID-based shared libraries are in use.
  • -mleaf-id-shared-library, -mno-leaf-id-shared-library โ€” same ID-based shared library energy, but this one assumes your library or executable won't link against any other ID shared libraries ๐Ÿ’… That lets the compiler use faster code for jumps and calls. The default is -mno-leaf-id-shared-library, where nobody makes that assumption, so the jump and call instructions come out slower ๐Ÿข
  • -mshared-library-id=n โ€” sets the identification number of the ID-based shared library you're compiling ๐Ÿ“ A value of 0 gives you more compact code. Other values force that number onto the current library, but they're no more space- or time-efficient than just leaving the option out.
  • -msep-data, -mno-sep-data โ€” lets the data segment live in a different area of memory from the text segment ๐Ÿ’œ This helps with execute in place when there's no virtual memory management, since it gets rid of relocations against the text section. The default is -mno-sep-data, which assumes the data segment follows the text segment.
  • -mlong-calls, -mno-long-calls โ€” tells the compiler to do function calls the long way: load the function's address into a register first, then make a subroutine call on that register ๐ŸŒˆ You need this if the target function lives outside the 24-bit addressing range of the offset-based subroutine call instruction. It's not on by default, and -mno-long-calls puts you back to the default. Heads up, these switches don't change how calls through function pointers get generated ๐Ÿ‘€
  • -mfast-fp โ€” links with the fast floating-point library โšก It relaxes some of the IEEE floating-point standard's rules for checking inputs against Not-a-Number (NAN), all in the name of performance.
  • -minline-plt โ€” enables inlining of PLT entries in function calls to functions that aren't known to bind locally ๐ŸŽ€ It does nothing unless you also use -mfdpic.
  • -mmulticore โ€” builds a standalone application for multicore Blackfin processors ๐Ÿ‘‘ It makes sure the right start files and link scripts for multicore get used, and defines the macro __BFIN_MULTICORE. This one only works with -mcpu=bf561[-sirevision]. You can pair it with -mcorea or -mcoreb for the one-application-per-core setup. Without either of those, you get the single-application, dual-core model, where the main function of Core B has to be named coreb_main. If you skip this option entirely, you get the single-core application model.
  • -mcorea โ€” builds a standalone application for Core A of BF561 in the one-application-per-core setup ๐Ÿซถ The proper start files and link scripts for Core A get used, and the macro __BFIN_COREA is defined. This only works together with -mmulticore.
  • -mcoreb โ€” builds a standalone application for Core B of BF561 in the one-application-per-core setup ๐Ÿ’– The right start files and link scripts for Core B get used, and __BFIN_COREB is defined. When you use this one, coreb_main replaces main. It also only works together with -mmulticore.
  • -msdram โ€” builds a standalone application for SDRAM ๐Ÿง  Start files and link scripts get used to put your app into SDRAM, and the macro __BFIN_SDRAM is defined. Your loader has to initialize SDRAM before it loads the application.
  • -micplb โ€” assumes ICPLBs are enabled at run time ๐Ÿ”ฅ This changes how certain anomaly workarounds behave. For Linux targets, the default is to assume ICPLBs are enabled. For standalone applications, the default is off.

๐Ÿฆ‹ C6X Options ๐Ÿฆ‹

  • -march=name โ€” sets the name of the target architecture ๐Ÿ’… GCC uses this to figure out which kinds of instructions it's allowed to emit when generating assembly code. The valid names 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 โœจ
  • -msim โ€” picks startup files and a linker script that work with the simulator ๐Ÿงš
  • -msdata=default โ€” puts small global and static data into the .neardata section, which gets pointed to by register B14 ๐Ÿ’œ Small uninitialized global and static data goes into .bss, which sits right next to .neardata. Small read-only data goes into .rodata. The sections for the big pieces of data are .fardata, .far, and .const.
  • -msdata=all โ€” puts all data into the small-data sections, not just the small stuff, and uses addressing relative to the B14 register to get at it ๐Ÿ“
  • -msdata=none โ€” no small-data sections at all. Everything gets accessed with absolute addresses ๐Ÿ™ˆ All initialized global and static data goes into .fardata, all uninitialized data goes into .far, and all constant data goes into .const.
  • -mdsbt โ€” compiles for the DSBT shared library ABI ๐Ÿฆ‹ You need this to compile with -fpic or -fPIC, and it implies -fpic.
  • -mlong-calls โ€” avoids PC-relative calls and uses indirection instead ๐ŸŒˆ

๐ŸŽ€ CRIS Options ๐ŸŽ€

These options are defined specifically for the CRIS ports ๐Ÿ’…

  • -march=architecture-type, -mcpu=architecture-type โ€” generates code for the architecture you pick โญ The choices are v3, v8, and v10, which line up with ETRAX 4, ETRAX 100, and ETRAX 100 LX. The default is v0.
  • -mtune=architecture-type โ€” tunes everything that applies to the generated code for the architecture you name, except the ABI and the set of available instructions ๐Ÿซถ The choices are the same as for -march=architecture-type.
  • -mmax-stackframe=n โ€” warns when a function's stack frame goes over n bytes โš ๏ธ
  • -metrax4, -metrax100 โ€” these are just synonyms for -march=v3 and -march=v8, respectively ๐Ÿงš
  • -mmul-bug-workaround, -mno-mul-bug-workaround โ€” works around a bug in the muls and mulu instructions, on the CPU models where that bug shows up ๐Ÿ”ฅ It's disabled by default.
  • -mpdebug โ€” turns on CRIS-specific verbose debug info in the assembly code ๐Ÿ” This also turns off the #NO_APP formatted-code indicator that gets sent to the assembler at the top of the assembly file.
  • -mcc-init โ€” don't reuse condition-code results from the previous instruction ๐Ÿ™…โ€โ™€๏ธ Instead, always emit compare and test instructions before any condition codes get used.
  • -mno-side-effects โ€” doesn't emit instructions with side effects in addressing modes, except for post-increment ๐ŸŒธ
  • -mstack-align, -mno-stack-align, -mdata-align, -mno-data-align, -mconst-align, -mno-const-align โ€” these decide whether the stack frame, individual data, and constants get aligned to the biggest single data access size for your chosen CPU model ๐Ÿ’– The no- versions turn that alignment off. The default is 32-bit alignment. ABI details like structure layout stay the same no matter what you pick here.
  • -m32-bit, -m16-bit, -m8-bit โ€” kind of like the stack, data, and const alignment options above, but these make the stack frame, writable data, and constants 32-bit, 16-bit, or 8-bit aligned ๐ŸŽ€ The default is 32-bit alignment.
  • -mno-prologue-epilogue, -mprologue-epilogue โ€” with -mno-prologue-epilogue, the normal function prologue and epilogue that set up the stack frame get skipped, and no return instructions or return sequences get generated ๐Ÿ’€
Use -mno-prologue-epilogue only alongside careful visual inspection of the compiled code, bestie. No warnings or errors show up when call-saved registers need to be saved, or when storage for local variables needs to be allocated. You're on your own for that one ๐Ÿ˜ญ
  • -mbest-lib-options, -moverride-best-lib-options โ€” -mbest-lib-options picks the most feature-enabling options that the other options allow ๐ŸŒˆ It has no no- form, but -moverride-best-lib-options turns it off no matter where the two options sit on the command line.
  • -mtrap-using-break8 โ€” emits traps as break 8 ๐Ÿฆ‹ This is the default for CRIS v3 and up. If you disable it, calls to abort get used instead.
  • -mtrap-unaligned-atomic โ€” emits checks that make break 8 instructions run when atomic builtins get applied to misaligned memory ๐Ÿ‘€
  • -munaligned-atomic-may-use-library โ€” handles atomic builtins that might get applied to unaligned data by calling library functions instead ๐Ÿ“š This one overrides -mtrap-unaligned-atomic.
  • -sim โ€” links with the input-output functions from a simulator library ๐Ÿงช Code, initialized data, and zero-initialized data all get allocated one after another.
  • -sim2 โ€” works like -sim, but the linker options place initialized data at 0x40000000 and zero-initialized data at 0x80000000 ๐Ÿ’ซ

๐ŸŒˆ C-SKY Options ๐ŸŒˆ

GCC has a whole set of options just for C-SKY V2 chips ๐Ÿงš Let's go through them ๐Ÿ’–

  • -march=arch โ€” pick the C-SKY target architecture ๐ŸŽ€ Valid values for arch are ck801, ck802, ck803, ck807, and ck810. The default is ck810.
  • -mcpu=cpu โ€” pick the exact C-SKY target processor ๐Ÿ’… Valid values for cpu are the whole squad:
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, -mlittle-endian โ€” choose big-endian or little-endian code ๐ŸŒธ The default is little-endian.
  • -mfloat-abi=name โ€” choose which floating-point ABI to use ๐Ÿ”ฅ Permissible values are soft, softfp, and hard.
    • soft: GCC generates output with library calls for floating-point operations.
    • softfp: you get hardware floating-point instructions, but it still uses the soft-float calling conventions.
    • hard: you get floating-point instructions and FPU-specific calling conventions.

The default depends on the specific target configuration.

โš ๏ธ The hard-float and soft-float ABIs are NOT link-compatible. Compile your entire program with the same ABI and link with a compatible set of libraries, or it's giving chaos ๐Ÿ’€
  • -mdouble-float, -mno-double-float โ€” when -mhard-float is in effect, turn on generation of double-precision float instructions ๐Ÿฆ‹ This is the default except when compiling for CK803.
  • -mfdivdu, -mno-fdivdu โ€” when -mhard-float is in effect, turn on generation of "frecipd", "fsqrtd", and "fdivd" instructions โœจ This is the default except when compiling for CK803.
  • -mfpu=fpu โ€” select the floating-point processor ๐Ÿ’œ This option can only be used with -mhard-float. Values for fpu are fpv2_sf (equivalent to -mno-double-float -mno-fdivdu), fpv2 (-mdouble-float -mno-divdu), and fpv2_divd (-mdouble-float -mdivdu).
  • -melrw, -mno-elrw โ€” enable the extended "lrw" instruction ๐Ÿงš This option defaults to on for CK801 and off otherwise.
  • -mistack, -mno-istack โ€” enable interrupt stack instructions. The default is off. ๐ŸŒˆ The -mistack option is required to handle the "interrupt" and "isr" function attributes.
  • -mmp, -mno-mp โ€” enable multiprocessor instructions. The default is off ๐Ÿ“
  • -mcp, -mno-cp โ€” enable coprocessor instructions. The default is off ๐Ÿซถ
  • -mcache, -mno-cache โ€” enable coprocessor instructions. The default is off ๐ŸŒธ
  • -msecurity, -mno-security โ€” enable C-SKY security instructions. The default is off ๐Ÿ”’
  • -mtrust, -mno-trust โ€” enable C-SKY trust instructions. The default is off ๐Ÿฆ‹
  • -mdsp, -mno-dsp, -medsp, -mno-edsp, -mvdsp, -mno-vdsp โ€” enable C-SKY DSP, Enhanced DSP, or Vector DSP instructions, respectively ๐Ÿ’… All of these options default to off.
  • -mdiv, -mno-div โ€” generate divide instructions. Default is off โœจ
  • -msmart, -mno-smart โ€” generate code for Smart Mode, using only registers numbered 0-7 so you can use 16-bit instructions ๐ŸŽ€ This option is ignored for CK801, where this is already the required behavior, and it defaults to on for CK802. For other targets, the default is off.
  • -mhigh-registers, -mno-high-registers โ€” generate code using the high registers numbered 16-31 ๐Ÿ‘‘ This option is not supported on CK801, CK802, or CK803, and it's enabled by default for other processors.
  • -manchor, -mno-anchor โ€” generate code using global anchor symbol addresses ๐ŸŒˆ
  • -mpushpop, -mno-pushpop โ€” generate code using "push" and "pop" instructions ๐Ÿ’– This option defaults to on.
  • -mmultiple-stld, -mno-multiple-stld โ€” generate code using "stm" and "ldm" instructions ๐Ÿงš This option isn't supported on CK801, but it's enabled by default on other processors.
  • -mconstpool, -mno-constpool โ€” create constant pools in the compiler, instead of handing that job off to the assembler ๐Ÿซถ This option is the default and is required for correct code generation on CK801 and CK802. On other processors it's optional.
  • -mstack-size, -mno-stack-size โ€” emit ".stack_size" directives for each function in the assembly output ๐Ÿฆ‹ This option defaults to off.
  • -mccrt, -mno-ccrt โ€” generate code for the C-SKY compiler runtime instead of libgcc ๐Ÿ’œ This option defaults to off.
  • -mbranch-cost=n โ€” set the branch costs to roughly "n" instructions ๐Ÿ“ The default is 1.
  • -msched-prolog, -mno-sched-prolog โ€” permit scheduling of function prologue and epilogue sequences ๐Ÿ’€ Using this option can result in code that is not compliant with the C-SKY V2 ABI prologue requirements, and that cannot be debugged or backtraced. It is disabled by default.
๐Ÿšจ Heads up, bestie: -msched-prolog can make code that isn't compliant with the C-SKY V2 ABI prologue requirements. That code also can't be debugged or backtraced. It's disabled by default for a reason.
  • -msim, -mno-sim โ€” links the library libsemi.a, which is compatible with the simulator ๐Ÿ–ฅ๏ธ Applicable to ELF compiler only.

โœจ Cygwin and MinGW Options โœจ

These additional options are available for Microsoft Windows targets ๐ŸŒธ

  • -mconsole โ€” generate a console application by telling the linker to set the PE header subsystem type needed for console apps ๐ŸŽ€ This option is available for Cygwin and MinGW targets and is enabled by default on those targets.
  • -mcrtdll=library โ€” preprocess, compile, or link with the specified C RunTime DLL library ๐Ÿงš This option adjusts the predefined macros "__CRTDLL__", "__MSVCRT__", "_UCRT", and "__MSVCRT_VERSION__" for the specified CRT library. It also picks the start file for the CRT library and links with the CRT library. Recognized CRT library names for the preprocessor are "crtdll*", "msvcrt10*", "msvcrt20*", "msvcrt40*", "msvcr40*", "msvcrtd*", "msvcrt-os*", "msvcr70*", "msvcr71*", "msvcr80*", "msvcr90*", "msvcr100*", "msvcr110*", "msvcr120*" and "ucrt*". If this option isn't specified, the default MinGW import library "msvcrt" is used for linking, and no other preprocessor adjustment is done. The MinGW import library "msvcrt" is just a symlink to (or a copy of) another MinGW CRT import library, chosen during MinGW compilation. The MinGW import library "msvcrt-os" is for the Windows system CRT DLL "msvcrt.dll", and in most cases it's the default MinGW import library. Generally speaking, changing the CRT DLL requires recompiling the entire MinGW CRT. This option is for experimental and testing purposes only ๐Ÿงช It's available for MinGW targets.
  • -mdll โ€” available for Cygwin and MinGW targets ๐ŸŒˆ It generates a DLL (a dynamic link library), letting you select the required runtime startup object and entry point.
  • -mnop-fun-dllimport โ€” available for Cygwin and MinGW targets ๐Ÿ’… It makes GCC ignore the "dllimport" attribute.
  • -mthreads โ€” supports thread-safe exception handling on MinGW ๐Ÿ”ฅ Programs that rely on thread-safe exception handling must compile and link all code with the -mthreads option. When compiling, -mthreads defines -D_MT. When linking, it links in a special thread helper library, -lmingwthrd, which cleans up per-thread exception-handling data.
  • -municode โ€” available for MinGW-w64 targets ๐ŸŒธ It predefines the "UNICODE" preprocessor macro and chooses Unicode-capable runtime startup code.
  • -mwin32 โ€” available for Cygwin and MinGW targets ๐Ÿฆ‹ It sets the typical Microsoft Windows predefined macros in the preprocessor, but it doesn't influence the choice of runtime library or startup code.
  • -mwindows โ€” available for Cygwin and MinGW targets ๐ŸŽ€ It generates a GUI application by telling the linker to set the PE header subsystem type appropriately.
  • -fno-set-stack-executable โ€” available for MinGW targets ๐Ÿซถ It means the executable flag for the stack used by nested functions isn't set. This is necessary for binaries running in kernel mode of Microsoft Windows, since the User32 API (the one used to set executable privileges) isn't available there.
  • -fwritable-relocated-rdata โ€” available for MinGW and Cygwin targets ๐Ÿงš It puts relocated data in a read-only section into the ".data" section. This is necessary for older runtimes that don't support modifying ".rdata" sections for pseudo-relocation.
  • -mpe-aligned-commons โ€” available for Cygwin and MinGW targets ๐Ÿ’œ It means the GNU extension to the PE file format, which permits correct alignment of COMMON variables, should be used when generating code. It's enabled by default if GCC detects that the target assembler found during configuration supports the feature.
  • -muse-libstdc-wrappers โ€” uses Cygwin DLL wrappers to support the C++ operators "new" and "delete" ๐ŸŒˆ

See also under x86 Options for standard options.


๐ŸŒˆ Darwin Options ๐ŸŒˆ

These options are defined for all architectures running the Darwin operating system ๐ŸŽ

FSF GCC on Darwin does not create "fat" object files. It creates an object file for the single architecture that GCC was built to target. Apple's GCC on Darwin does create "fat" files if multiple -arch options are used. It does this by running the compiler or linker multiple times and joining the results together with lipo ๐Ÿ’–

The subtype of the file created (like ppc7400 or ppc970 or i686) is determined by the flags that specify the ISA that GCC is targeting, like -mcpu or -march. You can use -force_cpusubtype_ALL to override this โœจ

okay so the Darwin tools are not chill about ISA mismatches ๐Ÿ’€ the assembler, as, only lets you use instructions that are valid for the subtype of the file it's generating, so you literally cannot put 64-bit instructions in a ppc750 object file ๐Ÿ™…โ€โ™€๏ธ the shared library linker, /usr/bin/libtool, will fail and print an error if you ask it to make a shared library with a less restrictive subtype than its input files (like shoving a ppc970 object file into a ppc7400 library, no thank you ๐Ÿ˜ญ). the executable linker, ld, is the chill one ๐Ÿซถ it quietly gives the executable the most restrictive subtype of any of its inputs.


โœจ OPTIONS โœจ

  • -Fdir โ€” okay this one's a whole vibe ๐ŸŒธ it adds the framework directory dir to the front of the list of places searched for header files. these directories get mixed in with the ones from -I options and they're scanned left-to-right, in order ๐Ÿงš

A framework directory is just a folder full of frameworks ๐Ÿ“ and a framework is a directory that ends in .framework and has a Headers and/or PrivateHeaders directory sitting right inside it. the framework's name is the folder name without the .framework part. its headers live in one of those two folders, and Headers gets searched first ๐Ÿ’–

A subframework is a framework directory that lives inside a framework's Frameworks directory ๐Ÿฆ‹ you can only #include subframework headers from inside a header of the framework that contains it, or from a sibling subframework header. two subframeworks are siblings if they're in the same framework. a subframework should not share a name with a framework, and if it does, GCC will warn you ๐Ÿšจ 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 its PrivateHeaders or Headers directory ๐ŸŽ€

  • -iframeworkdir โ€” 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 it finds through dir. this option only works for the C family of languages ๐Ÿ’…
  • -arch name โ€” generates output for architecture name ๐ŸŒˆ as we talked about earlier, GCC makes output for the architecture it was configured for, and you use its usual options to pick subarchitecture variants. -arch is only here for compatibility, but if name doesn't match GCC's own idea of the target architecture, you'll get an error ๐Ÿ™…โ€โ™€๏ธ
  • -dependency-file filename โ€” this is just an alias for the preprocessor option -MF filename ๐Ÿงš
  • -fapple-kext โ€” generates code for Darwin loadable kernel extensions ๐Ÿ”ฅ
  • -gused โ€” emits debugging info for symbols that are used โœจ for the stabs debugging format, this also turns on -feliminate-unused-debug-symbols. it's ON by default, so you're already getting it ๐Ÿ“
  • -gfull โ€” emits debugging info for all symbols and types ๐Ÿ’œ the whole buffet ๐Ÿฝ๏ธ
  • -matt-stubs, -mno-att-stubs โ€” turns on (or off) AT&T-style PIC stubs ๐Ÿฆ‹ this is the default when the target architecture supports it, which right now means x86 only ๐Ÿ’€
  • -mconstant-cfstrings, -fconstant-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 ๐ŸŒธ -fconstant-cfstrings is just an alias for -mconstant-cfstrings, so they're twins ๐Ÿ‘ฏโ€โ™€๏ธ
  • -mdynamic-no-pic โ€” generates code for executables, not shared libraries ๐ŸŽ€ heads up, this one is incompatible with -fpic, -fPIC, -fpie, and -fPIE, so don't mix them ๐Ÿ™…โ€โ™€๏ธ
  • -mfix-and-continue, -ffix-and-continue, -findirect-data โ€” generates code built for fast turnaround development โšก the example given is letting GDB dynamically load .o files into programs that are already running, which is giving main character debugging energy ๐Ÿ˜ญ -findirect-data and -ffix-and-continue are only around for backwards compatibility ๐Ÿซถ
  • -mkernel โ€” turns on kernel development mode ๐Ÿง  this one sets -static, -fno-common, -fno-use-cxa-atexit, -fno-exceptions, -fno-non-call-exceptions, -fapple-kext, -fno-weak, and -fno-rtti, where applicable ๐Ÿ’… for PowerPC targets it also sets -mno-altivec, -msoft-float, -fno-builtin, and -mlong-branch ๐Ÿ‘‘
  • -mmacosx-version-min=version, -asm_macosx_version_min=version โ€” -mmacosx-version-min sets the earliest MacOS X version this executable will run on, which is version โœจ typical values include 12, 10.12, and 10.5.8 ๐ŸŒˆ

If the compiler was built to use the system's headers by default, then the default for this option is the system version the compiler is running on. otherwise, the default is to make choices that work with as many systems and codebases as possible ๐Ÿซง

-asm_macosx_version_min=version is similar, but the GCC driver only passes its version info to the assembler ๐Ÿงš

  • -mone-byte-bool โ€” overrides the defaults for bool so that sizeof(bool)==1 ๐Ÿ“ by default, sizeof(bool) is 4 on Darwin/PowerPC and 1 on Darwin/x86, so this option does nothing on x86 ๐Ÿ™ˆ
Warning: -mone-byte-bool makes GCC generate code that is NOT binary compatible with code made without that switch ๐Ÿ˜ญ using it might mean recompiling every other module in your program, including system libraries. only use it to conform to a non-default data model.
  • -msymbol-stubs, -mno-symbol-stubs โ€” forces generation of external symbol indirection stubs for PIC references ๐Ÿฆ‹ by default this gets turned on automatically if the target linker version (-mtarget-linker) is old enough to need them ๐Ÿ’œ
  • -mtarget-linker=version, -mtarget-linker version โ€” specifies the target ld64 version and overrides whatever version is in the GCC configuration ๐Ÿ”ฅ newer linker versions support improved code generation in some cases, like for PIC code ๐ŸŒˆ
  • -ObjC โ€” equivalent to -x objective-c, which says the input is Objective-C source code ๐ŸŽ€
  • -ObjC++ โ€” equivalent to -x objective-c++, which says the input is Objective-C++ source code ๐Ÿ’–
  • -Wnonportable-cfstrings, -Wno-nonportable-cfstrings โ€” warns if constant CoreFoundation string objects contain non-portable characters โš ๏ธ this warning is enabled by default ๐Ÿซถ
  • -all_load โ€” loads all members of static archive libraries ๐Ÿ“ฆ see man ld(1) for more ๐Ÿงš
  • -arch_errors_fatal โ€” makes errors about files with the wrong architecture fatal ๐Ÿ’€
  • -bind_at_load โ€” marks the output file so the dynamic linker binds all undefined references when the file is loaded or launched ๐ŸŒธ
  • -bundle โ€” produces a Mach-o bundle format file ๐Ÿซง see man ld(1) for more information ๐Ÿ™Œ
  • -bundle_loader executable โ€” specifies the executable that will load the build output file being linked ๐ŸŽ€ see man ld(1) for more information ๐Ÿ“–
  • -dynamiclib โ€” when you pass this, GCC produces a dynamic library instead of an executable when linking, using the Darwin libtool command ๐Ÿฆ‹
  • -force_cpusubtype_ALL โ€” makes GCC's output file have the ALL subtype, instead of one controlled by the -mcpu or -march option ๐Ÿ‘‘
  • -nodefaultrpaths โ€” doesn't add default run paths for the compiler library directories to executables, modules, or dynamic libraries ๐Ÿง  on macOS 10.5 and later, the embedded runpath gets added by default unless you put -nodefaultrpaths on the link line. run paths are needed (and therefore enforced) to build on macOS 10.11 or later ๐Ÿ’…
  • -nodefaultexport โ€” doesn't add default symbol exports to modules or dynamic libraries ๐ŸŒˆ

These next ones are the linker options that get passed straight through to the Darwin linker, which explains them in full in its own man page ๐Ÿ“– no cap, it's a lot to cover here ๐Ÿ˜ญ

  • -allowable_client client_name โ€” passed to the Darwin linker ๐Ÿงš
  • -client_name โ€” passed to the Darwin linker ๐Ÿงš
  • -compatibility_version โ€” passed to the Darwin linker ๐Ÿงš
  • -current_version โ€” passed to the Darwin linker ๐Ÿงš
  • -dead_strip โ€” passed to the Darwin linker ๐Ÿงš
  • -dylib_file โ€” passed to the Darwin linker ๐Ÿงš
  • -dylinker โ€” passed to the Darwin linker ๐Ÿงš
  • -dylinker_install_name โ€” passed to the Darwin linker ๐Ÿงš
  • -dynamic โ€” passed to the Darwin linker ๐Ÿงš
  • -exported_symbols_list โ€” passed to the Darwin linker ๐Ÿงš
  • -filelist โ€” passed to the Darwin linker ๐Ÿงš
  • -flat_namespace โ€” passed to the Darwin linker ๐Ÿงš
  • -force_flat_namespace โ€” passed to the Darwin linker ๐Ÿงš
  • -framework โ€” passed to the Darwin linker ๐Ÿงš
  • -headerpad_max_install_names โ€” passed to the Darwin linker ๐Ÿงš
  • -image_base โ€” passed to the Darwin linker ๐Ÿงš
  • -init symbol-name โ€” passed to the Darwin linker ๐Ÿงš
  • -install_name โ€” passed to the Darwin linker ๐Ÿงš
  • -keep_private_externs โ€” passed to the Darwin linker ๐Ÿงš
  • -pagezero_size โ€” passed to the Darwin linker ๐Ÿงš
  • -preload โ€” passed to the Darwin linker ๐Ÿงš
  • -read_only_relocs โ€” passed to the Darwin linker ๐Ÿงš
  • -sectalign โ€” passed to the Darwin linker ๐Ÿงš
  • -sectcreate โ€” passed to the Darwin linker ๐Ÿงš
  • -seg_addr_table โ€” passed to the Darwin linker ๐Ÿงš
  • -seg1addr โ€” passed to the Darwin linker ๐Ÿงš
  • -segaddr โ€” passed to the Darwin linker ๐Ÿงš
  • -segprot โ€” passed to the Darwin linker ๐Ÿงš
  • -segs_read_only_addr โ€” passed to the Darwin linker ๐Ÿงš
  • -segs_read_write_addr โ€” passed to the Darwin linker ๐Ÿงš
  • -sub_library โ€” passed to the Darwin linker ๐Ÿงš
  • -sub_umbrella โ€” passed to the Darwin linker ๐Ÿงš
  • -twolevel_namespace โ€” passed to the Darwin linker ๐Ÿงš
  • -twolevel_namespace_hints โ€” passed to the Darwin linker ๐Ÿงš
  • -umbrella โ€” passed to the Darwin linker ๐Ÿงš
  • -undefined โ€” passed to the Darwin linker ๐Ÿงš
  • -unexported_symbols_list โ€” passed to the Darwin linker ๐Ÿงš
  • -weak_framework โ€” passed to the Darwin linker ๐Ÿงš
  • -weak_reference_mismatches โ€” passed to the Darwin linker ๐Ÿงš
  • -whatsloaded โ€” passed to the Darwin linker ๐Ÿงš
  • -whyload โ€” passed to the Darwin linker ๐Ÿงš

๐Ÿฆ‹ DEC Alpha Options ๐Ÿฆ‹

okay these are the -m options defined for the DEC Alpha implementations ๐Ÿ’œ

  • -mno-soft-float, -msoft-float โ€” tells GCC to use (or not use) the hardware floating-point instructions for floating-point operations ๐Ÿ”ฅ with -msoft-float, functions in libgcc.a get used to do the floating-point ops instead. unless those get replaced by routines that emulate the FP operations, or get compiled to call such emulation routines, those routines will issue floating-point operations ๐Ÿ˜ญ 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 ๐Ÿ™…โ€โ™€๏ธ

Note that Alpha implementations without floating-point operations are required to have floating-point registers ๐Ÿง 

  • -mfp-regs, -mno-fp-regs โ€” generates code that uses (or does not use) the floating-point register set ๐Ÿ’… -mno-fp-regs implies -msoft-float ๐Ÿซถ if the FP register 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 must also be compiled with that option ๐Ÿ™ˆ

A typical use of this option is building a kernel that doesn't use, and so doesn't need to save and restore, any floating-point registers ๐Ÿ‘‘

Okay bestie, the Alpha chip's floating-point hardware is built for maximum speed ๐ŸŽ๏ธ๐Ÿ’จ It's mostly IEEE compliant, but full compliance needs a little software help ๐Ÿซถ

  • -mieee โ€” This is the fully IEEE-compliant mode, except the inexact-flag isn't kept track of (more on that below) ๐Ÿ’– When it's on, the macro _IEEE_FP gets defined during compilation โœจ The code runs slower, but it can handle denormalized numbers and the special IEEE values like not-a-number and plus/minus infinity ๐Ÿฆ‹ Other Alpha compilers call this -ieee_with_no_inexact ๐Ÿ’…

DEBIAN SPECIFIC: this one is on by default for alpha-linux-gnu, unless -ffinite-math-only (which is part of the -ffast-math set) is specified ๐Ÿ™ˆ The reason is that the software functions in the GNU libc math libraries can produce denormalized numbers, NaNs, and infs, and without -mieee those will make a program SIGFPE the second it tries to use the results ๐Ÿ’€

  • -mieee-with-inexact โ€” This is like -mieee, but the generated code also keeps the IEEE inexact-flag ๐Ÿซง Turn it on and you get fully-compliant IEEE math. Along with _IEEE_FP, the macro _IEEE_FP_EXACT gets defined too โœจ On some Alpha chips the code can run significantly slower than the default, and since hardly any code depends on the inexact-flag, you should normally skip this one ๐Ÿ™…โ€โ™€๏ธ Other Alpha compilers call it -ieee_with_inexact ๐Ÿ’œ
  • -mfp-trap-mode=trap-mode โ€” This controls which floating-point-related traps are enabled ๐Ÿชค Other Alpha compilers call it -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 turned off in software, like the division by zero trap ๐Ÿ“
    • u โ€” Everything n gives you, plus underflow traps ๐ŸŒธ
    • su โ€” Like u, but the instructions are marked as safe for software completion (check the Alpha architecture manual for the deets) ๐Ÿงš
    • sui โ€” Like su, plus inexact traps ๐Ÿ‘‘
  • -mfp-rounding-mode=rounding-mode โ€” Picks the IEEE rounding mode ๐ŸŽ€ Other Alpha compilers call it -fprm rounding-mode ๐Ÿ’– The rounding-mode can be one of these:
    • n โ€” Normal IEEE rounding. Numbers get rounded towards the nearest machine number, or towards the even machine number if it's a tie โš–๏ธ
    • m โ€” Round towards minus infinity โฌ‡๏ธ
    • c โ€” Chopped rounding. Numbers get rounded towards zero ๐Ÿซง
    • d โ€” Dynamic rounding. A field in the floating-point control register (fpcr, see the Alpha architecture reference manual) decides which rounding mode is in effect ๐ŸŽ›๏ธ The C library sets this register to round towards plus infinity, so unless your program changes the fpcr, d means round towards plus infinity โœจ
  • -mtrap-precision=trap-precision โ€” Okay so, Alpha 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 spot that caused a floating-point trap ๐Ÿ” You can pick how precise you want it:
    • p โ€” Program precision. This is the default, and it means a trap handler can only tell which program caused a floating-point exception ๐Ÿ“ฆ
    • f โ€” Function precision. The trap handler can find the function that caused the exception ๐Ÿงฉ
    • i โ€” Instruction precision. The trap handler can find the exact instruction that caused the exception ๐ŸŽฏ

Other Alpha compilers have equivalent options called -scope_safe and -resumption_safe ๐Ÿ’…

  • -mieee-conformant โ€” Marks the generated code as IEEE conformant ๐Ÿ“œ > Don't use this unless you also specify -mtrap-precision=i and either -mfp-trap-mode=su or -mfp-trap-mode=sui, or it's not gonna be it ๐Ÿ™…โ€โ™€๏ธ 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 put it together 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 with code, even if that takes more instructions (the max is six) ๐Ÿ”จ You typically use this when building a shared library dynamic loader ๐Ÿช„ Since it's itself a shared library, 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 โ€” These decide 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 ones supported by the CPU GCC was built on if you didn't specify one ๐Ÿซถ
  • -msafe-bwa, -mno-safe-bwa โ€” These decide whether, without the optional BWX instruction set, GCC should generate multi-thread and async-signal safe code for byte and aligned word memory accesses ๐Ÿงต
  • -msafe-partial, -mno-safe-partial โ€” These decide 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 start and end on an aligned 64-bit data boundary ๐Ÿ“
  • -mfloat-vax, -mfloat-ieee โ€” -mfloat-vax generates code that uses VAX F and G floating-point arithmetic, and -mfloat-ieee generates code that does not use it, so you get IEEE single and double precision instead ๐Ÿฆ•
  • -mexplicit-relocs, -mno-explicit-relocs โ€” 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 is accessed through 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 caps the small data area at 64KB, but the variables can be accessed with a single instruction, which is iconic ๐Ÿ’…

The default is -mlarge-data. With this option, the data area is limited to just below 2GB ๐ŸŒŒ Programs that need more than 2GB of data must use malloc or mmap to allocate the data in the heap instead of in the program's data segment ๐Ÿง 

When 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 of the entire program (or shared library) fits in 4MB, so it's reachable with a branch instruction ๐Ÿฆ‹ When -msmall-data is also used, the compiler can assume all local symbols share the same $gp value, which cuts the instructions for a function call from 4 down to 1 ๐Ÿƒโ€โ™€๏ธ 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 supports scheduling parameters for the EV4, EV5 and EV6 families, and it picks the default instruction set values from the processor you specify ๐ŸŽฏ If you don't specify a processor type, GCC defaults to the 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 has no effect if GCC doesn't recognize the processor ๐Ÿคทโ€โ™€๏ธ

  • -mtune=cpu_type โ€” Sets only the instruction scheduling parameters for the machine type cpu_type ๐ŸŽถ The instruction set stays the same. Native toolchains also support native, which picks the best architecture option for the host processor ๐Ÿ–ฅ๏ธ -mtune=native has no effect 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 depends a lot on the application's memory access patterns and the size of the external cache on the machine ๐Ÿงƒ Valid options for time are:
    • number โ€” A decimal number representing clock cycles ๐Ÿ”ข
    • L1, L2, L3, main โ€” GCC has estimates of clock cycles for "typical" EV4 & EV5 hardware at the Level 1, 2 & 3 caches (also called Dcache, Scache, and Bcache), as well as main memory ๐Ÿง  Heads up, L3 is only valid for EV5 โš ๏ธ
  • -mtls-kernel โ€” Emits rdval instead of rduniq for the thread pointer ๐Ÿงตโœจ

๐Ÿฆ‹ eBPF Options ๐Ÿฆ‹

  • -mtls-size=bitsize โ€” okay so this one sets the bit size of immediate TLS offsets ๐ŸŽ€ The valid values are 16, 32, and 64, and it defaults to 32 โœจ
  • -mlong-double-128, -mlong-double-64 โ€” these decide how big the "long double" type is ๐Ÿ“ ngl -mlong-double-128 is incompatible with VAX floating point, so don't mix them ๐Ÿ’€

๐Ÿงš eBPF Options ๐Ÿงš

  • -mframe-limit=bytes โ€” this is the hard limit for frame sizes, in bytes ๐Ÿ“ฆ The value has to be less than or equal to 32767. If you don't pick one, it defaults to whatever limit the version of the Linux kernel you're targeting imposes ๐ŸŒธ
  • -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 slaying ๐Ÿ‘‘
  • -mjmpext, -mno-jmpext โ€” turn the extra conditional-branch instructions on or off ๐Ÿฆ‹ They're enabled for CPU v2 and above.
  • -mjmp32, -mno-jmp32 โ€” turn 32-bit jump instructions on or off ๐Ÿ’œ Enabled for CPU v3 and above.
  • -malu32, -mno-alu32 โ€” turn 32-bit ALU instructions on or off โœจ Enabled for CPU v3 and above.
  • -mv3-atomics, -mno-v3-atomics โ€” turn on or off the instructions for general atomic operations that came in with CPU v3 โšก Enabled for CPU v3 and above.
  • -mbswap, -mno-bswap โ€” turn byte swap instructions on or off ๐Ÿ”„ Enabled for CPU v4 and above.
  • -msdiv, -mno-sdiv โ€” turn signed division and modulus instructions on or off โž— Enabled for CPU v4 and above.
  • -msmov, -mno-smov โ€” turn sign-extending move and memory load instructions on or off ๐ŸŽ€ Enabled for CPU v4 and above.
  • -mcpu=version โ€” picks which version of the eBPF ISA you're targeting ๐Ÿ‘‘ Newer versions might not be supported by every kernel, so check first ๐Ÿ™ˆ The default is v4.

Here are the supported values for version:

  • v1 โ€” the first stable eBPF ISA, with no special features or extensions ๐Ÿผ
  • v2 โ€” supports the jump extensions, as in -mjmpext ๐Ÿฆ‹
  • v3 โ€” all the v2 features, plus:
    • 32-bit jump operations, as in -mjmp32
    • 32-bit ALU operations, as in -malu32
    • general atomic operations, as in -mv3-atomics
  • v4 โ€” all the v3 features, 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, -mno-co-re โ€” enable or disable BPF Compile Once - Run Everywhere (CO-RE) support ๐ŸŒˆ CO-RE support is enabled by default when you generate BTF debug information for the BPF target (-gbtf).
  • -mxbpf โ€” generates code for an expanded version of BPF that relaxes some of the usual BPF restrictions ๐Ÿš€ One of the things it does is save and restore callee-saved registers at function entry and exit, respectively.
  • -masm=dialect โ€” outputs assembly using the eBPF dialect you pick ๐Ÿ’… The default is pseudoc.

Here are the supported values for dialect:

  • normal โ€” outputs the normal assembly dialect
  • pseudoc โ€” outputs the pseudo-C assembly dialect
  • -minline-memops-threshold=bytes โ€” sets a size threshold in bytes. At or below it, memmove, memcpy, and memset are always expanded inline ๐Ÿง  Anything bigger would need a library call instead, but BPF doesn't allow libcalls, so going over the threshold is a compile-time error ๐Ÿ’€ The default is 1024 bytes.
  • -Wco-re โ€” turns on warnings for situations where -mco-re is enabled but it might not be possible to generate CO-RE compatible code ๐Ÿ‘€

๐ŸŒธ FR30 Options ๐ŸŒธ

These options are defined specifically for the FR30 port ๐ŸŽ€

  • -msmall-model โ€” uses the small address space model ๐Ÿฃ This can make smaller code, but it assumes that all symbolic values and addresses fit into a 20-bit range.
  • -mno-lsim โ€” assumes runtime support has already been provided, so there's no need to include the simulator library (libsim.a) on the linker command line ๐Ÿงƒ

๐Ÿ’œ 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 instead ๐Ÿ“š
  • -malloc-cc โ€” dynamically allocates condition code registers ๐Ÿช„
  • -mfixed-cc โ€” doesn't try to dynamically allocate condition code registers, and only uses icc0 and fcc0 ๐Ÿ”’
  • -mdword, -mno-dword โ€” control whether the ABI uses double-word instructions ๐ŸŽ€
  • -mdouble, -mno-double โ€” enable or disable use of floating-point double instructions ๐Ÿฆ‹
  • -mmedia, -mno-media โ€” enable or disable use of media instructions ๐ŸŽถ
  • -mmuladd, -mno-muladd โ€” enable or disable use of 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 aren't known to bind locally ๐Ÿงฉ It has no effect without -mfdpic. It's enabled by default if you're optimizing for speed and compiling for shared libraries (i.e., -fPIC or -fpic), or when an optimization option such as -O3 or above is in 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 that's known to be in read-only sections ๐Ÿ“– It's enabled by default, except for -fpic or -fpie. Even though it may help make the global offset table smaller, it trades 1 instruction for 4 there ๐Ÿค” With -fPIC or -fPIE, it trades 3 instructions for 4, one of which may be shared by multiple symbols, and it avoids the need for a GOT entry for the referenced symbol, so it's more likely to be a win โœจ If it isn't, you can use -mno-gprel-ro 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 โ€” follows the EABI requirement of always creating a frame pointer whenever a stack frame is allocated ๐Ÿ–ผ๏ธ This option is enabled by default and can be disabled with -mno-linked-fp.
  • -mlong-calls โ€” uses indirect addressing to call functions outside the current compilation unit ๐Ÿ“ž This lets the functions be placed anywhere within the 32-bit address space ๐ŸŒ
  • -malign-labels โ€” tries to align labels to an 8-byte boundary by inserting NOPs into the previous packet ๐Ÿงฑ This only does anything when VLIW packing is enabled. It doesn't create new packets; it just adds NOPs to existing ones ๐Ÿซถ
  • -mlibrary-pic โ€” generates position-independent EABI code ๐Ÿงญ
  • -macc-4 โ€” uses only the first four media accumulator registers 4๏ธโƒฃ
  • -macc-8 โ€” uses all eight media accumulator registers 8๏ธโƒฃ
  • -mpack, -mno-pack โ€” enable or disable packing VLIW instructions ๐Ÿ“ฆ
  • -mno-eflags โ€” does not mark ABI switches in e_flags ๐Ÿšซ
  • -mcond-move, -mno-cond-move โ€” enable or disable the use of conditional-move instructions. It's enabled by default โ†ช๏ธ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ™ˆ
  • -mscc, -mno-scc โ€” enable or disable the use of conditional set instructions. It's enabled by default ๐ŸŽฏ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ™ˆ
  • -mcond-exec, -mno-cond-exec โ€” enable or disable the use of conditional execution. It's enabled by default ๐Ÿƒโ€โ™€๏ธ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ™ˆ
  • -mvliw-branch, -mno-vliw-branch โ€” enable or disable an optimization pass that packs branches into VLIW instructions. It's enabled by default ๐Ÿ“ฆ
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ™ˆ
  • -mmulti-cond-exec, -mno-multi-cond-exec โ€” enable or disable optimization of "&&" and "||" in conditional execution. It's enabled by default ๐Ÿ”€
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ™ˆ
  • -mnested-cond-exec, -mno-nested-cond-exec โ€” enable or disable nested conditional execution optimizations. It's enabled by default ๐Ÿช†
This switch is mainly for debugging the compiler and will likely be removed in a future version. ๐Ÿ™ˆ
  • -moptimize-membar, -mno-optimize-membar โ€” this switch removes redundant membar instructions from the compiler-generated code ๐Ÿงน It's enabled by default.
  • -mtomcat-stats โ€” makes gas print out tomcat statistics ๐Ÿ“Š
  • -mcpu=cpu โ€” selects the processor type to generate code for ๐Ÿ–ฅ๏ธ The possible values are frv, fr550, tomcat, fr500, fr450, fr405, fr400, fr300, and simple โœจ

๐ŸŽ€ FT32 Options ๐ŸŽ€

These options are defined specifically for the FT32 port ๐Ÿ’–

  • -msim โ€” specifies that the program will run on the simulator ๐Ÿ–ฅ๏ธ This causes an alternate runtime startup and library to be linked.
You must not use this option when generating programs that will run on real hardware ๐Ÿšจ You must provide your own runtime library for whatever I/O functions are needed.
  • -mnodiv โ€” does 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 โ€” does not generate code that reads program memory ๐Ÿ“ต

๐Ÿง 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 ๐ŸŽ€ Okay bestie, this one switches GCC over to the uClibc C library. It's the default on *-*-linux-*uclibc* targets.
  • -mmusl ๐ŸŒธ This one uses the musl C library instead. It's the default on *-*-linux-*musl* targets.
  • -mbionic ๐Ÿค– This one uses the Bionic C library. It's the default on *-*-linux-*android* targets.
  • -mandroid, -mno-android ๐Ÿ“ฑ This makes your code Android-compatible ๐Ÿ’… It's the default on *-*-linux-*android* targets.
    • When compiling, it 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. It also defines the preprocessor macro "__ANDROID__".
    • You can turn all of this off with -mno-android.
  • -tno-android-cc ๐Ÿ™…โ€โ™€๏ธ This turns off the compilation side of -mandroid. So -mbionic, -fPIC, -fno-exceptions and -fno-rtti won't get turned on by default.
  • -tno-android-ld ๐Ÿง This turns off the linking side of -mandroid. The linker gets the standard Linux linking options instead.

โœจ H8/300 Options โœจ

Okay so these -m options are only for the H8/300 implementations ๐Ÿงš

  • -mrelax ๐Ÿฆ‹ Shortens some address references at link time, whenever that's possible. It does this by using 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. You have to use this with either -mh or -ms.
  • -msx ๐Ÿ”ฅ Generates H8SX code.
  • -ms2600 ๐Ÿ‘‘ Generates code for the H8S/2600. You must use this with -ms.
  • -mquickcall ๐Ÿซถ Uses registers for passing arguments.
  • -mslowbyte ๐Ÿข Treats access to byte-sized memory as slow.
  • -mexr, -mno-exr ๐Ÿ’… Stores extended registers on the stack before running functions with the "monitor" attribute. The default is -mexr. This one is only valid for H8S targets.
  • -mint32 ๐Ÿ“ Makes "int" data 32 bits by default.
  • -malign-300 ๐Ÿ™ˆ On the H8/300H and H8S, this uses the same alignment rules as the H8/300. By default those chips align longs and floats on 4-byte boundaries. -malign-300 moves them to 2-byte boundaries instead. It has no effect 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 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, check /usr/lib/sched.models on an HP-UX system. 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, -mno-atomic-libcalls โš›๏ธ Generates libcalls for atomic loads and stores when sync libcalls are disabled. This one is enabled by default. It only affects how the HPPA backend generates atomic libcalls.
    • Both the sync and libatomic libcall implementations use locking ๐Ÿ”’ So 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 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.
  • -mcaller-copies ๐Ÿ“ฆ The caller copies function arguments that get passed by hidden reference. Use this one with care, because it isn't compatible with the default 32-bit runtime. Only aggregates larger than eight bytes are passed by hidden reference, though, and this option gives better compatibility with OpenMP ๐Ÿ’œ
  • -mcoherent-ldcw ๐ŸงŠ Uses the ldcw/ldcd coherent cache-control hint.
  • -mdisable-fpregs ๐Ÿšซ Disables floating-point registers. It's equivalent to -msoft-float.
  • -mdisable-indexing ๐Ÿ™ˆ Stops the compiler from using indexing address modes. This avoids some pretty obscure problems when compiling MIG generated code under MACH.
  • -mfast-indirect-calls ๐ŸŽ๏ธ Generates code that assumes calls never cross space boundaries. This lets GCC emit faster indirect calls.
    • Heads up: this doesn't 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 compiling kernel code. Write a register range as two registers separated by a dash. You can list multiple ranges separated by a comma.
  • -mgas ๐Ÿงต Enables assembler directives that only GAS understands.
  • -mgnu-ld ๐Ÿ‚ Uses options specific to GNU ld. This passes -shared to ld when building a shared library. It's the default when GCC is configured, explicitly or implicitly, with the GNU linker.
    • This option doesn't change which ld gets called. It only changes the parameters passed to that ld. The ld that's called is decided by the --with-ld configure option, then 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*.
  • -mhp-ld ๐Ÿข Uses options specific to HP ld. This passes -b to ld when building a shared library, and passes +Accept TypeMismatch to ld on all links. It's the default when GCC is configured, explicitly or implicitly, with the HP linker.
    • Like -mgnu-ld, this doesn't change which ld gets called. It only changes the parameters 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 ๐Ÿ”ง Enables the optimization pass in the HP-UX linker.
    • Heads up: this makes symbolic debugging impossible ๐Ÿ’€
  • -mlong-calls ๐Ÿ˜ Generates code that uses long call sequences. This makes sure a call can always reach linker generated stubs.
    • By default, long calls are only generated when the distance from the call site to the beginning of the function or translation unit (depending on the case) goes past a predefined limit. That limit is set by the branch type being used. The limits for normal calls are 7,600,000 and 240,000 bytes, for PA 2.0 and PA 1.X architectures respectively. Sibling calls are always limited to 240,000 bytes.
    • Distances are measured from the beginning of functions when using the -ffunction-sections option. They're also measured that way when using the -mgas and -mno-portable-runtime options together under HP-UX with the SOM linker.
    • It's usually not something you want, since it degrades performance ๐Ÿ˜ฌ But it may be useful in large applications, especially when partial linking is used to build the application.
    • The types of long calls used depend on the capabilities of the assembler and linker, and on the type of code being generated. Systems that support long absolute calls, and long PIC symbol-difference or PC-relative calls, should see a relatively small impact. An indirect call is used on 32-bit ELF systems in PIC code, though, and it's quite long ๐Ÿซ 
  • -mlong-load-store ๐Ÿ“ฆ Generates 3-instruction load and store sequences, which the HP-UX 10 linker sometimes requires. This is equivalent to the +k option to the HP compilers.
  • -mno-space-regs ๐Ÿš€ Generates code that assumes the target has no space registers. This allows GCC to generate faster indirect calls and use unscaled index address modes. Such 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. To find the right scheduling option for your machine, check /usr/lib/sched.models on an HP-UX system. The default scheduling is 8000.
  • -msio, -mwsio ๐Ÿ–จ๏ธ -msio generates the predefine "_SIO" for server IO. The default is -mwsio, which generates the predefines "__hp9000s700", "__hp9000s700__" and "_WSIO" for workstation IO. These options are available under HP-UX and HI-UX.
  • -msoft-float ๐Ÿงฎ Generates output containing library calls for floating point.
Warning: the requisite libraries aren't available for all HPPA targets. Normally the facilities of the machine's usual C compiler are used, but this can't be done directly in cross-compilation. You must 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 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.

okay so -munix=93 gives you the exact same predefines as GCC 3.3 and 3.4 ๐ŸŒธ and then it's giving upgrades. -munix=95 adds extra predefines for XOPEN_UNIX and _XOPEN_SOURCE_EXTENDED, plus the startfile unix95.o โœจ. -munix=98 goes even harder ๐Ÿ”ฅ with extra predefines for _XOPEN_UNIX, _XOPEN_SOURCE_EXTENDED, _INCLUDE__STDC_A1_SOURCE and _INCLUDE_XOPEN_SOURCE_500, plus the startfile unix98.o ๐Ÿฆ‹.

๐Ÿ˜ญ bestie, listen: this option changes the interfaces for various library routines, and it also messes with the operational behavior of the C library. So extreme care is needed when you use it ๐Ÿ’€

If you're writing library code that has to work with more than one UNIX standard, you need to test, set and restore the variable __xpg4_extended_mask as appropriate ๐Ÿงš. Most GNU software doesn't provide this capability, so don't be surprised if it's missing ๐Ÿ™ˆ


โœจ OPTIONS โœจ

  • -nolibdld โ€” no link options to search libdld.sl get generated when -static is used on HP-UX 10 and later ๐Ÿซถ
  • -static โ€” okay, this one's a whole saga ๐Ÿ˜ญ. The HP-UX C library version of setlocale depends on libdld.sl, and there's no archive version of it. So when you pass -static, you need special link options to sort that dependency out ๐Ÿ’…. On HP-UX 10 and later, the GCC driver adds the necessary options to link with libdld.sl for you, which means the resulting binary ends up dynamic ๐Ÿฆ‹. On the 64-bit port, the linkers already make dynamic binaries by default anyway. If you don't want the driver adding those link options, use -nolibdld ๐ŸŒˆ
  • -threads โ€” adds multithreading support with the dce thread library under HP-UX ๐Ÿงต and 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. The -mgnu-as one is the default ๐Ÿงš
  • -mgnu-ld, -mno-gnu-ld โ€” generates (or doesn't) code for the GNU linker. This is the default too ๐Ÿ”—
  • -mno-pic โ€” generates code that doesn't use a global pointer register. heads up: 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 right before and right 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 โ€” doesn't 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 โ€” doesn't 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 โ€” doesn't generate inline code for sqrt ๐Ÿ™…
  • -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 before the instruction that triggered the stop bit. This can improve instruction scheduling, but it doesn't always ๐Ÿคทโ€โ™€๏ธ
  • -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. This is useful when compiling kernel code ๐Ÿซง. A register range is two registers separated by a dash, and you can list multiple 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 (that is, 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 โ€” (en/dis)ables speculative scheduling of the instructions that depend on the data speculative loads before reload. This is only effective with -msched-br-data-spec enabled. The default setting is enabled โœ…
  • -msched-ar-in-data-spec, -mno-sched-ar-in-data-spec โ€” (en/dis)ables speculative scheduling of the instructions that depend on the data speculative loads after reload. This is only effective with -msched-ar-data-spec enabled. The default setting is enabled โœ…
  • -msched-in-control-spec, -mno-sched-in-control-spec โ€” (en/dis)ables speculative scheduling of the instructions that depend on the control speculative loads. This is only effective with -msched-control-spec enabled. The default setting is enabled โœ…
  • -mno-sched-count-spec-in-critical-path, -msched-count-spec-in-critical-path โ€” if enabled, speculative dependencies are considered when computing the instruction priorities. This makes the use of speculation a bit more conservative. The default setting is disabled ๐Ÿ™…
  • -msched-spec-ldc, -mno-sched-spec-ldc โ€” uses a simple data speculation check. This option is on by default ๐ŸŸข
  • -msched-control-spec-ldc, -mno-sched-control-spec-ldc โ€” uses a simple check for control speculation. This option is on by default ๐ŸŸข
  • -msched-stop-bits-after-every-cycle, -mno-sched-stop-bits-after-every-cycle โ€” places a stop bit after every cycle when scheduling. This option is on by default ๐ŸŸข
  • -msched-fp-mem-deps-zero-cost, -mno-sched-fp-mem-deps-zero-cost โ€” assumes that floating-point stores and loads are not likely to cause a conflict when placed into the same instruction group. This option is disabled by default ๐Ÿ™…
  • -msel-sched-dont-check-control-spec, -mno-sel-sched-dont-check-control-spec โ€” generates checks for control speculation in selective scheduling. This flag is disabled by default ๐Ÿ™…
  • -msched-max-memory-insns=max-insns โ€” sets a limit on the number of memory insns per instruction group, giving lower priority to subsequent memory insns that try to schedule in the same instruction group. This is frequently useful for preventing cache bank conflicts. The default value is 1 ๐Ÿงฑ
  • -msched-max-memory-insns-hard-limit, -mno-sched-max-memory-insns-hard-limit โ€” makes the limit set by -msched-max-memory-insns a hard limit, disallowing more than that number in an instruction group. Otherwise the limit is "soft," meaning non-memory operations are preferred when the limit is reached, but memory operations may still be scheduled ๐Ÿ’ซ

๐Ÿฆ‹ 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. -march=arch-type lets GCC generate code that may not run at all on processors other than the one you indicated ๐Ÿ˜ฌ. The choices for arch-type are:
    • native โ€” the local processor type detected by the native compiler ๐Ÿ 
    • loongarch64 โ€” the generic LoongArch 64-bit processor ๐ŸŒ
    • la464 โ€” a LoongArch LA464-based processor with LSX and LASX ๐ŸŒธ
    • la664 โ€” a 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 ๐Ÿงฉ
    • la32v1.0 โ€” LoongArch32 ISA version 1.0 ๐Ÿงฉ
    • la32rv1.0 โ€” LoongArch32 Reduced ISA version 1.0 ๐Ÿงฉ

More info about the LoongArch ISA versions can be found 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, sniffed out by the native compiler ๐Ÿ”
  • generic โ€” a generic LoongArch processor ๐ŸŒธ
  • loongarch64 โ€” a generic LoongArch 64-bit processor ๐Ÿ’œ
  • la464 โ€” the LoongArch LA464 core โœจ
  • la664 โ€” the LoongArch LA664 core ๐Ÿฆ‹
  • loongarch32 โ€” a generic LoongArch 32-bit processor ๐Ÿงš

  • -mabi=base-abi-type โ€” okay so this one picks the calling convention you want the code generated for ๐Ÿ‘‘ base-abi-type can be one of:
    • lp64d โ€” uses 64-bit general purpose registers and 32/64-bit floating-point registers for passing parameters ๐ŸŽ€ The data model is LP64, so 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 passing parameters ๐Ÿ’… Same LP64 data model: int is 32 bits, long int and pointers are 64 bits.
    • lp64s โ€” uses 64-bit general purpose registers and no floating-point registers for passing parameters ๐Ÿซถ Same LP64 data model: int is 32 bits, long int and pointers are 64 bits.
  • -mfpu=fpu-type โ€” generates code for the FPU type you pick, which can be one of:
    • 64 โ€” lets hardware floating-point instructions be used for both 32-bit and 64-bit operations ๐Ÿ”ฅ
    • 32 โ€” lets hardware floating-point instructions be used for 32-bit operations only ๐ŸŒˆ
    • none or 0 โ€” stops hardware floating-point instructions from being used at all ๐Ÿ™ˆ
  • -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 is allowed to be generated ๐Ÿ’€
  • -msoft-float โ€” forces -mfpu=none and stops floating-point registers from being used for passing parameters ๐Ÿงธ Heads up: this one may change the target ABI ๐Ÿšจ
  • -msingle-float โ€” forces -mfpu=32 and lets 32-bit floating-point registers be used for passing parameters ๐Ÿฆ‹ Also may change the target ABI โš ๏ธ
  • -mdouble-float โ€” forces -mfpu=64 and lets 32/64-bit floating-point registers be used for passing parameters ๐Ÿ‘‘ Yep, this one may change the target ABI too ๐Ÿ™ƒ
  • -mlasx, -mno-lasx, -mlsx, -mno-lsx โ€” these incrementally adjust how much of the SIMD stuff (none / LSX / LASX) the compiler is allowed to use for code generation ๐Ÿ’… 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 changes in the order they show up on the GCC driver's command line ๐Ÿ”„ That gives 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 โ€” this pair decides whether integer division by zero traps (or doesn't) ๐Ÿ’€ Fun fact: the page really does spell the second one -mno-check-zero-divison with that typo, so copy it exactly ๐Ÿ™ˆ The default is -mcheck-zero-division for -O0 or -Og, and -mno-check-zero-division for other optimization levels โœจ
  • -mbreak-code=code โ€” tells GCC to emit a "break" code instruction for irrecoverable traps, the kind coming from "__builtin_trap" or inserted by the compiler (like an erroneous path isolated with -fisolate-erroneous-paths-dereference) ๐Ÿ’ฅ The other option is an "amswap.w $r0, $r1, $r0" instruction, which makes the hardware trigger an Instruction Not-defined Exception if code is negative or greater than 32767 ๐Ÿ”ฅ The default is -1, which means use the "amswap.w" instruction ๐ŸŽ€
  • -mcond-move-int, -mno-cond-move-int โ€” conditional moves for integral data in general-purpose registers are enabled (or disabled) ๐Ÿงš The default is -mcond-move-int ๐Ÿ’–
  • -mcond-move-float, -mno-cond-move-float โ€” conditional moves for floating-point registers are 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 "memcpy", but -mno-memcpy can override that if you set it explicitly, no matter what order the 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) less than or equal to n bytes ๐Ÿ’œ The default value of n is 1024 โญ
  • -mcmodel=code-model โ€” sets the code model to one of the following ๐ŸŒ
    • tiny-static (Not implemented yet) ๐Ÿ™ƒ
    • tiny (Not implemented yet) ๐Ÿ™ƒ
    • normal โ€” the text segment must be within 128MB of addressing space, and 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 ๐Ÿฆ‹ This is the default code model, unless GCC was configured with --with-cmodel= specifying a different default code model ๐Ÿ’–
    • large (Not implemented yet) ๐Ÿ™ƒ
    • extreme โ€” this mode doesn't limit the size of the code segment or the data segment ๐Ÿ”ฅ Heads up: -mcmodel=extreme is incompatible with -fplt and/or -mexplicit-relocs=none ๐Ÿšจ
  • -mexplicit-relocs=style, -mexplicit-relocs, -mno-explicit-relocs โ€” this decides when to use assembler relocation operators for symbolic addresses ๐Ÿง  The alternative is assembler macros, which may limit instruction scheduling but allow linker relaxation ๐Ÿซถ With -mexplicit-relocs=none, the assembler macros are always used. With -mexplicit-relocs=always, the assembler relocation operators are always used. With -mexplicit-relocs=auto, the compiler uses the relocation operators where linker relaxation is impossible (to improve code quality) and macros everywhere else โœจ

The default is figured out from the assembler capability detected 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 the assembler supports them but -mrelax isn't enabled, and -mexplicit-relocs=auto if the assembler supports them and -mrelax is enabled ๐Ÿ’…

For backward compatibility, -mexplicit-relocs is equivalent to -mexplicit-relocs=always, and -mno-explicit-relocs is equivalent to -mexplicit-relocs=none ๐Ÿฆ‹

  • -mdirect-extern-access, -mno-direct-extern-access โ€” controls use of the GOT to access external symbols ๐ŸŒ The default is -mno-direct-extern-access: 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 and all external symbols are PC-relatively addressed ๐Ÿงš This is only suitable for environments where no dynamic link is performed, like firmwares, OS kernels, and executables linked with -static or -static-pie ๐Ÿ” -mdirect-extern-access is not compatible with -fPIC or -fpic ๐Ÿšจ

  • -mrelax, -mno-relax โ€” takes (or doesn't take) advantage of linker relaxations ๐ŸŒธ If -mpass-mrelax-to-as is enabled, this option is also passed to the assembler ๐Ÿ’œ The default is figured out at GCC build time by detecting assembler support: -mrelax if the assembler supports both the -mrelax option and conditional branch relaxation, -mno-relax otherwise ๐ŸŽ€ The conditional branch relaxation part is required, because the ".align" directives and conditional branch instructions in the assembly code GCC outputs may be rejected by the assembler due to a relocation overflow ๐Ÿ˜ญ
  • -mpass-mrelax-to-as, -mno-pass-mrelax-to-as โ€” passes (or doesn't pass) the -mrelax or -mno-relax option to the assembler ๐Ÿงธ The default is figured out at GCC build time by detecting assembler support: -mpass-mrelax-to-as if the assembler supports the -mrelax option, -mno-pass-mrelax-to-as otherwise ๐Ÿฆ‹ This option is mostly useful for debugging, or for interoperating with assemblers that are different from the one used at build time ๐Ÿ”ง
  • -mrecip โ€” enables use of 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 then a divide) for floating-point arguments ๐Ÿงฎ These instructions only get generated when -funsafe-math-optimizations is enabled together with -ffinite-math-only and -fno-trapping-math ๐Ÿซถ This option is off by default โŒ Before you use it, you must make sure the target CPU supports the "frecipe" and "frsqrte" instructions ๐Ÿ™ Note that while the throughput of the sequence is higher than the throughput of the non-reciprocal instruction, the precision of the sequence can be decreased by up to 2 ulp (i.e. the inverse of 1.0 equals 0.99999994) ๐Ÿ˜ฌ
  • -mrecip=opt โ€” controls which reciprocal estimate instructions may be used ๐ŸŽฏ opt is a comma-separated list of options, and each one may be preceded by a ! to invert it ๐Ÿ”„
    • all โ€” enables all estimate instructions ๐ŸŒˆ
    • default โ€” enables the default instructions, equivalent to -mrecip ๐ŸŽ€
    • none โ€” disables all estimate instructions, equivalent to -mno-recip ๐Ÿšซ
    • div โ€” enables the approximation for scalar division โž—
    • vec-div โ€” enables the approximation for vectorized division ๐Ÿงฎ
    • sqrt โ€” enables the approximation for scalar square root ๐ŸŒฑ
    • vec-sqrt โ€” enables the approximation for vectorized square root ๐ŸŒฟ
    • rsqrt โ€” enables the approximation for scalar reciprocal square root ๐Ÿฆ‹
    • vec-rsqrt โ€” enables the approximation for vectorized reciprocal square root ๐Ÿ’œ

So, for example, -mrecip=all,!sqrt enables all of the reciprocal approximations except for scalar square root ๐Ÿ’…โœจ

  • -mfrecipe, -mno-frecipe โ€” okay so these let you turn the "frecipe.{s/d}" and "frsqrte.{s/d}" instructions on or off ๐Ÿ’…. When you compile with -march=la664 it's on by default, and otherwise the default is -mno-frecipe ๐ŸŒธ
  • -mdiv32, -mno-div32 โ€” this one lets you use (or skip) the div.w[u] and mod.w[u] instructions when the input is not sign-extended โœจ. With -march=la664 it's enabled by default. Everywhere else it defaults to -mno-div32 ๐Ÿงš
  • -mlam-bh, -mno-lam-bh โ€” toggles the am{swap/add}[_db].{b/h} instructions ๐Ÿ”ฅ. Same deal: on with -march=la664, and -mno-lam-bh otherwise ๐Ÿ’–
  • -mlamcas, -mno-lamcas โ€” toggles the amcas[_db].{b/h/w/d} instructions ๐Ÿฆ‹. It's enabled by default with -march=la664, and the default is -mno-lamcas otherwise ๐Ÿ’œ
  • -mld-seq-sa, -mno-ld-seq-sa โ€” this is about whether a same-address load-load barrier (dbar 0x700) is needed at all ๐Ÿ™ˆ. With -march=la664 it's enabled by default. Otherwise the default is -mno-ld-seq-sa, which means the load-load barrier is needed ๐Ÿ˜ญ
  • -mscq, -mno-scq โ€” lets you use (or not use) the 16-byte conditional store instruction sc.q ๐ŸŽ€. The default is -mscq if the machine type you pick with -march= supports this instruction, and -mno-scq if it doesn't ๐ŸŒˆ
  • -mtls-dialect=opt โ€” this controls which TLS dialect can be used for general dynamic and local dynamic TLS models ๐Ÿ‘‘. The opt argument can be one of these:
    • trad โ€” traditional TLS, and this is the default ๐Ÿ“
    • desc โ€” uses TLS descriptors โœจ
  • -mannotate-tablejump, -mno-annotate-tablejump โ€” when a jump table gets used to optimize a switch statement, this makes a .discard.tablejump_annotate annotation section ๐Ÿงš. It ties the jirl instruction to the jump table, which is super handy because some external tools, like objtool from the Linux kernel building system, need that annotation to analyze control flow ๐Ÿซถ. The default is -mno-annotate-tablejump ๐Ÿ’…

๐Ÿฆ‹ LynxOS Options ๐Ÿฆ‹

These options are for LynxOS targets, so if that's your world, this part's for you ๐ŸŒธ

  • -mshared โ€” uses shared libraries ๐Ÿ’–. The default is linking with static libraries instead ๐ŸงŠ
  • -mthreads โ€” adds multi-threading support ๐Ÿงต
  • -mlegacy-threads โ€” adds legacy multi-threading support ๐Ÿ•ฐ๏ธ. Heads up, this one is incompatible with -mthreads, so you can't have both ๐Ÿ’€

๐ŸŒˆ M32C Options ๐ŸŒˆ

  • -mcpu=name โ€” picks the CPU your code gets generated 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 your program will run on the simulator ๐Ÿงช. This links in an alternate runtime library that supports stuff like file I/O ๐Ÿ“
โš ๏ธ Do not use this option for programs that will run on real hardware! You have to supply your own runtime library for whatever I/O functions you need, no shortcuts bestie ๐Ÿ˜ญ
  • -memregs=number โ€” sets how many memory-based pseudo-registers GCC uses during code generation ๐Ÿง . These get used like real registers, so it's a tradeoff between fitting your code into the available registers and the performance hit from using memory instead ๐Ÿฆ‹. Every module in a program has to 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 โ€” generates code for the M32R/2 ๐Ÿ“
  • -m32rx โ€” generates code for the M32R/X โœจ
  • -m32r โ€” generates 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 ๐Ÿงš. It also assumes 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, so the compiler generates seth/add3 instructions to load their addresses ๐ŸŒˆ. It also assumes all subroutines are reachable with the bl instruction ๐Ÿ”ฅ
  • -mmodel=large โ€” assumes objects may be anywhere in the 32-bit address space, with the compiler generating seth/add3 instructions to load their addresses ๐ŸŒŒ. This time it assumes subroutines may not be reachable with the bl instruction, so the compiler generates the much slower seth/add3/jl instruction sequence ๐Ÿข
  • -msdata=none โ€” disables use of the small data area ๐Ÿšซ. Variables get put into .data, .bss, or .rodata, unless the "section" attribute has been specified ๐ŸŽ€. This is the default ๐Ÿ’…

The small data area is made up of the .sdata and .sbss sections ๐Ÿงธ. You can explicitly put objects into 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 it ๐ŸŒท
  • -msdata=use โ€” puts small global and static data in the small data area and generates special instructions to reference it โญ
  • -G num โ€” puts global and static objects that are 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 has to be set to either sdata or use for this to do anything ๐Ÿ™ƒ

All modules should be compiled with the same -G num value ๐Ÿซ‚. Compiling with different values of num may or may not work ๐Ÿคทโ€โ™€๏ธ. If it doesn't work, the linker gives you 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, -mno-align-loops โ€” aligns all loops to a 32-byte boundary ๐Ÿ“. This option is disabled by 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 โ€” specifies the trap number used to flush the cache ๐Ÿงน. The default is 12, and valid numbers are between 0 and 15 inclusive ๐ŸŽฏ
  • -mno-flush-trap โ€” specifies that the cache cannot be flushed by using a trap ๐Ÿšซ๐Ÿชค
  • -mflush-func=name โ€” specifies the name of the operating system function to call to flush the cache ๐Ÿ› ๏ธ. The default is _flush_cache, but a function call is only used if a trap isn't available ๐Ÿ”
  • -mno-flush-func โ€” indicates that there's no OS function for flushing the cache ๐Ÿ™…โ€โ™€๏ธ

๐ŸŒท M680x0 Options ๐ŸŒท

These are the -m options defined for M680x0 and ColdFire processors ๐ŸŒธ. The default settings depend on which architecture was selected when the compiler was configured, so the defaults for the most common choices are listed below ๐Ÿ“.

  • -march=arch โ€” generates code for a specific M680x0 or ColdFire instruction set architecture ๐Ÿ›๏ธ. For M680x0 architectures, arch can be 68000, 68010, 68020, 68030, 68040, 68060, or cpu32 ๐Ÿ’…. ColdFire architectures are picked based on Freescale's ISA classification, and the valid values are isaa, isaaplus, isab, and isac ๐Ÿฆ‹

GCC defines the 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, they select code that runs on a family of similar processors, but that's optimized 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 the CPUs into families ๐Ÿ“Š:
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
๐Ÿ™…โ€โ™€๏ธ -mcpu=cpu overrides -march=arch if arch is compatible with cpu. Other combinations of -mcpu and -march get rejected, so be careful, bestie ๐Ÿ’€

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 still within the constraints 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 it includes 68060 targets too ๐Ÿ’œ. These two options select the same tuning decisions as -m68020-40 and -m68020-60 respectively ๐Ÿ”

GCC defines the macros "__mcarch" and "__mcarch__" when 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, as selected by -mtune=68020-40 or -mtune=68020-60, it defines the macros for every architecture in that range ๐ŸŒˆ

GCC also defines the macro "__muarch__" when 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 ๐Ÿ–ฒ๏ธ, and it's equivalent to -march=68000 โœจ

Use this option for microcontrollers with a 68000 or EC000 core, including the 68008, 68302, 68306, 68307, 68322, 68328, and 68356 ๐Ÿ”ง๐Ÿ’–

  • -m68010 โ€” okay bestie, this one generates output for a 68010 ๐Ÿงš It's the default when the compiler is set up for 68010 systems, and it's the same as -march=68010 โœจ
  • -m68020, -mc68020 โ€” a 68020 glow-up ๐Ÿ’– Default for 68020 systems, and equivalent to -march=68020 ๐ŸŒธ
  • -m68030 โ€” targets the 68030 ๐Ÿฆ‹ Default for 68030 systems, same as -march=68030
  • -m68040 โ€” targets the 68040 ๐Ÿ”ฅ Default for 68040 systems, and equivalent to -march=68040. This one also turns off the 68881/68882 instructions that a 68040 would have to fake in software. Use it if your 68040 doesn't have the code to emulate them ๐Ÿ’…
  • -m68060 โ€” targets the 68060 ๐Ÿ‘‘ Default for 68060 systems, equivalent to -march=68060. It also blocks the 68020 and 68881/68882 instructions that the 68060 would otherwise have to emulate in software. Use it if your 68060 is missing that emulation code ๐Ÿซถ
  • -m68302 โ€” generates code for a 68302 ๐Ÿ“
  • -m68332 โ€” generates code for a 68332 ๐ŸŒˆ
  • -m68851 โ€” the 68851 was an external MMU for the 68020 ๐Ÿ™ˆ GCC accepts this option, but it doesn't actually use it, so it's basically a nice gesture ๐Ÿ’€
  • -mcpu32 โ€” generates output for a CPU32 ๐Ÿ’œ Default for CPU32 systems, equivalent to -march=cpu32. Use it for microcontrollers with a CPU32 or CPU32+ core, like the 68330, 68331, 68332, 68333, 68334, 68336, 68340, 68341, 68349 and 68360 โœจ
  • -mfidoa โ€” generates code for a Fido A ๐Ÿงš
  • -m5200 โ€” generates output for a 520X ColdFire CPU ๐ŸŒธ Default for 520X systems. It's equivalent to -mcpu=5206, but it's deprecated now, so switch to that option instead ๐Ÿฅ€ Use it for microcontrollers with a 5200 core, like the MCF5202, MCF5203, MCF5204 and MCF5206 ๐Ÿฆ‹
  • -m5206e โ€” generates output for a 5206e ColdFire CPU ๐Ÿ’… It's deprecated in favor of the equivalent -mcpu=5206e
  • -m528x โ€” generates output for a member of the ColdFire 528X family ๐Ÿ”ฅ Deprecated in favor of the equivalent -mcpu=528x
  • -m5307 โ€” generates output for a ColdFire 5307 CPU โญ Deprecated in favor of the equivalent -mcpu=5307
  • -m5407 โ€” generates output for a ColdFire 5407 CPU ๐ŸŽ€ Deprecated in favor of the equivalent -mcpu=5407
  • -mcfv4e โ€” generates output for a ColdFire V4e family CPU, like the 547x/548x ๐Ÿ’– This one includes hardware floating-point instructions. It's equivalent to -mcpu=547x, and it's deprecated in favor of that option ๐Ÿซถ
  • -m68020-40 โ€” generates output for a 68040 without using any of the new instructions ๐ŸŒˆ The result 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 โœจ It's equivalent to -march=68020 -mtune=68020-40.
  • -m68020-60 โ€” generates output for a 68060 without using any of the new instructions ๐Ÿฆ‹ Same deal: it 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 ๐Ÿ’œ It's equivalent to -march=68020 -mtune=68020-60.
  • -mhard-float, -m68881 โ€” generates floating-point instructions ๐Ÿ”ฅ This is the default for 68020 and up, and for ColdFire devices with 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 don't have an 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 set 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, -mno-short, -mnoshort โ€” treats type "int" as 16 bits wide, just like "short int" ๐ŸŽ€ Parameters passed on the stack also get aligned to a 16-bit boundary, even on targets whose API says they should be promoted to 32-bit ๐Ÿ™ˆ This option is disabled by default.
  • -mbitfield, -mno-bitfield, -mnobitfield โ€” controls use of the bit-field instructions ๐Ÿ’œ -m68000, -mcpu32 and -m5200 imply -mnobitfield, and -m68020 implies -mbitfield ๐Ÿซถ
  • -mrtd, -mno-rtd, -mnortd โ€” controls use of a different function-calling convention, and this one is kinda spicy ๐ŸŒถ๏ธ Functions that take a fixed number of arguments return with the "rtd" instruction, which pops their arguments while returning. That saves one instruction in the caller, since the caller doesn't need to pop the arguments anymore โœจ
Heads up, bestie: 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 ๐Ÿ˜ญ
Seriously: incorrect code results if you call a function with too many arguments. Normally, extra arguments are harmlessly ignored, but not here ๐Ÿ™ˆ

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 ๐ŸŽ€

  • -malign-int, -mno-align-int โ€” controls whether GCC aligns "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 on 32-bit boundaries gives code that runs somewhat faster on processors with 32-bit busses, but it costs more memory ๐Ÿ’–
Warning: if you use the -malign-int switch, GCC aligns structures containing the above types differently than most published application binary interface specifications for the m68k ๐Ÿšจ

Use the pc-relative addressing mode of the 68000 directly, instead of using a global offset table ๐Ÿงš At present, this option implies -fpic, allowing at most a 16-bit offset for pc-relative addressing. -fPIC is not presently supported with -mpcrel, though this could be supported for 68020 and higher processors ๐Ÿฅน

  • -mno-strict-align, -mstrict-align โ€” doesn't (or does) assume that unaligned memory references are handled by the system ๐ŸŒธ
  • -msep-data, -mno-sep-data โ€” with -msep-data, GCC 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. This option implies -fPIC โœจ

This option is disabled by default. GCC generates code that assumes the data segment follows the text segment ๐Ÿ™ˆ

  • -mid-shared-library, -mno-id-shared-library โ€” if enabled, GCC generates code that supports shared libraries via the library ID method ๐Ÿ’œ This allows execute-in-place and shared libraries in an environment without virtual memory management. This option implies -fPIC ๐Ÿซถ

This option is disabled by default โœจ

  • -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 that's no more space- or time-efficient than leaving this option out ๐Ÿฆ‹
  • -mxgot, -mno-xgot โ€” when generating position-independent code for ColdFire, this generates code that works if the GOT has more than 8192 entries ๐Ÿ”ฅ This code is larger and slower than code generated without this option. On M680x0 processors, this option isn't needed, since -fPIC is enough ๐Ÿ’…

GCC normally uses a single instruction to load values from the GOT. It's pretty efficient, but it only works if the GOT is smaller than about 64k. Anything bigger makes the linker report an error like this:

relocation truncated to fit: R_68K_GOT16O foobar

If that happens, recompile your code with -mxgot, and it should work with very large GOTs ๐Ÿฅบ Just know that code generated 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 have one of those linkers, 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 ๐ŸŒธ

  • -mxtls โ€” supports a TLS segment larger than 64K โœจ The considerations are similar to the ones for -mxgot above. The default 16-bit offset addressing for TLS is more efficient, but it may be inadequate for some programs ๐Ÿงš
  • -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 less ๐Ÿ”ฅ
  • -mdiv, -mno-div โ€” uses the divide instruction ๐Ÿ’… (Enabled by default).
  • -mrelax-immediates, -mno-relax-immediates โ€” 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 โ€” okay so these two decide how byte-sized reads happen ๐Ÿ‘€ -mslow-bytes tells GCC to prefer word access when reading byte quantities โœจ
  • -mlittle-endian, -mbig-endian โ€” pick your endian vibe ๐Ÿ’– these make code for a little-endian or big-endian target, in that order. Big-endian is the default, and little-endian only works with -m340 ๐ŸŒธ
  • -m210 โ€” generates code for the 210 processor ๐ŸŽ€
  • -m340 โ€” generates code for the 340 processor ๐Ÿ’…
  • -mno-lsim โ€” the bestie move if your runtime support is already handled ๐Ÿซถ it tells GCC to skip the simulator library (libsim.a) on the linker command line
  • -mstack-increment=size โ€” sets the biggest amount a single stack increment can be ๐Ÿ“š big numbers can speed things up for programs that need lots of stack space, but they can also trigger a segmentation fault if the stack gets extended too much ๐Ÿ˜ญ the default is 0x1000 ๐Ÿงš

๐Ÿฆ‹ MicroBlaze Options ๐Ÿฆ‹

  • -msoft-float โ€” software emulation for floating point, and this is the default ๐ŸŒˆ
  • -mhard-float โ€” uses hardware floating-point instructions ๐Ÿ”ฅ
  • -mmemcpy โ€” don't optimize block moves, just use memcpy ๐Ÿ’œ
  • -mcpu=cpu-type โ€” uses the features of, and schedules code for, the CPU you name ๐Ÿ‘‘ values look like vX.YY.Z, where X is the major version, YY is the minor version, and Z is the compatibility code ๐Ÿฆ‹ example values are v3.00.a, v4.00.b, v5.00.a, v5.00.b, and v6.00.a โœจ
  • -mxl-soft-mul, -mno-xl-soft-mul โ€” software multiply emulation, enabled by default ๐Ÿ“
  • -mxl-soft-div, -mno-xl-soft-div โ€” software emulation for divides, also enabled by default ๐ŸŒธ
  • -mxl-barrel-shift โ€” uses the hardware barrel shifter ๐Ÿ’–
  • -mxl-pattern-compare โ€” uses pattern compare instructions ๐ŸŽ€
  • -msmall-divides โ€” table lookup optimization for small signed integer divisions ๐Ÿงš
  • -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 โ€” picks the application model app-model. Here are your valid models ๐Ÿ‘‡
    • executable โ€” the normal executable, and the default ๐Ÿซถ it uses the startup code crt0.o
    • xmdstub โ€” made for the Xilinx Microprocessor Debugger (XMD) based software intrusive debug agent called xmdstub ๐Ÿ” it uses the startup file crt1.o and sets the start address of the program to 0x800
    • bootstrap โ€” for applications loaded by a bootloader ๐Ÿฅพ this model uses the startup file crt2.o, which doesn't have a processor reset vector handler. It's perfect for handing control back to the bootloader on a processor reset instead of the application ๐Ÿ’…
    • novectors โ€” for applications that don't need any of the MicroBlaze vectors ๐ŸŒ™ this can be handy inside a monitoring application. This model uses crt3.o as its startup file ๐Ÿงš
  • -mxl-prefetch โ€” turns on insertion of prefetch (wic) instructions at call sites ๐Ÿš€
  • -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 the GOT, since PC-relative addressing isn't supported ๐Ÿ™ˆ

๐ŸŒˆ MIPS Options ๐ŸŒˆ

  • -EB, -meb โ€” generates big-endian code ๐Ÿ’–
  • -EL, -mel โ€” 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. allegrex, The special value from-abi selects 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 (for example, -march=r2k) โœจ 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" ๐ŸŽ€

Heads up bestie: 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 ๐Ÿ™ˆ with 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 โ€” optimizes for arch ๐ŸŽฏ among other things, this option controls how instructions get scheduled and the perceived cost of arithmetic operations ๐Ÿง  the list of arch values is the same as for -march ๐Ÿ’…

When this option isn't used, GCC optimizes for the processor specified by -march ๐Ÿ‘‘ by using -march and -mtune together, you can generate code that runs on a whole family of processors, but optimize it for one particular member of that family ๐Ÿฆ‹

-mtune defines the macros "_MIPS_TUNE" and "_MIPS_TUNE_foo", which work the same way as the -march ones described above โœจ

  • -mips1 โ€” equivalent to -march=mips1 ๐ŸŒธ
  • -mips2 โ€” equivalent to -march=mips2 ๐ŸŒธ
  • -mips3 โ€” equivalent to -march=mips3 ๐ŸŒธ
  • -mips4 โ€” equivalent to -march=mips4 ๐ŸŒธ
  • -mips32 โ€” equivalent to -march=mips32 ๐ŸŒธ
  • -mips32r3 โ€” equivalent to -march=mips32r3 ๐ŸŒธ
  • -mips32r5 โ€” equivalent to -march=mips32r5 ๐ŸŒธ
  • -mips32r6 โ€” equivalent to -march=mips32r6 ๐ŸŒธ
  • -mips64 โ€” equivalent to -march=mips64 ๐ŸŒธ
  • -mips64r2 โ€” equivalent to -march=mips64r2 ๐ŸŒธ
  • -mips64r3 โ€” equivalent to -march=mips64r3 ๐ŸŒธ
  • -mips64r5 โ€” equivalent to -march=mips64r5 ๐ŸŒธ
  • -mips64r6 โ€” equivalent to -march=mips64r6 ๐ŸŒธ
  • -mips16, -mno-mips16 โ€” generates (or doesn't generate) MIPS16 code ๐Ÿงฉ if GCC is targeting a MIPS32 or MIPS64 architecture, it uses the MIPS16e ASE ๐Ÿ’œ

MIPS16 code generation can also be controlled on a per-function basis with the "mips16" and "nomips16" attributes ๐Ÿฆ‹

  • -mmips16e2, -mno-mips16e2 โ€” uses (or doesn't use) the MIPS16e2 ASE ๐Ÿงƒ this option changes how -mips16 behaves, so it targets the MIPS16e2 ASE instead โœจ
  • -mflip-mips16, -mflip-mips16 โ€” generates MIPS16 code on alternating functions ๐Ÿ™ƒ this one is only there for regression testing of mixed MIPS16/non-MIPS16 code generation, so it's not meant for everyday compiling of user code ๐Ÿšซ
  • -minterlink-compressed, -mno-interlink-compressed โ€” requires (or doesn't require) that code using the standard (uncompressed) MIPS ISA be link-compatible with MIPS16 and microMIPS code, and vice versa ๐Ÿ”—

For example, code using the standard ISA encoding can't jump directly to MIPS16 or microMIPS code. It has to use a call or an indirect jump instead ๐Ÿซ  so -minterlink-compressed disables direct jumps unless GCC knows the jump's target isn't compressed ๐Ÿ’€

  • -minterlink-mips16, -mno-interlink-mips16 โ€” aliases of -minterlink-compressed and -mno-interlink-compressed ๐Ÿ•ฐ๏ธ these predate the microMIPS ASE and are kept around for backwards compatibility ๐Ÿซถ
  • -mabi=32, -mabi=o64, -mabi=n32, -mabi=64, -mabi=eabi โ€” generates code for the given ABI ๐Ÿ“ฆ

The EABI comes in a 32-bit and a 64-bit variant โœจ GCC normally generates 64-bit code when you pick a 64-bit architecture, but you can use -mgp32 to get 32-bit code instead ๐Ÿ’…

For info about the O64 ABI, check out https://gcc.gnu.org/projects/mipso64-abi.html ๐Ÿ“–

GCC supports a variant of the o32 ABI where the floating-point registers are 64 bits wide instead of 32 ๐ŸŒˆ you can select this combo with -mabi=32 -mfp64 ๐ŸŽ€ this ABI relies on the mthc1 and mfhc1 instructions, so it's only supported for MIPS32R2, MIPS32R3, and MIPS32R5 processors ๐Ÿ”ฅ

The register assignments for arguments and return values stay the same, but each scalar value is passed in a single 64-bit register instead of a pair of 32-bit registers ๐Ÿง  for example, scalar floating-point values are returned in $f0 only, not a $f0/$f1 pair ๐Ÿ’– the set of call-saved registers also stays the same, in that the even-numbered double-precision registers are saved ๐Ÿซถ

So okay, there are two extra flavours of the o32 ABI that help you hop from 32-bit to 64-bit registers without losing your mind ๐Ÿฆ‹. The first is FPXX (-mfpxx), and it's giving full flexibility: all code has to run correctly with either 32-bit or 64-bit registers ๐Ÿ’…. FPXX code can be linked with FP32 or FP64, but never both at once ๐Ÿซถ. The second is FP64A (-mfp64 -mno-odd-spreg). It's basically FP64 but it refuses to touch odd-numbered single-precision registers ๐ŸŒธ. Pair it with the "FRE" mode on MIPS32R5 FPUs and FP32 and FP64A code can link together and run in the same process without switching FPU modes โœจ. Iconic, honestly.

  • -mabicalls, -mno-abicalls โ€” Okay, so these tell GCC to generate (or not generate) code that works with SVR4-style dynamic objects ๐ŸŽ€. -mabicalls is the default on SVR4-based systems ๐Ÿ’–.
  • -mshared, -mno-shared โ€” This one decides whether your code is fully position-independent, which means it can get linked into shared libraries ๐ŸŒˆ. It only matters when you're using -mabicalls ๐Ÿ™ˆ.

All -mabicalls code has traditionally been position-independent, even if you use stuff like -fPIC and -fpic ๐Ÿงš. BUT, and this is the tea โ˜•, the GNU toolchain lets executables use absolute accesses for locally-binding symbols as an extension ๐Ÿ‘‘. It can also use shorter GP initialization sequences and make direct calls to locally-defined functions ๐Ÿ”ฅ. That's the mode -mno-shared turns on โญ.

-mno-shared needs binutils 2.16 or higher, and the objects it makes can only be linked by the GNU linker ๐Ÿ’…. The ABI of the final executable doesn't change, only the ABI of relocatable objects does ๐Ÿฆ‹. Using -mno-shared usually leaves executables both smaller and quicker, which is so slay ๐Ÿƒโ€โ™€๏ธโœจ. -mshared is the default ๐Ÿ’œ.

  • -mplt, -mno-plt โ€” This asks GCC to assume (or not assume) that the static and dynamic linkers support PLTs and copy relocations ๐ŸŒธ. It only affects -mno-shared -mabicalls ๐Ÿซถ. For the n64 ABI, it does absolutely 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 โ€” These lift (or don't lift) the usual limits on how big the global offset table can be ๐ŸŒˆ.

GCC normally loads GOT values with a single instruction, which is efficient, but only if the GOT is smaller than about 64k ๐Ÿ˜ญ. Anything bigger and the linker gets mad and reports something like this:

relocation truncated to fit: R_MIPS_GOT16 foobar

If you see that, recompile with -mxgot ๐Ÿ’–. It works with really huge GOTs, but the code is a bit less efficient, because it takes three instructions to fetch the value of a global symbol ๐Ÿ“. Some linkers can make multiple GOTs, and if yours is one of them, you should only need -mxgot when one object file touches more than 64k's worth of GOT entries ๐Ÿงš. Very few linkers do that, lowkey ๐Ÿซ .

These options do nothing unless GCC is generating position-independent code ๐Ÿ™ˆ.

  • -mgp32 โ€” Assume that general-purpose registers are 32 bits wide ๐ŸŒธ.
  • -mgp64 โ€” Assume that general-purpose registers are 64 bits wide ๐Ÿ’œ.
  • -mfp32 โ€” Assume that floating-point registers are 32 bits wide ๐Ÿฆ‹.
  • -mfp64 โ€” Assume that floating-point registers are 64 bits wide โœจ.
  • -mfpxx โ€” Don't assume any particular width for floating-point registers at all ๐Ÿงš.
  • -mhard-float โ€” Use the floating-point coprocessor instructions ๐Ÿ”ฅ.
  • -msoft-float โ€” Don't use the floating-point coprocessor instructions. Instead, floating-point math gets done with library calls ๐Ÿ’….
  • -mno-float โ€” This is basically -msoft-float, but it also swears that the program you're compiling does zero floating-point operations ๐Ÿ™ˆ. Right now it's only supported by some bare-metal MIPS configurations, where it might pick a special set of libraries with no floating-point support at all (that includes stuff like the floating-point printf formats) ๐Ÿ’€. If code compiled with -mno-float accidentally has floating-point operations in it, it's very likely to fail at link time or run time ๐Ÿ˜ญ.
  • -msingle-float โ€” Assume the floating-point coprocessor only does single-precision operations ๐ŸŒˆ.
  • -mdouble-float โ€” Assume the floating-point coprocessor supports double-precision operations. This is the default, obvi ๐Ÿ‘‘.
  • -modd-spreg, -mno-odd-spreg โ€” These enable (or disable) the use of odd-numbered single-precision floating-point registers for the o32 ABI ๐Ÿฆ‹. Enabling them is the default for processors that are 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 the special not-a-number (NaN) IEEE 754 floating-point data gets handled by the abs.fmt and neg.fmt machine instructions ๐ŸŽ€.

By default, or when you use -mabs=legacy, you get the legacy treatment ๐Ÿ™ˆ. In this case, those instructions are treated as arithmetic, so they get avoided wherever correct operation is needed and the input might be a NaN ๐Ÿ˜ญ. Instead, GCC uses a longer sequence of instructions that manipulates the sign bit of the floating-point datum by hand, unless -ffinite-math-only is also specified ๐Ÿงš.

-mabs=2008 picks the IEEE 754-2008 treatment ๐Ÿ’–. Here, those instructions count as non-arithmetic, so they work correctly in every case, including when the input operand is a NaN ๐ŸŒธ. That's why they're always used for the respective operations ๐Ÿ‘‘.

  • -mnan=2008, -mnan=legacy โ€” These control how the special not-a-number (NaN) IEEE 754 floating-point data gets encoded ๐Ÿ’….

-mnan=legacy selects the legacy encoding ๐Ÿฆ‹. In this one, quiet NaNs (qNaNs) are marked by the first bit of their trailing significand field being 0, while signaling NaNs (sNaNs) are marked by that first bit being 1 ๐ŸŒˆ.

-mnan=2008 selects the IEEE 754-2008 encoding โœจ. Here, 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 โ€” These decide whether to use (or not use) the ll, sc, and sync instructions to implement the atomic memory built-in functions ๐Ÿ”ฅ. If you specify neither, GCC uses those instructions as long as the target architecture supports them ๐Ÿซถ.

-mllsc is handy if the runtime environment can emulate the instructions ๐Ÿงš. -mno-llsc can be useful when you're compiling for nonstandard ISAs ๐Ÿ™ˆ. You can make either one the default by configuring GCC with --with-llsc and --without-llsc respectively ๐ŸŽ€. --with-llsc is the default for some configurations, so check the installation documentation for details ๐Ÿ’–.

  • -mdsp, -mno-dsp โ€” These use (or don't use) revision 1 of the MIPS DSP ASE ๐ŸŒธ. This option defines the preprocessor macro "__mips_dsp", and it also defines "__mips_dsp_rev" to 1 โœจ.
  • -mdspr2, -mno-dspr2 โ€” These use (or don't use) revision 2 of the MIPS DSP ASE ๐Ÿฆ‹. This option defines the preprocessor macros "__mips_dsp" and "__mips_dspr2", and it also defines "__mips_dsp_rev" to 2 ๐Ÿ‘‘.
  • -msmartmips, -mno-smartmips โ€” These use (or don't use) the MIPS SmartMIPS ASE ๐Ÿ’….
  • -mpaired-single, -mno-paired-single โ€” These use (or don't use) paired-single floating-point instructions ๐ŸŒˆ. This option needs hardware floating-point support to be enabled first ๐Ÿซถ.
  • -mdmx, -mno-mdmx โ€” These use (or don't use) MIPS Digital Media Extension instructions ๐Ÿ”ฅ. This only works when you're generating 64-bit code, and it also needs hardware floating-point support to be enabled ๐Ÿ˜ญ.
  • -mips3d, -mno-mips3d โ€” These use (or don't use) the MIPS-3D ASE ๐Ÿ’–. The option -mips3d implies -mpaired-single, so they come as a package deal ๐Ÿงš.
  • -mmicromips, -mno-micromips โ€” These 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 ๐Ÿ“.
  • -mmt, -mno-mt โ€” These use (or don't use) MT Multithreading instructions ๐ŸŒธ.
  • -mmcu, -mno-mcu โ€” These use (or don't use) the MIPS MCU ASE instructions ๐Ÿฆ‹.
  • -meva, -mno-eva โ€” These use (or don't use) the MIPS Enhanced Virtual Addressing instructions โœจ.
  • -mvirt, -mno-virt โ€” These use (or don't use) the MIPS Virtualization (VZ) instructions ๐Ÿ’œ.
  • -mxpa, -mno-xpa โ€” These use (or don't use) the MIPS eXtended Physical Address (XPA) instructions ๐Ÿ‘‘.
  • -mcrc, -mno-crc โ€” These use (or don't use) the MIPS Cyclic Redundancy Check (CRC) instructions ๐Ÿซถ.
  • -mginv, -mno-ginv โ€” These use (or don't use) the MIPS Global INValidate (GINV) instructions ๐ŸŒˆ.
  • -mmsa, -mno-msa โ€” These use (or don't use) the MIPS MSA extension instructions ๐Ÿ”ฅ.
  • -mloongson-mmi, -mno-loongson-mmi โ€” These use (or don't use) the MIPS Loongson MultiMedia extensions Instructions (MMI) ๐Ÿ’….
  • -mloongson-ext, -mno-loongson-ext โ€” These use (or don't use) the MIPS Loongson EXTensions (EXT) instructions ๐Ÿงš.
  • -mloongson-ext2, -mno-loongson-ext2 โ€” These use (or don't use) the MIPS Loongson EXTensions r2 (EXT2) instructions ๐ŸŽ€.
  • -mlong64 โ€” Force long types to be 64 bits wide ๐ŸŒธ. Check out -mlong32 for an explanation of the default and how the pointer size is determined ๐Ÿ’–.
  • -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 ints ๐Ÿ’œ. The n64 ABI uses 64-bit longs, and so does the 64-bit EABI, but the others use 32-bit longs ๐Ÿ‘‘. Pointers are the same size as longs, or the same size as integer registers, whichever is smaller โœจ.

  • -msym32, -mno-sym32 โ€” These assume (or don't assume) that all symbols have 32-bit values, no matter which ABI you picked ๐ŸŒˆ. This is super useful together with -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 ๐Ÿ“. This lets GCC generate more efficient accesses to the data, so check out -mgpopt for the details ๐Ÿงš. The default -G value depends on the configuration ๐Ÿ’….
  • -mlocal-sdata, -mno-local-sdata โ€” okay so these two either extend ๐Ÿ™Œ or don't extend the -G behavior to local data too, like static variables in C ๐Ÿ’…. -mlocal-sdata is the default for every configuration ๐Ÿ’–.

If the linker starts yelling that an application is hogging too much small data ๐Ÿ˜ญ, try rebuilding the less performance-critical bits with -mno-local-sdata. You can also build big libraries with -mno-local-sdata so they leave more room for the main program ๐Ÿซถ.

  • -mextern-sdata, -mno-extern-sdata โ€” this one decides whether GCC assumes (or doesn't assume ๐Ÿ™…โ€โ™€๏ธ) that externally-defined data lives in a small data section, as long as its size fits within the -G limit โœจ. -mextern-sdata is the default for all configurations ๐ŸŒธ.

Be careful here, bestie ๐Ÿ’€. If you compile a module Mod with -mextern-sdata -G num -mgpopt, and Mod references a variable Var that's no bigger than num bytes, you must make sure Var is placed in a small data section. If another module defines Var, you either compile that module with a high-enough -G setting or attach a "section" attribute to Var's definition. If Var is common, you have to link the application with a high-enough -G setting ๐Ÿ”ฅ.

The easiest way to avoid stress is to compile and link every module with the same -G option ๐Ÿงš. But if you're building a library that supports several different small data limits, you can compile it with the highest supported -G setting and also use -mno-extern-sdata, so the library stops making assumptions about externally-defined data ๐Ÿฆ‹.

  • -mgpopt, -mno-gpopt โ€” uses (or doesn't use) GP-relative accesses for symbols that are known to live in a small data section ๐Ÿง . See -G, -mlocal-sdata and -mextern-sdata for the full picture ๐Ÿ‘€. -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 โ€” allocates variables to the read-only data section first if possible, then to the small data section if possible, and otherwise to data ๐Ÿ“ฆ. It gives slightly slower code than the default ๐Ÿข, but it reduces the amount of RAM needed while executing, so it might be your fave for some embedded systems ๐ŸŒˆ.
  • -muninit-const-in-rodata, -mno-uninit-const-in-rodata โ€” puts uninitialized "const" variables in the read-only data section ๐ŸŽ€. This option only means something when paired with -membedded-data ๐Ÿซถ.
  • -mcode-readable=setting โ€” specifies whether GCC is allowed to generate code that reads from executable sections ๐Ÿ‘‘. There are three possible settings, babe:
    • -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 ๐Ÿ™…. 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 to have a dual instruction/data SRAM interface that (unlike the M4K) do not automatically redirect PC-relative loads to the instruction RAM ๐Ÿ“.

On SDE targets, -mcode-data-in-code is available as a traditional alias for -mcode-readable=no, and -mcode-xonly is the traditional alias for -mcode-readable=pcrel ๐Ÿ’….

  • -msplit-addresses, -mno-split-addresses โ€” enables (or disables) use of the %hi() and %lo() assembler relocation operators ๐Ÿงฉ. This option has been superseded by -mexplicit-relocs ๐Ÿ˜ค, but it's 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 that was around when GCC itself was built ๐Ÿ•ฐ๏ธ.

The "base" explicit-relocs support was introduced into GAS in 2001 ๐Ÿ“…. The "pcrel" explicit-relocs support was introduced into GAS in 2014, and it supports %pcrel_hi and %pcrel_lo ๐Ÿฆ‹.

  • -mcheck-zero-division, -mno-check-zero-division โ€” traps (or doesn'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 ๐Ÿ”. Using traps gives smaller code, but it's 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 using --with-divide=breaks ๐Ÿ› ๏ธ. Divide-by-zero checks can be completely disabled using -mno-check-zero-division ๐Ÿ™ˆ.

  • -mload-store-pairs, -mno-load-store-pairs โ€” enables (or disables) an optimization that pairs consecutive load or store instructions to enable load/store bonding ๐Ÿ”—. It's on 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 โ€” disables (or enables) direct unaligned access for MIPS Release 6 ๐Ÿ™…. MIPSr6 requires load/store unaligned-access support, either by hardware or by trapping and emulation ๐Ÿง . In the latter case, -mstrict-align may be needed by the operating system kernel ๐Ÿ–ฅ๏ธ. The options -munaligned-access and -mno-unaligned-access are obsolete and only provided for backward compatibility ๐Ÿ•ฐ๏ธ.
  • -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 โšก.
  • -mlong-calls, -mno-long-calls โ€” disables (or doesn't disable) use of the "jal" instruction ๐Ÿ“ž. Calling functions using "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 โ€” enables (or disables) use of the "mad", "madu" and "mul" instructions, as provided by the R4650 ISA ๐Ÿงฎ.
  • -mimadd, -mno-imadd โ€” enables (or disables) use of the "madd" and "msub" integer instructions ๐Ÿ”ข. The default is -mimadd on architectures that support "madd" and "msub", except for the 74k architecture, where it was found to generate slower code ๐ŸŒ.
  • -mfused-madd, -mno-fused-madd โ€” enables (or disables) use of the floating-point multiply-accumulate instructions when they're available ๐Ÿงฎ. The default is -mfused-madd โœจ.

On the R8000 CPU, when multiply-accumulate instructions are used, the intermediate product is calculated to infinite precision and is not subject to the FCSR Flush to Zero bit ๐ŸŒ€. This may be undesirable in some circumstances ๐Ÿค”. On other processors, the result is numerically identical to the equivalent computation using separate multiply, add, subtract and negate instructions ๐Ÿ’ฏ.

  • -nocpp โ€” tells the MIPS assembler to not run its preprocessor over user assembler files (with a .s suffix) when assembling them ๐Ÿ™…โ€โ™€๏ธ.
  • -mfix-24k, -mno-fix-24k โ€” works around the 24K E48 (lost data on stores during refill) errata ๐Ÿฉน. The workarounds are implemented by the assembler rather than by GCC ๐Ÿงฉ.
  • -mfix-r4000, -mno-fix-r4000 โ€” works around certain R4000 CPU errata ๐Ÿ› ๏ธ. There are three of them, so buckle up:
    • A double-word or a variable shift may give an incorrect result if executed immediately after starting an integer division.
    • A double-word or a variable shift may give an incorrect result if executed while an integer multiplication is in progress.
    • An integer division may give an incorrect result if 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 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; -mno-fix-r10000 is the default otherwise ๐ŸŽฏ.

  • -mfix-r5900, -mno-fix-r5900 โ€” do not attempt to schedule the preceding instruction into the delay slot of a branch instruction placed at the end of a short loop of six instructions or fewer. Instead, always schedule a "nop" instruction there ๐Ÿง˜. The short loop bug under certain conditions causes loops to execute only once or twice, due to a hardware bug in the R5900 chip ๐Ÿ˜ต. The workaround is implemented by the assembler rather than by 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 present, these functions are only provided by the "mips64vr*-elf" configurations ๐ŸŽฏ.

Other VR4120 errata require a NOP to be inserted between certain pairs of instructions ๐Ÿซ . These errata are handled by the assembler, not by GCC itself ๐Ÿงฉ.

Okay so the VR4130 has this sneaky mflo/mfhi errata ๐Ÿ™ˆ

  • -mfix-vr4130, -mno-fix-vr4130 โ€” these work around the VR4130 "mflo"/"mfhi" errata ๐Ÿฉน. The fix is handled by the assembler, not GCC, but GCC steers clear of mflo and mfhi when the VR4130 macc, macchi, dmacc and dmacchi instructions are available instead โœจ.
  • -mfix-sb1, -mno-fix-sb1 โ€” bestie, this one patches certain SB-1 CPU core errata ๐Ÿ’…. Right now it only handles the SB-1 revision 2 "F1" and "F2" floating-point errata ๐Ÿ”ฅ.
  • -mfix4300, -mno-fix4300 โ€” this works around a bug in early VR4300 silicon ๐Ÿ›. Multiplies with certain operands can corrupt the multiply that comes right after them ๐Ÿ’€.

๐Ÿฆ‹ -mr10k-cache-barrier=setting ๐Ÿฆ‹

This one controls whether GCC inserts cache barriers to dodge the side effects of speculation on R10K processors ๐Ÿ‘‘.

Okay, the vibe: lots of processors guess which way a conditional branch will go and speculatively run instructions from the "taken" branch ๐Ÿ”ฎ. If the guess was wrong, they abort those instructions later. The R10K is extra though, because even aborted instructions can leave side effects behind ๐Ÿ˜ญ.

This only really messes with kernel stores and, depending on the system, kernel loads ๐Ÿ™ˆ. For example, a speculatively-executed store might pull the target memory into cache and mark the line as dirty, even if the store gets aborted later. If a DMA operation writes to that same memory before the "dirty" line is flushed, the cached data overwrites the DMA-ed data ๐Ÿ’€. The R10K processor manual has the full scoop, including other potential problems ๐Ÿ“–.

One workaround is to insert cache barrier instructions before every memory access that might be speculatively executed and that might have side effects even if aborted ๐Ÿšง. -mr10k-cache-barrier=setting controls how GCC does this. It assumes that aborted accesses to any byte in the following regions have no 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 your input program has a function declaration like this:

void foo (void);

then the implementation of foo must allow j foo and jal foo to run speculatively ๐Ÿงš. GCC honors this for functions it compiles itself. It expects non-GCC functions (like hand-written assembly) 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 that might have side effects even if aborted ๐ŸŒธ.
  • -mr10k-cache-barrier=store โ€” inserts a cache barrier before a store that might be speculatively executed and that 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 โ€” picks the function GCC calls to flush the I and D caches, or tells it to skip calling any such function ๐ŸŒŠ. If you do call one, it has to take the same arguments as the common _flush_func: the address of the memory range being flushed, the size of that 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 ๐Ÿงฎ. Heads up, this is only a heuristic, so it isn't 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 Branch Likely instructions, no matter what the selected architecture defaults to ๐Ÿฆ„. By default they may be generated if the selected architecture supports them. The exception is the MIPS32 and MIPS64 architectures, plus processors that implement those. For those, Branch Likely instructions aren't generated by default, because MIPS32 and MIPS64 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 is 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 is used instead ๐Ÿข. If it's used for MIPS16/microMIPS targets, it's just ignored right now. The MIPS16/microMIPS behavior may change in the future, since those do have some compact branch instructions ๐Ÿ‘€.
    • -mcompact-branches=optimal โ€” uses a delay slot branch 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 is chosen when one is available โœจ.
  • -mfp-exceptions, -mno-fp-exceptions โ€” specifies whether FP exceptions are enabled ๐Ÿงฏ. This affects how FP instructions get scheduled on some processors. By default, 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 on, GCC aligns pairs of instructions that it thinks should execute in parallel ๐Ÿ’ซ. It only does anything when you're optimizing for the VR4130 ๐ŸŽฏ. It usually makes code faster, but at the cost 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 them ๐Ÿ”„. If enabled, the synci instructions get generated when __builtin___clear_cache is compiled ๐Ÿ› ๏ธ. This option defaults to -mno-synci, but you can override the default by configuring GCC with --with-synci โš™๏ธ. Compiling for single processor systems? It's generally safe to use synci ๐Ÿ˜Œ. But on many multi-core (SMP) systems, it doesn't invalidate the instruction caches on all cores, and it may lead to undefined behavior ๐Ÿšจ.
  • -mrelax-pic-calls, -mno-relax-pic-calls โ€” tries to turn PIC calls that are normally dispatched via register $25 into direct calls ๐ŸŽฏ. This only works if the linker can resolve the destination at link time, and if the destination is within range for a direct call ๐Ÿ“. -mrelax-pic-calls is the default if GCC was configured to use an assembler and linker that support the .reloc assembly directive and -mexplicit-relocs is in effect ๐Ÿงฉ. With -mno-explicit-relocs, this optimization can be done by the assembler and the linker alone, without help from the compiler ๐Ÿค.
  • -mmcount-ra-address, -mno-mcount-ra-address โ€” emits (or doesn't emit) code that lets _mcount modify the calling function's return address ๐Ÿช„. When enabled, this extends the usual _mcount interface with a new ra-address parameter. It has type intptr_t * and gets passed in register $12 ๐Ÿงต. _mcount can then modify the return address by doing both of the following:
    • Returning the new address in register $31.
    • Storing the new address in *ra-address, if ra-address is nonnull. The default is -mno-mcount-ra-address ๐Ÿ’….
  • -mframe-header-opt, -mno-frame-header-opt โ€” enables (or disables) frame header optimization in the o32 ABI ๐Ÿงบ. With the o32 ABI, calling a function allocates 16 bytes on the stack for the called function to write out register arguments. When enabled, this optimization skips allocating the frame header if it can be determined that it's unused ๐Ÿงน. Heads up, it's off by default at all optimization levels ๐Ÿ™…โ€โ™€๏ธ.
  • -mlxc1-sxc1, -mno-lxc1-sxc1 โ€” when applicable, enables (or disables) generation of lwxc1, swxc1, ldxc1, sdxc1 instructions ๐ŸŒŸ. Enabled by default ๐Ÿ’–.
  • -mmadd4, -mno-madd4 โ€” when applicable, enables (or disables) generation of 4-operand madd.s, madd.d and related instructions ๐Ÿงฎ. Enabled by default โœจ.

๐ŸŒŒ MMIX Options ๐ŸŒŒ

These options are defined for the MMIX ๐Ÿง‘โ€๐Ÿ’ป:

  • -mlibfuncs, -mno-libfuncs โ€” specifies that intrinsic library functions are being compiled, passing all values in registers, no matter the size ๐Ÿ“ฆ.
  • -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 that passes function parameters and return values that (in the called function) are seen as registers $0 and up ๐Ÿงบ. -mabi=gnu uses 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, uses (or doesn't use) zero-extending load instructions by default, instead of sign-extending ones ๐Ÿ”ข.
  • -mknuthdiv, -mno-knuthdiv โ€” makes the result of a division that yields a remainder have 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 used by the mmix simulator ๐ŸŽ.
  • -mbranch-predict, -mno-branch-predict โ€” uses (or doesn't use) the probable-branch instructions when static branch prediction indicates a probable branch ๐Ÿ”ฎ.
  • -mbase-addresses, -mno-base-addresses โ€” okay so this one decides if your code uses base addresses ๐Ÿ’– When it does, the assembler and linker set up a constant in a global register, and then a bunch of base address requests within 0 to 255 get measured from whatever's sitting in that register โœจ Generally that means short, fast code ๐Ÿ”ฅ BUT only a limited number of different data items can be addressed this way, so if your program uses lots of static data, you might need -mno-base-addresses ๐Ÿ™ˆ
  • -msingle-exit, -mno-single-exit โ€” this either forces or doesn't force the generated code to have one single exit point in each function ๐Ÿฆ‹
  • -mset-data-start=address โ€” when linking, this sets the start of the data section to address ๐ŸŽ€
  • -mset-program-start=address, -mno-set-program-start โ€” when linking, this sets the program start address to address ๐ŸŒธ It defaults to 0x100, unless you use -mno-set-program-start, which shuts this option off completely ๐Ÿ’…

๐Ÿฆ‹ MN10300 Options ๐Ÿฆ‹

These -m options are defined for Matsushita MN10300 architectures ๐ŸŒˆ

  • -mmult-bug, -mno-mult-bug โ€” when enabled, it generates code that dodges bugs in the multiply instructions for MN10300 processors ๐Ÿ›ก๏ธ This one's the default, periodt โœจ
  • -mam33, -mno-am33 โ€” generates code using features specific to the AM33 processor ๐Ÿ’œ The default is -mno-am33.
  • -mam33-2, -mno-am33-2 โ€” generates code using features specific to the AM33/2.0 processor ๐Ÿซถ The default is -mno-am33-2.
  • -mam34, -mno-am34 โ€” generates code using features specific to the AM34 processor ๐Ÿ“ The default is -mno-am34.
  • -mtune=cpu-type โ€” uses the timing characteristics of the CPU type you pick when scheduling instructions โฑ๏ธ This does not change the targeted processor type, so don't panic ๐Ÿ˜ญ The CPU type has to be one of mn10300, am33, am33-2 or am34.
  • -mreturn-pointer-on-d0, -mno-return-pointer-on-d0 โ€” when generating a function that returns a pointer, this returns the pointer in both "a0" and "d0" ๐Ÿ‘‘ Otherwise, the pointer only comes back in "a0", and trying to call those functions without a prototype throws errors ๐Ÿ’€ This option is on by default. To turn it off, use -mno-return-pointer-on-d0 ๐Ÿ™…โ€โ™€๏ธ
  • -mno-crt0 โ€” do 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 has an effect when you use it on the command line for the final link step.
Heads up bestie: this option makes symbolic debugging impossible ๐Ÿ’€
  • -mliw, -mno-liw โ€” lets the compiler generate Long Instruction Word instructions if the target is AM33 or later ๐ŸŒŸ This is enabled by default. -mliw defines the preprocessor macro "__LIW__", and -mno-liw defines the preprocessor macro "__NO_LIW__" ๐Ÿ’–
  • -msetlb, -mno-setlb โ€” lets the compiler generate the SETLB and Lcc instructions if the target is AM33 or later ๐Ÿฆ„ This is enabled by default. -msetlb defines the preprocessor macro "__SETLB__", and -mno-setlb 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, -mno-mul.x โ€” generates mul.x and umul.x instructions ๐Ÿง This option is enabled by default for moxiebox-*-* configurations.
  • -mno-crt0 โ€” do 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 these constants are signed decimals, but this one's around for testsuite and/or aesthetic purposes ๐ŸŽจ
  • -mmcu=name โ€” selects the MCU you're targeting ๐ŸŽฏ This creates a C preprocessor symbol based on the MCU name, converted to upper case and wrapped in __ on both ends. The msp430.h header file then uses that symbol to pick an MCU-specific supplementary header file ๐Ÿซง

The option also sets the ISA to use ๐Ÿง  If the MCU name is one that's known to only support the 430 ISA, that one gets selected. Otherwise, the 430X ISA gets selected. The generic MCU name msp430 can also 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 tacked on the end. So -mmcu=xxx defines the C preprocessor symbol "__XXX__" and makes the linker search for a script called xxx.ld ๐Ÿฅฐ

The ISA and hardware multiply support for each MCU is hard-coded into GCC ๐Ÿ—๏ธ However, an external devices.csv file can extend device support beyond those hard-coded ones, which is lowkey amazing ๐Ÿ™Œ

GCC searches for the devices.csv file using the methods below, in precedence order. The first method beats the second, and the second beats the third ๐Ÿ‘‘

  1. Include path specified with -I and -L โ€” devices.csv gets searched for in each of the directories specified by the include paths and linker library search paths ๐Ÿ“‚
  2. Path specified by the environment variable MSP430_GCC_INCLUDE_DIR โ€” define 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 it ๐Ÿ—‚๏ธ If devices.csv is found there, this directory also gets registered as an include path and linker library path. That means header files and linker scripts in this directory can be used without manually specifying -I and -L on the command line ๐Ÿซถ
  3. The msp430-elf{,bare}/include/devices directory โ€” finally, GCC checks msp430-elf{,bare}/include/devices from the toolchain root directory ๐ŸŒฒ This directory doesn't exist in a default installation. But if you've created it and copied devices.csv in there, the MCU data gets read. Like before, 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 gets used ๐Ÿฅน

  • -mwarn-mcu, -mno-warn-mcu โ€” this turns warnings on or off about conflicts between the MCU name from -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 โœ…
  • -msim โ€” links to the simulator runtime libraries and linker script ๐Ÿงช This overrides any scripts that would've been picked by -mmcu= ๐Ÿ’…
  • -mlarge โ€” uses large-model addressing, with 20-bit pointers and a 20-bit "size_t" ๐Ÿ˜
  • -msmall โ€” uses small-model addressing, with 16-bit pointers and a 16-bit "size_t" ๐Ÿ
  • -mrelax โ€” this gets passed to the assembler and linker, and lets the linker do certain optimizations that can't happen until the final link ๐Ÿง˜โ€โ™€๏ธ
  • mhwmult=type โ€” describes the type of hardware multiply the target supports ๐Ÿงฎ The accepted values for type are:
    • none for no hardware multiply ๐Ÿ™…
    • 16bit for the original 16-bit-only multiply supported by early MCUs ๐Ÿฆ–
    • 32bit for the 16/32-bit multiply supported by later MCUs ๐Ÿฆ•
    • f5series for the 16/32-bit multiply supported by F5-series MCUs ๐ŸŒŸ
    • auto, which tells GCC to figure out the hardware multiply support from the MCU name you gave with -mmcu ๐Ÿ•ต๏ธโ€โ™€๏ธ

If there's no -mmcu option, or the MCU name isn't recognized, then no hardware multiply support is assumed. "auto" is the default setting ๐Ÿ’œ

Hardware multiplies are normally performed by calling a library routine, which saves space in the generated code ๐Ÿ“ฆ When compiling at -O3 or higher, though, the hardware multiplier gets invoked inline. That makes the code bigger but faster ๐Ÿš€

The hardware multiply routines disable interrupts while they're running and restore the previous interrupt state when they finish ๐Ÿ›‘ This makes them safe to use inside interrupt handlers as well as in normal code ๐Ÿซถ

  • -minrt โ€” enables a minimum runtime environment with no support for static initializers or constructors ๐Ÿชถ This is meant for memory-constrained devices. The compiler puts special symbols in some objects that tell the linker and runtime which code fragments are required ๐Ÿงฉ
  • -mtiny-printf โ€” enables reduced code size "printf" and "puts" library functions ๐Ÿฃ
These tiny implementations are not reentrant, so be careful using them in multi-threaded applications ๐Ÿ˜ฌ

Support for streams has been removed, and the string to be printed is always sent to stdout via the "write" syscall ๐Ÿ“จ The string isn't buffered before it's sent to write.

This option requires Newlib Nano IO, so GCC must be configured with --enable-newlib-nano-formatted-io โš™๏ธ

  • -mmax-inline-shift=n โ€” this takes an integer n between 0 and 64 inclusive, and sets the maximum number of inline shift instructions that can be emitted to perform a shift by a constant amount ๐ŸŒ€ When that limit needs to be exceeded, an mspabi helper function is used instead. The default value is 4 ๐Ÿฐ

This only affects cases where a shift by multiple positions can't be finished with a single instruction, like all shifts greater than 1 on the 430 ISA ๐Ÿซง

Shifts of a 32-bit value are at least twice as costly, so the value you pass for this option gets divided by 2, and that resulting value is used instead ๐Ÿงฎ

  • -mcode-region=where, -mdata-region=where โ€” these tell the compiler where to place functions and data that don't have one of the "lower", "upper", "either" or "section" attributes ๐Ÿ—บ๏ธ The possible values for where are lower, upper, either or any. The first three behave like the corresponding attribute. The fourth value, any, is the default, and it leaves placement entirely up to the linker script and how it assigns the standard sections (".text", ".data", etc.) to the memory regions ๐ŸŒˆ
  • -muse-lower-region-prefix โ€” adds the .lower prefix to section names when compiling with -mcode-region=lower or -mdata-region=lower ๐ŸŽ€ This is disabled by default.
  • -msilicon-errata=name[,name...]: okay bestie, this one passes your request along to the assembler so it turns on the fixes for the silicon errata you name ๐Ÿ”งโœจ For the full scoop, check the assembler docs.
  • -msilicon-errata-warn=name[,name...]: same vibe, but instead of fixing things, the assembler just warns you when a named silicon errata might need a fix ๐Ÿ‘€ Again, the assembler docs have the details.
  • -mwarn-devices-csv, -mno-warn-devices-csv: this one warns you if devices.csv is missing or if there are problems parsing it. It's on by default ๐Ÿ’…

โœจ NDS32 Options โœจ

These options are defined for NDS32 implementations ๐ŸŒธ

  • -mbig-endian, -EB: generates code in big-endian mode ๐Ÿฆ‹
  • -mlittle-endian, -EL: generates code in little-endian mode ๐Ÿซถ
  • -mabi=2, -mfloat-abi=soft: uses the soft floating-point ABI ๐Ÿ“
  • -mabi=2fp+, -mfloat-abi=hard: uses the hard floating-point ABI ๐Ÿ”ฅ
  • -mreduced-regs: uses the reduced-set registers for register allocation ๐ŸŽ€
  • -mfull-regs: uses the full-set registers for register allocation ๐Ÿ‘‘
  • -malways-align: always aligns function entry, jump targets, and return address. Main character alignment energy ๐Ÿงš
  • -malign-functions: aligns function entry to 4 bytes ๐Ÿ“
  • -mfp-as-gp, -mno-fp-as-gp: enables or disables the fp-as-gp optimization ๐Ÿ’œ
  • -mcmov, -mno-cmov: enables or disables generation of conditional move instructions โœจ
  • -mhw-abs, -mno-hw-abs: enables or disables generation of hardware abs instructions ๐ŸŒˆ
  • -mext-perf, -mno-ext-perf: enables or disables generation of performance extension instructions โญ
  • -mext-perf2, -mno-ext-perf2: enables or disables generation of performance extension 2 instructions ๐Ÿ’–
  • -mext-string, -mno-ext-string: enables or disables generation of string extension instructions ๐Ÿซถ
  • -mext-dsp, -mno-ext-dsp: enables or disables generation of DSP extension instructions ๐Ÿฆ‹
  • -mext-fpu-fma, -mno-ext-fpu-fma: enables or disables generation of floating-point multiply-accumulation instructions ๐Ÿ”ฅ
  • -mext-fpu-sp, -mno-ext-fpu-sp: enables or disables generation of single-precision floating-point instructions ๐ŸŒธ
  • -mext-fpu-dp, -mno-ext-fpu-dp: enables or disables generation of double-precision floating-point instructions ๐Ÿ‘‘
  • -mv3push, -mno-v3push: enables or disables generation of v3 push25/pop25 instructions ๐Ÿ“
  • -m16-bit, -mno-16-bit: enables or disables generation of 16-bit instructions ๐Ÿ’…
  • -mvh, -mno-vh: enables or disables Virtual Hosting support ๐ŸŽ€
  • -misr-vector-size=num: specifies the size of each interrupt vector, which must be 4 or 16 ๐Ÿงš
  • -misr-secure=num: specifies the security level of c-isr for the whole file ๐Ÿ”
  • -mcache-block-size=num: specifies the size of each cache block, which must be a power of 2 between 4 and 512 ๐Ÿ’œ
  • -march=arch: specifies the target architecture. arch may be one of v2, v3, v3j, v3m, v3f, or v3s โœจ
  • -mcpu=cpu: specifies the CPU to compile for. cpu may be one of n6, n650, n7, n705, n8, n801, sn8, sn801, s8, s801, e8, e801, n820, s830, e830, n9, n903, n903a, n968, n968a, n10, n1033, n1033a, n1033-fpu, n1033-spu, n1068, n1068a, n1068-fpu, n1068a-fpu, d10, d1088, d1088-fpu, d1088-spu, n15, d15, n15s, d15s, n15f, d15f, n12, n1213, n1233, n1233-fpu, n1233-spu, n13, n1337, n1337-fpu, n1337-spu, or simple ๐ŸŒˆ
  • -mconfig-fpu=num: specifies an FPU configuration value from 0 to 7. 0--3 is as the FPU spec says, and 4--7 correspond to 0--3 ๐Ÿซถ
  • -mconfig-mul=type: specifies the configuration for the multiply instruction. type can be one of fast, fast1 (equivalent to fast), fast2, or slow. The default is fast1 ๐Ÿฆ‹
  • -mconfig-register-ports=kind: specifies the numbers of read/write ports for n9/n10 cores. The value should be 3r2w or 2r1w ๐Ÿ“
  • -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 addressing space. The text segment must be within 16MB addressing space.
    • medium: the data segment must be within 512KB while the read-only data segment can be within 4GB addressing space. The text segment should be still within 16MB addressing space.
    • large: all the text and data segments can be within 4GB addressing space.

The default is large on GNU/Linux targets and medium on other ELF targets ๐Ÿ’–

  • -mctor-dtor, -mno-ctor-dtor: enables or disables the constructor/destructor feature ๐Ÿงš
  • -mrelax, -mno-relax: enables or disables the linker option to relax instructions ๐Ÿ’…
  • -mrelax-hint, -mno-relax-hint: enables or disables insertion of hints for the linker to do relaxation ๐ŸŽ€
  • -msched-prolog-epilog, -mno-sched-prolog-epilog: enables or disables scheduling of a function's prologue and epilogue sequence ๐Ÿ‘‘
  • -mret-in-naked-func, -mno-ret-in-naked-func: enables or disables generation of return instructions in naked functions. This option is enabled by default ๐ŸŒˆ
  • -malways-save-lp, -mno-always-save-lp: enables or disables always saving $lp on the stack ๐Ÿ’œ
  • -munaligned-access, -mno-unaligned-access: enables or disables unaligned word and halfword accesses to packed data ๐Ÿ“
  • -minline-asm-r15, -mno-inline-asm-r15: allows or disallows use of r15 for inline asm ๐Ÿซถ

โœจ Nvidia PTX Options โœจ

These options are defined for Nvidia PTX ๐Ÿ’…

  • -m64: ignored, but preserved for backward compatibility. Only the 64-bit ABI is supported ๐Ÿ”ฅ
  • -march=architecture-string: generates code for the specified PTX ISA target architecture. 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 has been configured, see --with-arch โœจ

This option sets the value of the preprocessor macro "__PTX_SM__". For instance, for sm_35, it has the value 350 ๐Ÿงš

  • -misa=architecture-string: an alias of -march= ๐ŸŒธ
  • -march-map=architecture-string: selects the closest available -march= value that is not more capable. For instance, for -march-map=sm_50 it selects -march=sm_37, and for -march-map=sm_53 it selects -march=sm_53 ๐Ÿ—บ๏ธ
  • -mptx=version-string: generates code for the specified PTX ISA version. 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 the one that added support for the selected PTX ISA target architecture, see -march=, but 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 instance, 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 instead of offloading execution ๐Ÿซถ
  • -moptimize, -mno-optimize: enables or disables partitioned execution optimizations. This option is enabled by default when any level of optimization is selected ๐ŸŒˆ
  • -msoft-stack, -mno-soft-stack: ๐Ÿ“

For -mno-soft-stack (the default, unless -mgomp has been specified), use PTX "native" stacks, that is, generate code that 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.

For -msoft-stack (implied by -mgomp), generate code that does not use ".local" memory or PTX "alloca" directly for stack storage. Instead, a per-warp stack pointer is maintained explicitly. This enables variable-length stack allocation (with variable-length arrays or "alloca"), and when global memory is used for underlying storage, makes it possible to 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 for the purpose of testing the compiler. To generate code suitable for linking into programs using OpenMP offloading, use option -mgomp ๐Ÿงš

  • -msoft-stack-reserve-local=size: specifies the size of ".local" memory used for the stack when the exact amount is not known. It defaults to 128 ๐Ÿ’œ
  • -muniform-simt, -mno-uniform-simt: enables or disables a code generation variant that allows execution of all threads in each warp, while maintaining memory state and side effects as if only one thread in each warp was active outside of OpenMP SIMD regions. All atomic operations and calls to runtime ("malloc", "free", "vprintf") are conditionally executed (iff current lane index equals the master lane index), and the register being assigned is copied via a shuffle instruction from the master lane. Outside of SIMD regions lane 0 is the master; inside, each thread sees itself as the master. Shared memory array "int __nvptx_uni[]" stores all-zeros or all-ones bitmasks for each warp, indicating current mode (0 outside of SIMD regions). Each thread can bitwise-and the bitmask at position "tid.y" with current lane index to compute the master lane index ๐Ÿ‘‘
  • -mgomp, -mno-gomp: enables or disables generation of code for use in OpenMP offloading. -mgomp enables -msoft-stack and -muniform-simt options, and selects a corresponding multilib variant ๐ŸŒธ

โœจ OpenRISC Options โœจ

These options are defined for OpenRISC ๐ŸŒˆ

  • -mboard=name: configures a board specific runtime. This is passed to the linker for newlib board library linking. The default is "or1ksim" ๐Ÿฆ‹
  • -msoft-div, -mhard-div: selects software or hardware divide ("l.div", "l.divu") instructions. The default is hardware divide ๐Ÿ“
  • -msoft-mul, -mhard-mul: selects software or hardware multiply ("l.mul", "l.muli") instructions. The default is hardware multiply ๐Ÿ’–
  • -msoft-float, -mhard-float: selects software or hardware for floating point operations. The default is software ๐Ÿซถ
  • -mdouble-float: when -mhard-float is selected, 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, enables generation of unordered floating point compare and set flag ("lf.sfun*") instructions. By default, functions from libgcc are used to perform unordered floating point compare and set flag operations ๐Ÿ’…
  • -mcmov โ€” okay so this turns on conditional move (l.cmov) instructions ๐Ÿ’…. Without it, GCC builds the same effect with set and branch instead.
  • -mror โ€” enables l.ror rotate-right instructions โœจ. By default, rotate-right operations go through functions from libgcc.
  • -mrori โ€” enables l.rori rotate-right-with-immediate instructions ๐Ÿฆ‹. By default, these also go through libgcc functions.
  • -msext โ€” enables l.ext* sign extension instructions ๐ŸŒˆ. By default, sign extension is handled with memory loads.
  • -msfimm โ€” enables l.sf*i compare-and-set-flag-with-immediate instructions ๐Ÿ”ฅ. By default, GCC adds extra instructions to store the immediate in a register first.
  • -mshftimm โ€” enables l.srai, l.srli, and l.slli shift-with-immediate instructions ๐Ÿ’œ. By default, extra instructions store the immediate in a register first.
  • -mcmodel=small โ€” generates OpenRISC code for the small model ๐ŸŒธ. The GOT is limited to 64k, and function call jumps are limited to 64M offsets. This is the default model.
  • -mcmodel=large โ€” generates OpenRISC code for the large model ๐Ÿ‘‘. The GOT can grow up to 4G in size, and function call jumps can target the full 4G address space.

๐ŸŒธ PDP-11 Options ๐ŸŒธ

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 โ€” skips hardware floating point ๐Ÿ™ˆ.
  • -mac0, -mno-ac0 โ€” with -mac0, floating-point results come back in ac0 (fr0 in Unix assembler syntax) ๐Ÿ’…. The default, -mno-ac0, returns floating-point results in memory.
  • -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 a 16-bit int ๐Ÿ’–. This is the default.
  • -mint32, -mno-int16 โ€” uses a 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 seriously shrink the final ELF binary, but some standard C runtime features get removed.

This option disables support for static initializers and constructors. Beware, because the compiler could still generate code with static initializers and constructors. It's up to the programmer to make sure the source program doesn't use those features.

The minimal startup code does not pass argc and argv arguments to main, so main has to be declared as int main (void). This is already the norm for most firmware projects โœจ.

  • -mmcu=mcu โ€” picks the PRU hardware variant to use ๐Ÿ”ฅ. A correspondingly named spec file gets loaded, which passes the memory region sizes to the linker and defines hardware-specific C macros.

Newlib only provides the sim spec, which is meant for running regression tests with a simulator ๐Ÿ™ˆ. Specs for real hardware can be obtained by installing the GnuPruMcu package at https://github.com/dinuxbg/gnuprumcu/ ๐Ÿซถ.

  • -mno-relax โ€” makes GCC pass --no-relax to the linker instead of --relax ๐Ÿ’€.
  • -mloop, -mno-loop โ€” allows (or doesn't allow) GCC to use the LOOP instruction ๐Ÿ“.
  • -mmul, -mno-mul โ€” allows (or doesn't allow) GCC to use the PRU multiplier unit ๐Ÿ’–.
  • -mfillzero, -mno-fillzero โ€” allows (or doesn't allow) GCC to use the FILL and ZERO instructions ๐ŸŒธ.
  • -mabi=variant โ€” picks 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. Here's how they differ:
    • Function Pointer Size โ€” TI ABI says function (code) pointers are 16-bit, whereas GNU only supports 32-bit data and code pointers ๐ŸŽ€.
    • Optional Return Value Pointer โ€” return values larger than 64 bits get passed through a hidden pointer as the first argument of the function ๐Ÿฆ‹. TI ABI mandates that this pointer can be NULL if the caller isn't using the returned value. GNU always passes and expects a valid return value pointer ๐Ÿ’….
    • Size Of Struct Containing Bit-fields โ€” TI ABI says the struct size is determined by the bit-field type, if it has any ๐Ÿงš. GNU allocates the smallest number of bytes that would fit the bit-field โœจ.

For example, TI ABI reserves 4 bytes for this struct, whereas GNU reserves a single byte ๐Ÿ™ˆ:

        struct S { int i:1; };
  • Access Size For Volatile Bit-fields โ€” TI ABI mandates that volatile bit-fields are accessed using their type ๐Ÿ”ฅ. GNU ABI instead uses the smallest integer type that fits the bit-field ๐Ÿ’œ.

For example, TI ABI requires a single load of 4 bytes for the following bit-field. GNU generates a load of 1 byte ๐Ÿ’€:

        struct S { volatile int i:1; };

The current -mabi=ti implementation just raises a compile error whenever any of the above code constructs shows up ๐Ÿ˜ญ. As a result, the standard C library can't be built, so it's omitted when linking with -mabi=ti.

Relaxation is a GNU feature, and for safety reasons it's disabled when using -mabi=ti. The TI toolchain doesn't emit relocations for QBBx instructions, so the GNU linker can't adjust them when it shortens 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 ๐Ÿ’….
  • -mabi=ABI-string โ€” picks the integer and floating-point calling convention โœจ. An 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 long and pointers are 64-bit (which implicitly makes int 32-bit), and 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 there's -march=rv64ifd -mabi=lp64, where no floating-point arguments get passed in registers ๐Ÿ’€.

The default for this argument is system dependent, so if you want a specific calling convention, you should specify one explicitly ๐Ÿซถ. The valid calling conventions are ilp32, ilp32f, ilp32d, lp64, lp64f, and lp64d ๐Ÿ’–. Some calling conventions are impossible 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's also the ilp32e ABI, which can only be used with the rv32e architecture, and the lp64e ABI, which can only be used with rv64e ๐ŸŽ€.

The ilp32e and lp64e ABIs aren't well-specified at present, and they're 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 ๐Ÿ’…. It's unimplemented on some processors, including those from T-Head ๐Ÿ™ˆ. If the fence.tso instruction isn't 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 โ€” picks the version of the RISC-V Unprivileged (formerly User-Level) ISA specification that generated code should conform to ๐Ÿงš. The possible values 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|Profile|Profile_ISA|processor-string] โ€” generates code for a given RISC-V ISA, profile, or a combination of them (for example, rv64im, rvi20u64, or rvi20u64_zbb) ๐Ÿ‘‘. ISA and profile names must be lower case. Examples include rv64i, rv32g, rv32e, rv32imaf, rva22u64, and rva23u64 ๐Ÿ’…. To combine a named profile with optional RISC-V ISA extensions, put the profile first and then append the extension name(s) with an underscore as the delimiter (for example, rvi20u64_zca_zcb or rva23u64_zacas) โœจ. There's also a special value, help (-march=help), which lists all supported extensions ๐Ÿ”ฅ.

-march=unset makes the compiler ignore any -march=... options that appear earlier on the command line, behaving as if that option was never passed ๐Ÿฆ‹. This is handy for making sure the architecture comes from the -mcpu option. An error results if no -mcpu option is given when -march=unset is used ๐Ÿ’€.

The syntax of the ISA string is defined as follows:

  • The string must start with rv32 or rv64, followed by i, e, or g. These are called the base ISA ๐Ÿงš.
  • The rest of the string is a list of extension names. Extension names can be multi-letter (for example, zba) or single-letter (for example, v). Single-letter extensions can appear back to back, 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's used to improve readability and can act as a separator ๐Ÿซถ.

okay so extension names can carry a version too ๐Ÿ’… the syntax is <major>p<minor> or just <major>, so m2p1 or m2 both work ๐Ÿฆ‹ here's the full lineup, with the version(s) each one supports and what it's about. quick heads up: a version of - just means there isn't one listed ๐Ÿซถ

  • g (version -) โ€” the general-purpose computing base extension โœจ g is shorthand that expands to i, m, a, f, d, zicsr and zifencei, so you get all of them in one go ๐Ÿ‘‘
  • e (version 2.0) โ€” the reduced base integer extension ๐ŸŒธ
  • i (versions 2.0 and 2.1) โ€” the base integer extension ๐Ÿ’–
  • m (version 2.0) โ€” integer multiplication and division, obsessed ๐Ÿ”ฅ
  • a (versions 2.0 and 2.1) โ€” the atomic extension ๐Ÿ’œ
  • f (versions 2.0 and 2.2) โ€” single-precision floating-point ๐Ÿฆ‹
  • d (versions 2.0 and 2.2) โ€” double-precision floating-point, iconic ๐ŸŒˆ
  • c (version 2.0) โ€” the compressed extension ๐Ÿงš
  • b (version 1.0) โ€” the standard extension for bit manipulation functions ๐Ÿ“
  • v (version 1.0) โ€” the vector extension โœจ
  • h (version 1.0) โ€” the hypervisor extension ๐Ÿ‘‘
  • zic64b (version 1.0) โ€” cache block size is 64 bytes ๐ŸŽ€
  • zicbom (version 1.0) โ€” cache-block management, main character energy ๐Ÿ’…
  • zicbop (version 1.0) โ€” cache-block prefetch ๐ŸŒธ
  • zicboz (version 1.0) โ€” cache-block zero ๐Ÿฆ‹
  • ziccamoa (version 1.0) โ€” main memory supports all atomics in A ๐Ÿ’œ
  • ziccif (version 1.0) โ€” main memory supports instruction fetch with an atomicity requirement ๐Ÿซถ
  • zicclsm (version 1.0) โ€” main memory supports misaligned loads and stores ๐ŸŒˆ
  • ziccrse (version 1.0) โ€” main memory supports forward progress on LR/SC sequences โœจ
  • zicfilp (version 1.0) โ€” control-flow integrity landing pad extension ๐Ÿ”ฅ
  • zicfiss (version 1.0) โ€” control-flow integrity shadow stack extension ๐Ÿ‘‘
  • zicntr (version 2.0) โ€” the standard extension for base counters and timers โญ
  • zicond (version 1.0) โ€” integer conditional operations ๐Ÿงš
  • zicsr (version 2.0) โ€” control and status register access, slay ๐Ÿ’…
  • zifencei (version 2.0) โ€” instruction-fetch fence ๐Ÿ“
  • zihintntl (version 1.0) โ€” non-temporal locality hints ๐ŸŒธ
  • zihintpause (version 2.0) โ€” pause hint ๐Ÿฆ‹
  • zihpm (version 2.0) โ€” the standard extension for hardware performance counters ๐Ÿ’–
  • zimop (version 1.0) โ€” may-be-operations ๐ŸŒˆ
  • zilsd (version 1.0) โ€” load/store pair instructions โœจ
  • zmmul (version 1.0) โ€” integer multiplication ๐Ÿ’œ
  • za128rs (version 1.0) โ€” reservation set size of 128 bytes ๐ŸŽ€
  • za64rs (version 1.0) โ€” reservation set size of 64 bytes ๐ŸŽ€
  • zaamo (version 1.0) โ€” atomic memory operations ๐Ÿ”ฅ
  • zabha (version 1.0) โ€” byte and halfword atomic memory operations ๐Ÿ’…
  • zacas (version 1.0) โ€” atomic compare-and-swap instructions ๐Ÿ‘‘
  • zalrsc (version 1.0) โ€” the load-reserved/store-conditional subset of the A extension ๐Ÿซถ
  • zawrs (version 1.0) โ€” wait-on-reservation-set ๐Ÿงš
  • zama16b (version 1.0) โ€” misaligned loads, stores, and AMOs that are fully contained within a naturally-aligned 16-byte boundary are atomic ๐ŸŒธ
  • zfa (version 1.0) โ€” additional floating-point ๐Ÿฆ‹
  • zfbfmin (version 1.0) โ€” minimal BF16 support ๐Ÿ’–
  • zfh (version 1.0) โ€” half-precision floating-point ๐ŸŒˆ
  • zfhmin (version 1.0) โ€” minimal half-precision floating-point โœจ
  • zfinx (version 1.0) โ€” single-precision floating-point in integer registers ๐Ÿ’œ
  • zdinx (version 1.0) โ€” double-precision floating-point in integer registers ๐Ÿ”ฅ
  • zca (version 1.0) โ€” integer compressed instructions ๐Ÿงš
  • zcb (version 1.0) โ€” simple compressed instructions ๐Ÿ“
  • zcd (version 1.0) โ€” compressed double-precision floating-point loads and stores ๐ŸŒธ
  • zce (version 1.0) โ€” compressed instruction extensions for embedded processors ๐ŸŽ€
  • zcf (version 1.0) โ€” compressed single-precision floating-point loads and stores ๐Ÿฆ‹
  • zcmop (version 1.0) โ€” compressed may-be-operations ๐ŸŒˆ
  • zcmp (version 1.0) โ€” compressed push and pop ๐Ÿ’…
  • zcmt (version 1.0) โ€” table jump instructions ๐Ÿ‘‘
  • zclsd (version 1.0) โ€” compressed load/store pair instructions ๐Ÿซถ
  • zba (version 1.0) โ€” address calculation ๐Ÿ’–
  • zbb (version 1.0) โ€” basic bit manipulation ๐Ÿ”ฅ
  • zbc (version 1.0) โ€” carry-less multiplication โœจ
  • zbkb (version 1.0) โ€” cryptography bit-manipulation ๐Ÿ’œ
  • zbkc (version 1.0) โ€” cryptography carry-less multiply ๐ŸŒˆ
  • zbkx (version 1.0) โ€” cryptography crossbar permutation ๐Ÿงš
  • zbs (version 1.0) โ€” single-bit operations ๐Ÿ“
  • zk (version 1.0) โ€” the standard scalar cryptography extension ๐Ÿ”โœจ
  • zkn (version 1.0) โ€” the NIST algorithm suite extension ๐ŸŒธ
  • zknd (version 1.0) โ€” AES decryption ๐Ÿฆ‹
  • zkne (version 1.0) โ€” AES encryption ๐Ÿ’–
  • zknh (version 1.0) โ€” hash functions ๐ŸŒˆ
  • zkr (version 1.0) โ€” entropy source โœจ
  • zks (version 1.0) โ€” the ShangMi algorithm suite extension ๐Ÿ’œ
  • zksed (version 1.0) โ€” SM4 block cipher ๐Ÿ”ฅ
  • zksh (version 1.0) โ€” SM3 hash function ๐Ÿงš
  • zkt (version 1.0) โ€” data independent execution latency ๐Ÿ“
  • ztso (version 1.0) โ€” total store ordering ๐Ÿ‘‘
  • zvbb (version 1.0) โ€” vector basic bit-manipulation ๐Ÿซถ
  • zvbc (version 1.0) โ€” vector carry-less multiplication ๐ŸŒธ
  • zve32f (version 1.0) โ€” vector extensions for embedded processors ๐ŸŽ€
  • zve32x (version 1.0) โ€” vector extensions for embedded processors ๐ŸŽ€
  • zve64d (version 1.0) โ€” vector extensions for embedded processors ๐Ÿฆ‹
  • zve64f (version 1.0) โ€” vector extensions for embedded processors ๐Ÿฆ‹
  • zve64x (version 1.0) โ€” vector extensions for embedded processors ๐ŸŒˆ
  • zvfbfmin (version 1.0) โ€” vector BF16 converts ๐Ÿ’…
  • zvfbfwma (version 1.0) โ€” vector BF16 widening multiply/add ๐Ÿ‘‘
  • zvfh (version 1.0) โ€” vector half-precision floating-point ๐Ÿ’–
  • zvfhmin (version 1.0) โ€” vector minimal half-precision floating-point ๐Ÿ”ฅ
  • zvkb (version 1.0) โ€” vector cryptography bit-manipulation โœจ
  • zvkg (version 1.0) โ€” vector GCM/GMAC ๐Ÿ’œ
  • zvkn (version 1.0) โ€” vector NIST Algorithm Suite, where zvkn will expand to (the page just trails off right there ๐Ÿ’€)
  • zvknc (version 1.0) โ€” vector NIST Algorithm Suite with carry-less multiply, zvknc ๐Ÿงš
  • zvkned (version 1.0) โ€” vector AES block cipher ๐Ÿ“
  • zvkng (version 1.0) โ€” vector NIST Algorithm Suite with GCM, where zvkng will expand (again, cut off here ๐Ÿ™ˆ)
  • zvknha (version 1.0) โ€” vector SHA-2 secure hash ๐ŸŒธ
  • zvknhb (version 1.0) โ€” vector SHA-2 secure hash ๐Ÿฆ‹
  • zvks (version 1.0) โ€” vector ShangMi algorithm suite, where zvks will expand (cut off ๐Ÿ™ˆ)
  • zvksc (version 1.0) โ€” vector ShangMi algorithm suite with carry-less multiplication, and the page stops right after the comma ๐Ÿ™ˆ
  • zvksed (version 1.0) โ€” vector SM4 block cipher ๐Ÿ’–
  • zvksg (version 1.0) โ€” vector ShangMi algorithm suite with GCM ๐ŸŒˆ
  • zvksh (version 1.0) โ€” vector SM3 secure hash โœจ
  • zvkt (version 1.0) โ€” vector data independent execution latency ๐Ÿ’œ
  • zvl1024b (version 1.0) โ€” minimum vector length standard extensions ๐Ÿ”ฅ
  • zvl128b (version 1.0) โ€” minimum vector length standard extensions ๐Ÿงš
  • zvl16384b (version 1.0) โ€” minimum vector length standard extension ๐Ÿ“
  • zvl2048b (version 1.0) โ€” minimum vector length standard extensions ๐Ÿ‘‘
  • zvl256b (version 1.0) โ€” minimum vector length standard extensions ๐Ÿซถ
  • zvl32768b (version 1.0) โ€” minimum vector length standard extension ๐ŸŒธ
  • zvl32b (version 1.0) โ€” minimum vector length standard extensions ๐ŸŽ€
  • zvl4096b (version 1.0) โ€” minimum vector length standard extensions ๐Ÿฆ‹
  • zvl512b (version 1.0) โ€” minimum vector length standard extensions ๐ŸŒˆ
  • zvl64b (version 1.0) โ€” minimum vector length standard extensions ๐Ÿ’…
  • zvl65536b (version 1.0) โ€” minimum vector length standard extension ๐Ÿ‘‘
  • zvl8192b (version 1.0) โ€” minimum vector length standard extension ๐Ÿ’–
  • zhinx (version 1.0) โ€” half-precision floating-point in integer registers ๐Ÿ”ฅ
  • zhinxmin (version 1.0) โ€” minimal half-precision floating-point in integer registers โœจ
  • sdtrig (version 1.0) โ€” debug triggers ๐Ÿ’œ
  • sha (version 1.0) โ€” the augmented hypervisor extension ๐ŸŒˆ
  • shcounterenw (version 1.0) โ€” writeable enables for any supported counter ๐Ÿงš
  • shgatpa (version 1.0) โ€” SvNNx4 mode supported for all modes that satp supports ๐Ÿ“
  • shlcofideleg (version 1.0) โ€” delegating LCOFI interrupts to VS-mode ๐Ÿ‘‘
  • shtvala (version 1.0) โ€” the htval register provides all the values needed ๐Ÿซถ
  • shvstvala (version 1.0) โ€” the vstval register provides all the values needed ๐ŸŒธ
  • shvstvecd (version 1.0) โ€” the vstvec register supports direct mode ๐ŸŽ€
  • shvsatpa (version 1.0) โ€” the vsatp register supports all modes that satp supports ๐Ÿฆ‹
  • smaia (version 1.0) โ€” advanced interrupt architecture ๐ŸŒˆ
  • smcntrpmf (version 1.0) โ€” cycle and instret privilege mode filtering ๐Ÿ’…
  • smcsrind (version 1.0) โ€” machine-level indirect CSR access ๐Ÿ‘‘
  • smepmp (version 1.0) โ€” PMP enhancements for memory access and execution prevention in machine mode ๐Ÿ’–
  • smmpm (version 1.0) โ€” supervisor-mode pointer masking ๐Ÿ”ฅ
  • smnpm (version 1.0) โ€” supervisor-mode pointer masking, same vibe as smmpm โœจ
  • smrnmi (version 1.0) โ€” resumable non-maskable interrupts ๐Ÿ’œ
  • smstateen (version 1.0) โ€” state enable ๐ŸŒˆ
  • smdbltrp (version 1.0) โ€” double trap extensions ๐Ÿงš
  • ssaia (version 1.0) โ€” advanced interrupt architecture for supervisor mode ๐Ÿ“

Okay so the extension list keeps going, bestie ๐Ÿซถ and every single one of these is giving main character energy โœจ

  • ssccptr (v1.0) โ€” main memory supports page table reads ๐Ÿ’–
  • sscofpmf (v1.0) โ€” count overflow and filtering extension ๐Ÿฆ‹
  • sscounterenw (v1.0) โ€” writeable enables for any supported counter, iconic ๐ŸŒธ
  • sscsrind (v1.0) โ€” supervisor-mode indirect CSR access
  • ssnpm (v1.0) โ€” supervisor-mode pointer masking extension
  • sspm (v1.0) โ€” same vibe as ssnpm, supervisor-mode pointer masking extension ๐Ÿ™ˆ
  • ssstateen (v1.0) โ€” supervisor-mode state-enable extension
  • sstc (v1.0) โ€” supervisor-mode timer interrupts extension โฐ
  • sstvala (v1.0) โ€” stval provides all the values you need โœจ
  • sstvecd (v1.0) โ€” stvec supports direct mode ๐Ÿ’…
  • ssstrict (v1.0) โ€” unimplemented reserved encodings raise illegal instruction exceptions, and no non-conforming extensions are present ๐Ÿ’€
  • ssdbltrp (v1.0) โ€” double trap extensions
  • ssu64xl (v1.0) โ€” UXLEN=64 must be supported
  • supm (v1.0) โ€” user-mode pointer masking extension
  • svinval (v1.0) โ€” fine-grained address-translation cache invalidation extension
  • svnapot (v1.0) โ€” NAPOT translation contiguity extension ๐ŸŒˆ
  • svpbmt (v1.0) โ€” page-based memory types extension
  • svvptc (v1.0) โ€” extension that makes memory-management instructions unnecessary after you mark PTEs valid ๐Ÿงš
  • svadu (v1.0) โ€” hardware updating of A/D bits extension
  • svade (v1.0) โ€” cause exception when hardware updating of A/D bits is disabled
  • svbare (v1.0) โ€” satp mode bare is supported
  • xcvalu (v1.0) โ€” Core-V miscellaneous ALU extension
  • xcvbi (v1.0) โ€” Core-V immediate branch extension
  • xcvelw (v1.0) โ€” Core-V event load word extension
  • xcvmac (v1.0) โ€” Core-V multiply-accumulate extension
  • xcvsimd (v1.0) โ€” Core-V SIMD extension
  • xsfcease (v1.0) โ€” SiFive CEASE instruction extension
  • xsfvcp (v1.0) โ€” SiFive VCIX vector coprocessor extension
  • xsfvfnrclipxfqf (v1.0) โ€” SiFive FP32-to-int8 ranged clip instructions
  • xsfvqmaccdod (v1.0) โ€” SiFive int8 matrix multiplication extension
  • xsfvqmaccqoq (v1.0) โ€” SiFive int8 matrix multiplication extension ๐Ÿ”ฅ
  • xtheadba (v1.0) โ€” T-head address calculation extension
  • xtheadbb (v1.0) โ€” T-head basic bit-manipulation extension
  • xtheadbs (v1.0) โ€” T-head single-bit instructions extension
  • xtheadcmo (v1.0) โ€” T-head cache management operations extension
  • xtheadcondmov (v1.0) โ€” T-head conditional move extension
  • xtheadfmemidx (v1.0) โ€” T-head indexed memory operations for floating-point registers extension
  • xtheadfmv (v1.0) โ€” T-head double floating-point high-bit data transmission extension
  • xtheadint (v1.0) โ€” T-head acceleration interruption extension
  • xtheadmac (v1.0) โ€” T-head multiply-accumulate extension
  • xtheadmemidx (v1.0) โ€” T-head indexed memory operation extension
  • xtheadmempair (v1.0) โ€” T-head two-GPR memory operation extension
  • xtheadsync (v1.0) โ€” T-head multi-core synchronization extension
  • xtheadvector (v1.0) โ€” T-head vector extension
  • xventanacondops (v1.0) โ€” Ventana integer conditional operations extension
  • xmipscmov (v1.0) โ€” Mips conditional move extension
  • xmipscbop (v1.0) โ€” Mips prefetch extension
  • xandesperf (v5.0) โ€” Andes performance extension ๐Ÿš€
  • xandesbfhcvt (v5.0) โ€” Andes bfloat16 conversion extension
  • xandesvbfhcvt (v5.0) โ€” Andes vector bfloat16 conversion extension
  • xandesvsintload (v5.0) โ€” Andes vector INT4 load extension
  • xandesvpackfph (v5.0) โ€” Andes vector packed FP16 extension
  • xandesvdot (v5.0) โ€” Andes vector dot product extension
  • xsmtvdot (v1.0) โ€” SpacemiT vector dot product extension ๐ŸŽ€

When -march= is not specified, GCC uses the setting from -mcpu.

If both -march and -mcpu= are not specified, the default for this argument is system dependent. So if you want a specific architecture extension, you should specify one explicitly, no excuses ๐Ÿ’…

When the RISC-V specifications define an extension as depending on other extensions, GCC implicitly adds the dependent extensions to the enabled extension set if they weren't added explicitly. It's like the extensions come as a package deal ๐Ÿซถ


๐ŸŽ€ Core Name

  • -mcpu=processor-string โ€” use the architecture of, and optimize the output for, the given processor, specified by a particular CPU name. Permissible values for this option 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-p450, sifive-p670, thead-c906, xt-c908, xt-c908v, xt-c910, xt-c910v2, xt-c920, xt-c920v2, tt-ascalon-d8, xiangshan-nanhu, xiangshan-kunminghu, mips-p8700, andes-n22, andes-n25, andes-a25, andes-nx25, andes-ax25, andes-a27, andes-ax27, andes-n225, andes-d23, andes-n45, andes-nx45, andes-a45, andes-ax45, spacemit-x60.

Note that -mcpu does not override -march or -mtune. Don't expect it to ๐Ÿ™…โ€โ™€๏ธ

๐ŸŽ€ Tune Name

  • -mtune=processor-string โ€” optimize the output for the given processor, specified by microarchitecture or a particular CPU name. Permissible values for this option are:

generic, rocket, sifive-3-series, sifive-5-series, sifive-7-series, sifive-p400-series, sifive-p600-series, tt-ascalon-d8, thead-c906, xt-c908, xt-c908v, xt-c910, xt-c910v2, xt-c920, xt-c920v2, xiangshan-nanhu, xiangshan-kunminghu, spacemit-x60, generic-ooo, size, mips-p8700, andes-25-series, andes-23-series, andes-45-series, and all valid options for -mcpu=.

When -mtune= is not specified, GCC uses the setting from -mcpu. The default is generic if neither is specified.

The size choice is not intended for use by end-users, bestie. It's used when -Os is specified. It overrides the instruction cost info provided by -mtune=, but it does not override the pipeline info. This helps reduce code size while still giving good performance ๐Ÿ“

  • -mpreferred-stack-boundary=num โ€” attempt to keep the stack boundary aligned to a 2 raised to num byte boundary. If -mpreferred-stack-boundary is not specified, the default is 4 (16 bytes or 128-bits).
โš ๏ธ Warning: if you use this switch, you must build all modules with the same value, including any libraries. That means the system libraries and startup modules too. Don't skip this one, it's serious ๐Ÿ’€
  • -msmall-data-limit=n โ€” put global and static data smaller than n bytes into a special section (on some targets) ๐Ÿ“ฆ
  • -msave-restore, -mno-save-restore โ€” do or don't use smaller but slower prologue and epilogue code that uses library function calls. The default is to use fast inline prologues and epilogues โšก
  • -mmovcc, -mno-movcc โ€” do or don't produce branchless conditional-move code sequences, even on targets that don't have specific instructions for conditional operations. If enabled, sequences of ALU operations are produced using base integer ISA instructions where profitable ๐Ÿง 
  • -minline-atomics, -mno-inline-atomics โ€” do or don't use smaller but slower subword atomic emulation code that uses libatomic function calls. The default is to use fast inline subword atomics that don't require libatomic ๐Ÿ”ฅ
  • -minline-strlen, -mno-inline-strlen โ€” do or do not attempt to inline strlen calls if possible. Inlining can only be done if the string is properly aligned and instructions for accelerated processing are available. The default is to inline strlen calls โœจ
  • -minline-strcmp, -mno-inline-strcmp โ€” do or do not attempt to inline strcmp calls if possible. Inlining can only be done if the strings are properly aligned and instructions for accelerated processing are available. The default is to inline strcmp calls ๐Ÿ’…
  • -minline-strncmp, -mno-inline-strncmp โ€” do or do not attempt to inline strncmp calls if possible. Inlining can only be done if the strings are properly aligned and instructions for accelerated processing are available. The default is to inline strncmp calls ๐Ÿฆ‹
  • -mstringop-strategy=strategy โ€” pick a specific strategy for inlining string and memory operations. strategy may be one of auto, libcall, scalar, or vector ๐ŸŒธ
  • -mshorten-memrefs, -mno-shorten-memrefs โ€” do or do not try to make more use of compressed load/store instructions, by replacing a load/store of 'base register + large offset' with a new load/store of 'new base + small offset'. If the new base gets stored in a compressed register, then the new load/store can be compressed. Currently this only targets 32-bit integer load/stores ๐Ÿงš
  • -mstrict-align, -mno-strict-align โ€” this one is a lot: it controls whether unaligned memory accesses get generated or not. The default depends on whether the processor you're optimizing for supports fast unaligned access or not ๐Ÿ’€
  • -mscalar-strict-align, -mno-scalar-strict-align โ€” same deal as the one above, it controls unaligned memory accesses, with the default depending on fast unaligned access support. This is an alias for -mstrict-align ๐Ÿซถ

-mvector-strict-align, -mno-vector-strict-align ๐ŸŽ€ โ€” bestie, these decide whether GCC is allowed to generate unaligned vector memory accesses ๐Ÿซถ Default is off, unless the processor you're optimizing for explicitly supports element-misaligned vector memory access โœจ

  • -menable-vectorization-override โ€” no wait, scratch that. Here's the real one: -mmax-vectorization, -mno-max-vectorization ๐Ÿ’… โ€” this turns an override on or off for the vectorizer's cost model, so vectorization always looks profitable ๐Ÿ”ฅ Unlike -fno-vect-cost-model or -fvect-cost-model=unlimited, this one does not turn off the cost comparison between different vector modes ๐Ÿง 
  • -mcmodel=medlow ๐ŸŒธ โ€” generates code for the medium-low code model. The program and its statically defined symbols have to live inside one single 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 ๐Ÿฆ‹ โ€” generates code for the medium-any code model. The program and its statically defined symbols just need to fit in any single 2 GiB address range. Programs can be statically or dynamically linked โœจ
The medium-any code model produces position-independent code, but it is not guaranteed to function correctly when linked into position-independent executables or libraries ๐Ÿ’€
  • -mcmodel=large ๐Ÿ‘‘ โ€” generates code for a large code model with no restrictions on the size or placement of symbols ๐ŸŒˆ
  • -mexplicit-relocs, -mno-explicit-relocs ๐Ÿงš โ€” use or skip assembler relocation operators when dealing with symbolic addresses. The other route is assembler macros, which may limit optimization ๐Ÿฅบ
  • -mrelax, -mno-relax โญ โ€” takes advantage of linker relaxations to cut down the number of instructions needed to materialize symbol addresses. The default is to take advantage of them ๐Ÿ’–
  • -mriscv-attribute, -mno-riscv-attribute ๐Ÿ“ โ€” emits (or doesn't emit) a RISC-V attribute that records extra info into ELF objects. This feature needs at least binutils 2.32 ๐Ÿซถ
  • -mcsr-check, -mno-csr-check ๐Ÿ”ฅ โ€” enables or disables CSR checking. Simple as that ๐Ÿ’…
  • -momit-leaf-frame-pointer ๐Ÿฆ‹ โ€” don't keep the frame pointer in a register for leaf functions. This skips the instructions for saving, setting up, and restoring frame pointers, and frees up an extra register in leaf functions ๐ŸŒธ
  • -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. Heads up, though: support for RISC-V big-endian is experimental ๐Ÿ™ˆ
The ABI isn't stable yet, so it could change in incompatible ways in future releases โš ๏ธ
  • -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 ๐Ÿ›ก๏ธ โ€” generates stack protection code using a canary at guard. The supported locations are global (a global canary) or tls (a per-thread canary in the TLS block) ๐Ÿงต

With the tls choice, -mstack-protector-guard-reg=reg and -mstack-protector-guard-offset=offset get to decide which register is the base register for reading the canary, and what offset from that base register it lives at ๐ŸŽฏ There is no default register or offset here, 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 of TLS variables ๐Ÿ’–
  • -mtls-dialect=trad ๐ŸŒธ โ€” uses traditional TLS as the thread-local storage mechanism for dynamic accesses of TLS variables. This is the default ๐Ÿ‘‘
  • -mrvv-vector-bits=value ๐Ÿฆ‹ โ€” specifies how the number of bits for an RVV vector register (taken from the -march= option) gets interpreted. value is a string keyword that can be scalable or zvl. The default is scalable, which tells GCC to treat the number as a minimum, while zvl tells GCC to use exactly the number of bits you specified ๐Ÿ”ฅ
  • -mrvv-max-lmul=value ๐Ÿ’… โ€” gives you explicit control over the maximum length multiplier (LMUL) used when generating code for the RISC-V Vector Extensions (RVV). value is a string keyword that can be m1, m2, m4, m8, or dynamic. The default is m1, for compatibility with existing hardware that doesn't support the other options ๐Ÿซถ
  • -madjust-lmul-cost, -mno-adjust-lmul-cost ๐Ÿง  โ€” adjusts the cost model used to schedule vector instructions so the latency of instructions gets multiplied by the RVV length multiplier, LMUL. It's disabled by default ๐Ÿ™ˆ
  • -mautovec-segment, -mno-autovec-segment ๐ŸŒˆ โ€” enables or disables generation of vector segment load/store instructions. This option is enabled by default โœจ

โœจ RL78 Options โœจ

  • -msim ๐Ÿงš โ€” links in additional target libraries so things can run inside a simulator ๐ŸŽ€
  • -mmul=none, -mmul=g10, -mmul=g13, -mmul=g14, -mmul=rl78 ๐Ÿ”ฅ โ€” specifies the type of hardware multiplication and division support to use. The simplest one is "none", which uses software for both multiplication and division, and it's the default ๐Ÿ’– "g13" is for the hardware multiply/divide peripheral found on the RL78/G13 (S2 core) targets ๐ŸŒธ "g14" selects the multiplication and division instructions supported by the RL78/G14 (S3 core) parts โญ The value "rl78" is an alias for "g14", and the value "mg10" is an alias for "none" ๐Ÿฆ‹

In addition, a C preprocessor macro gets defined based on this option's setting. The possible values are "__RL78_MUL_NONE__", "__RL78_MUL_G13__", or "__RL78_MUL_G14__" ๐Ÿง 

  • -mcpu=g10, -mcpu=g13, -mcpu=g14, -mcpu=rl78 ๐Ÿ‘‘ โ€” specifies the RL78 core to target. The default is the G14 core, also known as an S3 core or just RL78 โœจ The G13 (or S2) core doesn't have multiply or divide instructions, and uses a hardware peripheral for those operations instead ๐Ÿ› ๏ธ The G10 (or S1) core doesn't have register banks, so it uses a different calling convention ๐ŸŒˆ

If this option is set, it also picks the type of 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 multiplications altogether ๐Ÿ’…

Note, though: even though the RL78/G14 core is the default target, specifying -mcpu=g14 or -mcpu=rl78 on the command line does change the toolchain's behavior, because it also enables G14 hardware multiply support ๐Ÿ‘€ If these options aren't specified on the command line, software multiplication routines get used even though the code targets the RL78 core ๐Ÿข That's for backward compatibility with older toolchains that didn't have hardware multiply and divide support ๐Ÿซถ

In addition, a C preprocessor macro gets defined based on this option's setting. The possible values are "__RL78_G10__", "__RL78_G13__", or "__RL78_G14__" ๐Ÿงš

  • -mg10, -mg13, -mg14, -mrl78 ๐Ÿ•ฐ๏ธ โ€” these are aliases for the corresponding -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 use in interrupt handlers, but with this option enabled, they can be used in ordinary functions too ๐Ÿ”ฅ
  • -mrelax, -mno-relax โญ โ€” enables or disables assembler and linker relaxation. This is enabled by default at -Os ๐Ÿ’–
  • -mes0 ๐Ÿฆ‹ โ€” assumes "ES" is zero throughout program execution, and uses it to address read-only data ๐ŸŽ€
  • -msave-mduc-in-interrupts, -mno-save-mduc-in-interrupts ๐Ÿ›ก๏ธ โ€” specifies that interrupt handler functions should preserve the MDUC registers. This is only necessary if normal code might use the MDUC registers, for example because it does multiplication and division operations ๐Ÿงฎ The default is to ignore the MDUC registers, because that makes the interrupt handlers faster โšก The target option -mg13 needs to be passed for this to work, since this feature is only available on the G13 target (S2 core) ๐Ÿ’… The MDUC registers only get saved if the interrupt handler performs a multiplication or division operation, or calls another function ๐Ÿซถ

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

These -m options are defined for the IBM RS/6000 and PowerPC ๐Ÿ“

  • -mcpu=cpu_type ๐Ÿ‘‘ โ€” sets the architecture type, register usage, and instruction scheduling parameters for the machine type cpu_type. The supported values for cpu_type 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 ๐ŸŒˆ

-mcpu=powerpc, -mcpu=powerpc64, and -mcpu=powerpc64le specify pure 32-bit PowerPC (either endian), 64-bit big endian PowerPC, and 64-bit little endian PowerPC architecture machine types ๐Ÿงš Each one gets an appropriate, generic processor model assumed for scheduling purposes โœจ

Specifying native as the cpu type detects and selects the architecture option that matches the host processor of the system doing the compilation ๐Ÿ–ฅ๏ธ -mcpu=native has no effect if GCC doesn't recognize the processor ๐Ÿ™ˆ

The other options specify a specific processor ๐ŸŽฏ Code generated under those options runs best on that processor, and may not run at all on others ๐Ÿ’€

The -mcpu options automatically enable or disable the following options ๐Ÿซถ

-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 -mrop-protect

So the exact settings for each CPU can shift between compiler versions ๐Ÿ’… It's whatever looks fastest for that chip, so it doesn't always match what the hardware can actually do ๐Ÿคทโ€โ™€๏ธ If you want one specific option set to a certain value, just put it after -mcpu, like -mcpu=970 -mno-altivec ๐ŸŽ€

On AIX, -mcpu doesn't turn -maltivec or -mpowerpc64 on or off right now, because AIX doesn't fully support those options yet ๐Ÿ™ˆ You can still toggle them yourself if you're sure it'll work in your setup ๐Ÿ’œ

Honestly? For most cases we'd recommend -mcpu=cpu_type over the switches that control specific instructions. It's the cleaner move ๐Ÿซถ

  • -mtune=cpu_type โ€” Sets the instruction scheduling parameters for the machine type cpu_type, but it leaves the architecture and register usage alone, unlike -mcpu=cpu_type โœจ You use the same cpu_type values for -mtune as you would for -mcpu. If you specify both, the code uses the architecture and registers from -mcpu, but the scheduling comes from -mtune ๐Ÿงš
  • -mpowerpc64, -mno-powerpc64 โ€” -mpowerpc64 lets GCC generate the extra 64-bit instructions from the full PowerPC64 architecture, and treats GPRs as 64-bit doubleword quantities ๐Ÿ’– GCC defaults to -mno-powerpc64 ๐ŸŒธ
  • -mcmodel=small โ€” Generates PowerPC64 code for the small model, where the TOC is limited to 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, where the TOC can be up to 4G. Other data and code are only limited by the 64-bit address space ๐ŸŒˆ
  • -mprofile-kernel โ€” Only on PowerPC64 GNU/Linux targets. When you use it with -pg, calls to "mcount" get inserted before the function prologue instead of after it ๐Ÿ”ฅ
  • -maltivec, -mno-altivec โ€” Generates code that does (or doesn't) use AltiVec instructions, and also turns on built-in functions that give you more direct access to the AltiVec instruction set ๐Ÿ’… You might also need -mabi=altivec to adjust the current ABI with the 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 the target's endianness ๐Ÿ˜ญ Element zero is the leftmost element in a vector register on a big-endian target, and the rightmost element in a vector register on a little-endian target.
  • -mpowerpc-gpopt, -mno-powerpc-gpopt โ€” Specifying -mpowerpc-gpopt lets GCC use the optional PowerPC instructions in the General Purpose group, including floating-point square root โœจ Specifying
  • -mpowerpc-gfxopt, -mno-powerpc-gfxopt โ€” -mpowerpc-gfxopt lets GCC use the optional PowerPC instructions in the Graphics group, including floating-point select ๐ŸŽ€
  • -mmfcrf, -mno-mfcrf โ€” -mmfcrf lets GCC generate the move-from-condition-register-field instruction, which is on the POWER4 and other processors that support PowerPC V2.01 ๐Ÿงš
  • -mpopcntb, -mno-popcntb โ€” -mpopcntb lets GCC generate the popcount and double-precision FP reciprocal estimate instruction, which is on the POWER5 and other processors that support PowerPC V2.02 ๐Ÿ’œ
  • -mpopcntd, -mno-popcntd โ€” -mpopcntd lets GCC generate the popcount instruction, which is on the POWER7 and other processors that support PowerPC V2.06 ๐Ÿ‘‘
  • -mfprnd, -mno-fprnd โ€” -mfprnd lets GCC generate the FP round-to-integer instructions, which are on the POWER5+ and other processors that support PowerPC V2.03 ๐ŸŒธ
  • -mcmpb, -mno-cmpb โ€” -mcmpb lets GCC generate the compare bytes instruction, which is on the POWER6 and other processors that support PowerPC V2.05 ๐Ÿฆ‹
  • -mhard-dfp, -mno-hard-dfp โ€” -mhard-dfp lets GCC generate the decimal floating-point instructions found on some POWER processors ๐Ÿ“
  • -mvrsave, -mno-vrsave โ€” Generates VRSAVE instructions when generating AltiVec code ๐ŸŒˆ
  • -msecure-plt โ€” Generates code so that ld and ld.so can build executables and shared libraries with non-executable ".plt" and ".got" sections ๐Ÿ”ฅ This is a PowerPC 32-bit SYSV ABI option ๐Ÿ’…
  • -mbss-plt โ€” Generates code that uses a BSS ".plt" section that ld.so fills in. It requires ".plt" and ".got" sections that are both writable and executable ๐Ÿ˜ญ This is a PowerPC 32-bit SYSV ABI option ๐Ÿ™ˆ
  • -msplit-patch-nops โ€” When you add NOPs for a patchable area with the -fpatchable-function-entry option, this emits the "before" NOPs in front of the global entry point and the "after" NOPs after the local entry point ๐Ÿงš That makes the NOP sequence non-consecutive when a global entry point is generated. Without this option, the NOPs go directly before and after the local entry point, so they're consecutive, but the global and local entry points end up further apart ๐Ÿซถ If only a single entry point is generated, this option does nothing โœจ
  • -misel, -mno-isel โ€” This switch enables or disables the generation of ISEL instructions ๐Ÿ’–
  • -mvsx, -mno-vsx โ€” Generates code that does (or doesn't) use vector/scalar (VSX) instructions, and also turns on built-in functions that give you more direct access to the VSX instruction set ๐Ÿฆ‹
  • -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 that give direct access to the Hardware Transactional Memory (HTM) instructions added in version 2.07 of the PowerPC ISA ๐ŸŒธ
  • -mpower8-fusion, -mno-power8-fusion โ€” Generates code that keeps some integer operations next to each other, so the instructions can be fused together on power8 and later processors ๐Ÿ“
  • -mquad-memory, -mno-quad-memory โ€” Generates code that does (or doesn't) use the non-atomic quad word memory instructions ๐Ÿ’… -mquad-memory requires 64-bit mode ๐ŸŒˆ
  • -mquad-memory-atomic, -mno-quad-memory-atomic โ€” Generates code that does (or doesn't) use the atomic quad word memory instructions ๐Ÿ”ฅ -mquad-memory-atomic requires 64-bit mode โญ
  • -mfloat128, -mno-float128 โ€” Enables or disables the __float128 keyword for IEEE 128-bit floating point, and picks either software emulation or hardware instructions for it ๐Ÿ‘‘

The VSX instruction set (-mvsx) has to be enabled to use IEEE 128-bit floating point support ๐Ÿ’œ IEEE 128-bit floating point is only supported on Linux ๐Ÿง

-mfloat128 is enabled by default on PowerPC Linux systems using the VSX instruction set, and disabled on other systems โœจ

If you use the ISA 3.0 instruction set (-mcpu=power9) on a 64-bit system, IEEE 128-bit floating point support also enables generation of ISA 3.0 IEEE 128-bit floating point instructions ๐Ÿงš Otherwise, if you don't specify ISA 3.0 instruction generation, or you're targeting a 32-bit big-endian system, IEEE 128-bit floating point is handled 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 enabled by default on PowerPC Linux systems using the ISA 3.0 instruction set, and disabled on other systems ๐ŸŒˆ

  • -m32, -m64 โ€” Generates code for 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 generates code that runs on any PowerPC variant ๐Ÿ“ The 64-bit environment sets int to 32 bits, and long and pointer to 64 bits, and generates code for PowerPC64, as for -mpowerpc64 ๐Ÿ‘‘
  • -mfull-toc, -mno-fp-in-toc, -mno-sum-in-toc, -mminimal-toc โ€” These modify how the TOC (Table Of Contents) gets generated, which happens for every executable file ๐Ÿ“œ -mfull-toc is selected by default ๐Ÿ’… In that case, GCC allocates at least one TOC entry for each unique non-automatic variable reference in your program. GCC also puts floating-point constants in the TOC ๐Ÿซถ Heads up: only 16,384 entries are available in the TOC ๐Ÿ˜ญ

If the linker throws an error saying you've overflowed the available TOC space, you can shrink the TOC usage with -mno-fp-in-toc and -mno-sum-in-toc ๐Ÿ”ฅ -mno-fp-in-toc stops GCC from putting floating-point constants in the TOC, and -mno-sum-in-toc makes GCC generate code that calculates the sum of an address and a constant at run time, instead of storing that sum in the TOC ๐Ÿงš You can use one or both of these. Each one makes GCC produce very slightly slower and larger code, but it saves TOC space ๐ŸŒธ

If you're still out of TOC space even with both of those options, try -mminimal-toc instead ๐Ÿ™ˆ It makes GCC create only one TOC entry for every file. The code comes out slower and larger, but it uses extremely little TOC space ๐Ÿ’œ You might want to use this only on files with less frequently executed code ๐Ÿฆ‹

  • -maix64, -maix32 โ€” Enables the 64-bit AIX ABI and calling convention: 64-bit pointers, a 64-bit "long" type, and the infrastructure needed to support them ๐Ÿ‘‘ -maix64 implies -mpowerpc64, while -maix32 disables the 64-bit ABI and implies -mno-powerpc64 โœจ GCC defaults to -maix32 ๐ŸŒˆ
  • -mxl-compat, -mno-xl-compat โ€” okay so these make your code act more like IBM XL compiler output when you're using the AIX-compatible ABI ๐Ÿ’…. With -mxl-compat, floating-point args to prototyped functions also go on the stack past the register save area (RSA), on top of the argument FPRs. It also stops assuming the most significant double in a 128-bit long double is properly rounded when comparing and converting to double, and it uses XL symbol names for the long double support routines.
    • The AIX calling convention got extended without any initial docs to cover an obscure K&R C case: calling a function that takes the address of its arguments with fewer arguments than it declares ๐Ÿ™ˆ. IBM XL compilers read floating-point args that don't fit in the RSA from the stack, but only when a subroutine is compiled without optimization.
    • Always storing FP args on the stack is inefficient and rarely needed, so this is not on by default. You only need it when calling subroutines built by IBM XL compilers without optimization.
  • -mpe โ€” this one's giving IBM RS/6000 SP Parallel Environment (PE) support ๐Ÿงš. It links an app that uses message passing with special startup code so it can actually run. Your system needs PE installed in the standard location (/usr/lpp/ppe.poe/), or you override the specs file with -specs= to point at the right directory.
The Parallel Environment doesn't support threads, so -mpe and -pthread are incompatible. Don't pair them, bestie ๐Ÿ˜ญ.
  • -malign-natural, -malign-power โ€” on AIX, 32-bit Darwin, and 64-bit PowerPC GNU/Linux, -malign-natural overrides the ABI's alignment for bigger types (like floating-point doubles) and puts them on their natural size-based boundary โœจ. -malign-power tells GCC to follow the ABI's alignment rules instead. GCC defaults to the standard ABI alignment.
    • On 64-bit Darwin, natural alignment is the default, and -malign-power is not supported ๐Ÿ’€.
  • -msoft-float, -mhard-float โ€” -msoft-float generates code that skips the floating-point register set, and -mhard-float generates code that uses it ๐Ÿฆ‹. With -msoft-float, software floating-point emulation gets provided, but you have to pass the option to GCC when linking too.
  • -mmultiple, -mno-multiple โ€” -mmultiple uses the load multiple word and store multiple word instructions, and -mno-multiple doesn't. These are generated by default on POWER systems but not on PowerPC systems ๐ŸŒธ.
Don't use -mmultiple on little-endian PowerPC systems, because those instructions don't work when the processor is in little-endian mode. The exceptions are PPC740 and PPC750, which allow them in little-endian mode.
  • -mupdate, -mno-update โ€” -mupdate uses the load or store instructions that update the base register to the calculated memory address, and -mno-update skips them. These are generated by default ๐Ÿ“.
If you use -mno-update, there's a small window between when the stack pointer gets updated and when 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 โ€” -mavoid-indexed-addresses tries to avoid indexed load or store instructions, and -mno-avoid-indexed-addresses tries not to avoid them ๐Ÿง . Indexed instructions can cost you performance on Power6 processors in certain situations, like stepping through large arrays that cross a 16M boundary. This is enabled by default when targeting Power6 and disabled otherwise.
  • -mfused-madd, -mno-fused-madd โ€” -mfused-madd uses the floating-point multiply and accumulate instructions, and -mno-fused-madd doesn't ๐Ÿ’œ. These are generated by default when hardware floating point is used.
    • The machine-dependent -mfused-madd option is now mapped to the machine-independent -ffp-contract=fast option.
    • -mno-fused-madd is mapped to -ffp-contract=off.
  • -mmulhw, -mno-mulhw โ€” -mmulhw uses the half-word multiply and multiply-accumulate instructions on the IBM 405, 440, 464 and 476 processors, and -mno-mulhw doesn't โญ. These are generated by default when targeting those processors.
  • -mdlmzb, -mno-dlmzb โ€” -mdlmzb uses the string-search dlmzb instruction on the IBM 405, 440, 464 and 476 processors, and -mno-dlmzb doesn't ๐Ÿ”ฅ. This instruction is generated by default when targeting those processors.
  • -mno-bit-align, -mbit-align โ€” on System V.4 and embedded PowerPC systems, -mno-bit-align does not force structures and unions with bit-fields to align to the base type of the bit-field, and -mbit-align does ๐Ÿ‘‘.
    • For example, by default a struct 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 struct is aligned to a 1-byte boundary and is just 1 byte. Iconic ๐Ÿ™Œ.
  • -mbit-word, -mno-bit-word โ€” allows bit-fields to cross word boundaries ๐ŸŒˆ.
  • -mno-strict-align, -mstrict-align โ€” on System V.4 and embedded PowerPC systems, -mno-strict-align does not assume unaligned memory references are handled by the system, and -mstrict-align does assume that ๐Ÿซถ.
  • -mrelocatable, -mno-relocatable โ€” -mrelocatable generates code that lets a static executable be relocated to a different address at run time, and -mno-relocatable doesn't allow that ๐Ÿฆ‹. A simple embedded PowerPC system loader should relocate the entire contents of ".got2" and the 4-byte locations listed in the ".fixup" section. That's 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 โ€” same vibe as -mrelocatable: it 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 that -mrelocatable does.
    • Objects compiled with -mrelocatable-lib can be linked with objects compiled with any combination of the -mrelocatable options ๐Ÿ’–.

  • -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's not relocatable, but its external references are ๐ŸŒˆ. The result is suitable for applications, but not 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 this register with an appropriate value before execution begins.
  • -mprioritize-restricted-insns=priority โ€” controls the priority given to dispatch-slot restricted instructions during the second scheduling pass ๐Ÿ‘‘. The priority argument takes 0, 1, or 2, which assigns no, highest, or second-highest (respectively) priority to those instructions.
  • -msched-costly-dep=dependence_type โ€” controls which dependences the target considers costly during instruction scheduling ๐Ÿง . The dependence_type argument 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 whose latency is greater than or equal to number is costly.
  • -minsert-sched-nops=scheme โ€” controls which NOP insertion scheme gets used during the second scheduling pass ๐Ÿ’…. The scheme argument 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. It inserts exactly as many NOPs as needed to push an insn into a new group, according to the estimated processor grouping.
    • number โ€” insert NOPs to force costly dependent insns into separate groups. It inserts number NOPs to force an insn into 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 โ€” same as specifying both -mcall-sysv and -meabi ๐Ÿซถ.
  • -mcall-sysv-noeabi โ€” same as specifying 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 โ€” on System V.4 and embedded PowerPC systems, this compiles code for the OpenBSD operating system ๐ŸŒˆ.
  • -mtraceback=traceback_type โ€” picks the type of traceback table โœจ. Valid values for traceback_type are full, part, and no.
  • -maix-struct-return โ€” returns all structures in memory, as the AIX ABI specifies ๐Ÿ‘‘.
  • -msvr4-struct-return โ€” returns structures smaller than 8 bytes in registers, as the SVR4 ABI specifies ๐Ÿ”ฅ.
  • -mabi=abi-type โ€” extends the current ABI with a particular extension, or removes one ๐Ÿงš. Valid values are altivec, no-altivec, ibmlongdouble, ieeelongdouble, elfv1, elfv2, and, for AIX, vec-extabi and vec-default.
  • -mabi=ibmlongdouble โ€” okay bestie, this one flips the ABI over to IBM extended-precision long double ๐Ÿ’–. Heads up, it's not likely to work if your system already defaults to IEEE extended-precision long double ๐Ÿ™ˆ. If you switch long double away from IEEE extended-precision, the compiler warns you unless you add -Wno-psabi. You also need -mlong-double-128 turned on for it to work ๐ŸŽ€
  • -mabi=ieeelongdouble โ€” the mirror image of the one above โœจ. It's switching the ABI to IEEE extended-precision long double, and it's probably not going to work if your system defaults to IBM extended-precision long double ๐Ÿ’€. If you change long double away from IBM extended-precision, you'll get a warning unless you use -Wno-psabi. And yep, you still need -mlong-double-128 enabled ๐Ÿฆ‹
  • -mabi=elfv1 โ€” this sets the ABI to ELFv1, which is the default for big-endian PowerPC 64-bit Linux ๐ŸŒธ. Overriding the default needs special system support, and honestly it's likely to fail in spectacular ways ๐Ÿ˜ญ
  • -mabi=elfv2 โ€” the ELFv2 ABI, which is the default for little-endian PowerPC 64-bit Linux ๐Ÿงš. Same warning applies: overriding it needs special system support and will probably fail in spectacular ways ๐Ÿ’…
  • -mgnu-attribute, -mno-gnu-attribute โ€” this tells GCC to emit .gnu_attribute assembly directives ๐Ÿซถ. Those set tag/value pairs in a .gnu.attributes section, which describe ABI variations in function parameters or return values โœจ
  • -mprototype, -mno-prototype โ€” on System V.4 and embedded PowerPC systems, this lets you assume every call to a variable argument function is properly prototyped ๐Ÿ‘‘. Without it, GCC has to slip an instruction in before every non-prototyped call to set or clear bit 6 of the condition code register (CR). That bit says whether floating-point values are being passed in the floating-point registers, just 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 that the standard C libraries are libsim.a and libc.a ๐Ÿ“. It's the default for powerpc-*-eabisim configurations ๐Ÿ’œ
  • -mmvme โ€” on embedded PowerPC systems, the startup module is crt0.o and the standard C libraries are libmvme.a and libc.a โญ
  • -mads โ€” on embedded PowerPC systems, the startup module is crt0.o and the standard C libraries are libads.a and libc.a ๐Ÿ”ฅ
  • -myellowknife โ€” on embedded PowerPC systems, the startup module is crt0.o and the standard C libraries are libyk.a and libc.a ๐ŸŒธ
  • -mvxworks โ€” on System V.4 and embedded PowerPC systems, this tells GCC you're compiling for a VxWorks system ๐Ÿฆ‹
  • -memb โ€” on embedded PowerPC systems, this sets the PPC_EMB bit in the ELF flags header ๐ŸŽ€. That's how you indicate that eabi extended relocations are being used ๐Ÿ’–
  • -meabi, -mno-eabi โ€” on System V.4 and embedded PowerPC systems, this either adheres to (-meabi) or doesn't adhere to (-mno-eabi) the Embedded Applications Binary Interface (EABI) ๐Ÿซถ. EABI is basically a set of modifications to the System V.4 specifications, so it's a whole vibe โœจ
    • With -meabi, the stack is aligned to an 8-byte boundary, a function __eabi is called from main to set up the EABI environment, and the -msdata option can use both r2 and r13 to point to two separate small data areas ๐Ÿ‘‘
    • With -mno-eabi, the stack is aligned to a 16-byte boundary, no EABI initialization function is called from main, and the -msdata option only uses 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 -mrelocatable, and it also sets -memb ๐Ÿ™ˆ
  • -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 .sbss, which is adjacent to .sdata ๐Ÿฆ‹. This one's 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's compiled the same as -msdata=sysv โœจ
  • -msdata=data โ€” on System V.4 and embedded PowerPC systems, small global data goes in the .sdata section ๐Ÿ’…. Small uninitialized global data goes in .sbss ๐Ÿซถ. The catch is that register r13 is not used to address small data here ๐Ÿ™ˆ. This is the default behaviour 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 โ€” this 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 depends on the target ๐ŸŒธ
  • -mblock-compare-inline-limit=num โ€” this 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 compares is disabled, both the non-loop and loop kinds ๐Ÿ’€. The default value depends on the target ๐ŸŽ€
  • -mblock-compare-inline-loop-limit=num โ€” this 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 isn't constant, at most num bytes get compared before memcmp is called to handle the rest of the block ๐Ÿ’…. The default value depends on the target ๐Ÿ’œ
  • -mstring-compare-inline-limit=num โ€” this compares at most num string bytes with inline code ๐Ÿซถ. If the difference or the end of the string isn't found at the end of the inline compare, a call to strcmp or strncmp takes care of the rest of the comparison ๐Ÿ™ˆ. 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 is also passed to the linker ๐Ÿ‘‘. All modules should be compiled with the same -G num value, no exceptions ๐Ÿ”ฅ
  • -mregnames, -mno-regnames โ€” on System V.4 and embedded PowerPC systems, this does (-mregnames) or doesn't (-mno-regnames) emit register names in the assembly output using symbolic forms ๐Ÿ’–
  • -mlongcall, -mno-longcall โ€” by default, GCC 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 can't be that far away ๐Ÿฆ‹. This setting can be overridden by the shortcall function attribute, or by #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's 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) ๐Ÿซถ. Heads up, 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 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 is 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 directs 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 โ€” this implements (-mpltseq) or doesn't implement (-mno-pltseq) -fno-plt and long calls using an inline PLT call sequence ๐Ÿฆ‹. 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 on 32-bit PowerPC systems with newer GNU linkers โœจ. They're enabled by default if the support is detected when configuring GCC ๐ŸŽ€. For 32-bit PowerPC, they're also enabled if GCC is configured with --enable-secureplt ๐ŸŒˆ. Watch out: -mpltseq code and -mbss-plt 32-bit PowerPC relocatable objects may not be linked together ๐Ÿ™ˆ
  • -mtls-markers, -mno-tls-markers โ€” this marks (-mtls-markers) or doesn't mark (-mno-tls-markers) calls to __tls_get_addr with a relocation that specifies the function argument ๐Ÿงš. That relocation lets the linker reliably associate the function call with the argument setup instructions for TLS optimization ๐Ÿ’…. In turn, that lets GCC schedule the sequence better ๐Ÿซถ
  • -mrecip, -mno-recip โ€” okay so this one flips on the reciprocal estimate and reciprocal square root estimate instructions ๐Ÿ’…, plus extra Newton-Raphson steps to win back precision, instead of doing a straight divide (or a square root and then a divide) on floating-point arguments ๐Ÿฆ‹. Bestie, you should pair -mrecip with -ffast-math (or at least -funsafe-math-optimizations, -ffinite-math-only, -freciprocal-math and -fno-trapping-math) ๐Ÿซถ. ngl the throughput is usually higher than the non-reciprocal instruction, BUT the precision can drop by up to 2 ulp for reciprocal square roots (like, the inverse of 1.0 comes out as 0.99999994) ๐Ÿ˜ญ
  • -mrecip=opt โ€” this is the picky version ๐ŸŽ€ where you choose which reciprocal estimate instructions get to show up. opt is a comma-separated list, and you can put a "!" in front of any option to invert it ๐Ÿ’œ
    • all โ€” enable all estimate instructions โœจ
    • default โ€” enable the default instructions, same as -mrecip ๐ŸŒธ
    • none โ€” disable all estimate instructions, same as -mno-recip ๐Ÿ™ˆ
    • div โ€” enable the reciprocal approximation instructions for both single and double precision ๐Ÿ“
    • divf โ€” enable the single-precision reciprocal approximation instructions ๐Ÿงš
    • divd โ€” enable the double-precision reciprocal approximation instructions ๐Ÿ’–
    • rsqrt โ€” enable the reciprocal square root approximation instructions for both single and double precision ๐ŸŒˆ
    • rsqrtf โ€” enable the single-precision reciprocal square root approximation instructions ๐Ÿฆ‹
    • rsqrtd โ€” enable the double-precision reciprocal square root approximation instructions ๐Ÿ‘‘

So, for example, -mrecip=all,!rsqrtd turns on every reciprocal estimate instruction except for FRSQRTE, XSRSQRTEDP, and XVRSQRTEDP, which are the ones handling double-precision reciprocal square root math ๐Ÿ”ฅ

  • -mrecip-precision, -mno-recip-precision โ€” this one lets GCC assume (or not assume ๐Ÿ™ƒ) that the reciprocal estimate instructions are more precise than the PowerPC ABI actually requires. Picking -mcpu=power6, -mcpu=power7 or -mcpu=power8 automatically selects -mrecip-precision for you โœจ. Also, the double-precision square root estimate instructions are NOT generated by default on low-precision machines, because they don't converge after three steps ๐Ÿ’€
  • -mveclibabi=type โ€” this sets 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 for that job ๐Ÿ’…. GCC currently emits calls to the following when generating code for power7 ๐Ÿซถ:
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, tanhf4

Both -ftree-vectorize and -funsafe-math-optimizations must ALSO be enabled ๐Ÿ”ฅ, and the MASS libraries must be specified at link time ๐ŸŽ€

  • -mfriz, -mno-friz โ€” okay so this decides whether GCC generates the friz instruction when -funsafe-math-optimizations is used to speed up rounding floating-point values to 64-bit integers and back to floating point ๐ŸŒธ
Warning, bestie ๐Ÿ˜ญ the friz instruction does NOT return 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 โ€” this controls whether GCC generates code that loads up the static chain register (r11) when calling through a pointer on AIX and 64-bit Linux systems ๐Ÿงš. On those systems a function pointer points to a 3-word descriptor that gives the function address, the TOC value to load into register r2, and the static chain value to load into register r11 ๐Ÿ’œ. -mpointers-to-nested-functions is on by default ๐ŸŒˆ
Heads up, bestie ๐Ÿ’€ 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 โ€” this generates (or doesn't generate ๐Ÿ™ˆ) code that saves the TOC value in the reserved stack location in the function prologue, if the function calls through a pointer on AIX and 64-bit Linux systems โœจ. If the TOC value isn't saved in the prologue, it gets saved just before the call through the pointer ๐Ÿฆ‹. The -mno-save-toc-indirect option is the default ๐Ÿ“
  • -mcompat-align-parm, -mno-compat-align-parm โ€” this generates (or doesn't generate) 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 did not align a structure parameter on a 128-bit boundary when that structure had a member needing 128-bit alignment ๐Ÿ˜ญ. This got fixed in more recent versions of GCC, but this option can help you make code that plays nicely with functions compiled by those older versions ๐Ÿซถ

The -mno-compat-align-parm option 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 (a global canary) or tls (a per-thread canary in the TLS block, which is the default with GNU libc version 2.4 or later) ๐Ÿ‘‘

With the tls choice, -mstack-protector-guard-reg=reg and -mstack-protector-guard-offset=offset get to decide which register is used as the base register for reading the canary, and what offset from that base register to use ๐Ÿ’…. The defaults for those are whatever the relevant ABI specifies โœจ

  • -mpcrel, -mno-pcrel โ€” this generates (or doesn't generate ๐Ÿ™ˆ) pc-relative addressing ๐Ÿฆ‹. The -mpcrel option requires that the medium code model (-mcmodel=medium) AND prefixed addressing (-mprefixed) options are enabled ๐Ÿซถ
  • -mprefixed, -mno-prefixed โ€” this generates (or doesn't generate) addressing modes that use prefixed load and store instructions ๐Ÿงš. The -mprefixed option requires that -mcpu=power10 (or later) is enabled ๐Ÿ’–
  • -mmma, -mno-mma โ€” this generates (or doesn't generate) the MMA instructions ๐ŸŒˆ. The -mma option requires that -mcpu=power10 (or later) is enabled ๐Ÿ”ฅ
  • -mrop-protect, -mno-rop-protect โ€” this generates (or doesn't generate) ROP protection instructions when the target processor supports them ๐Ÿ›ก๏ธ Currently, this option disables the shrink-wrap optimization (-fshrink-wrap) ๐Ÿ’€
  • -mprivileged, -mno-privileged โ€” this generates (or doesn't generate) code that runs in privileged state ๐Ÿ‘‘
  • -mblock-ops-unaligned-vsx, -mno-block-ops-unaligned-vsx โ€” this generates (or doesn't generate) unaligned VSX loads and stores for inline expansion of memcpy and memmove โœจ
  • -msplat-word-constant, -mno-splat-word-constant โ€” this generates (or doesn't generate) code that uses the XXSPLTIW instruction ๐ŸŒธ. This option is enabled by default ๐Ÿ“
  • -msplat-float-constant, -mno-splat-float-constant โ€” this generates (or doesn't generate) code that uses the XXSPLTIDP instruction ๐Ÿฆ‹. This option is enabled by default ๐Ÿ’œ
  • -mieee128-constant, -mno-ieee128-constant โ€” this generates (or doesn't generate) code that uses the LXVKQ instruction ๐Ÿงš. This option is enabled by default ๐Ÿซถ
  • -mwarn-altivec-long, -mno-warn-altivec-long โ€” this turns warnings on or off about deprecated vector long ... Altivec type usage โš ๏ธ This option is enabled by default ๐Ÿ’…

๐Ÿฆ‹ RX Options ๐Ÿฆ‹

These command-line options are defined for RX targets ๐ŸŒˆ

  • -m64bit-doubles, -m32bit-doubles โ€” this 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 โ€” this enables (-fpu) or disables (-nofpu) the use of RX floating-point hardware ๐Ÿ”ฅ 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 is not used for doubles if the -m64bit-doubles option is used ๐Ÿ’€

Note: if the -fpu option is enabled, then -funsafe-math-optimizations is also enabled automatically ๐Ÿ˜ญ This is because the RX FPU instructions are themselves unsafe.
  • -mcpu=name โ€” this 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 does not support the MVTIPL instruction ๐ŸŒธ

The RX200 series does not have a hardware floating-point unit, so -nofpu is enabled by default when this type is selected ๐Ÿซถ

  • -mbig-endian-data, -mlittle-endian-data โ€” this stores data (but NOT code) in the big-endian format ๐ŸŒˆ The default is -mlittle-endian-data, which means data is stored in the little-endian format ๐Ÿ’œ
  • -msmall-data-limit=N โ€” this 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 size of the area is limited and it's up to the programmer to make sure the area doesn't overflow ๐Ÿ˜ญ

Also, when the small data area is used, one of the RX's registers (usually r13) is reserved for pointing to this area, so the compiler can't use it anymore ๐Ÿ’… This could result in slower and/or larger code if variables get pushed onto the stack instead of being held in that register ๐Ÿ’€

Note, bestie: common variables (variables that have not been initialized) and constants are NOT placed into the small data area, because they get assigned to other sections in the output executable ๐Ÿซถ

okay so the default value is zero, which disables this feature ๐Ÿ™…โ€โ™€๏ธ and bestie, it is not switched on by default at higher optimization levels (-O2 etc) because reserving a register can be kinda detrimental ๐Ÿ˜ฌ. It's up to you to experiment and see whether it actually helps your program โœจ. Peep the -mpid description for how the register holding the small data area pointer gets picked ๐Ÿงš

  • -msim, -mno-sim โ€” Use the simulator runtime ๐ŸŽฎ. The default is the libgloss board-specific runtime.
  • -mas100-syntax, -mno-as100-syntax โ€” When GCC spits out assembler output, this makes it use a syntax that plays nice with Renesas's AS100 assembler ๐Ÿงธ. GAS can read it too, but it has some restrictions, so it is not generated by default ๐Ÿ™ˆ
  • -mmax-constant-size=N โ€” Sets the biggest a constant can be (in bytes) and still be used as an operand in an RX instruction ๐Ÿ“. RX instructions can take constants up to 4 bytes, but a longer value means a longer instruction, so sometimes it's lowkey beneficial to restrict this ๐Ÿ’…. Constants that are too big get tossed into a constant pool and referenced through register indirection ๐Ÿซถ

N can be between 0 and 4. A value of 0 (the default) or 4 means constants of any size are allowed โœจ

  • -mrelax โ€” Turns on linker relaxation ๐Ÿฆ‹. The linker tries to shrink your program by finding shorter versions of various instructions. It's disabled by default.
  • -mint-register=N โ€” Reserves registers for fast interrupt handler functions ๐Ÿ‘‘. N can be between 0 and 4:
    • 1 reserves "r13" for the exclusive use of fast interrupt handlers
    • 2 reserves "r13" and "r12"
    • 3 reserves "r13", "r12" and "r11"
    • 4 reserves "r13" through "r10"
    • 0, the default, reserves nothing ๐ŸŒธ
  • -msave-acc-in-interrupts โ€” Tells interrupt handlers to preserve the accumulator register ๐Ÿ”ฅ. This is only needed if normal code might use the accumulator, like when it does 64-bit multiplications. The default is to ignore the accumulator, because that keeps the interrupt handlers faster โšก
  • -mpid, -mno-pid โ€” Enables position-independent data ๐Ÿงšโ€โ™€๏ธ. When it's on, any access to constant data happens via 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 huge win for embedded apps with tight memory constraints ๐Ÿ’œ

Heads up, this reserves a register (usually "r13") for the constant data base address ๐Ÿ˜ญ. That can make code slower and/or bigger, especially in complicated functions.

Which register ends up holding that base address depends on whether -msmall-data-limit and/or -mint-register are on. Starting at "r13" and working downwards, registers get allocated first for -mint-register, then -mpid, and finally -msmall-data-limit. So the small data area register can be "r8" if both -mint-register=4 and -mpid are specified on the command line ๐Ÿ’…

By default this feature is not enabled. You can restore the default with -mno-pid ๐Ÿ™ƒ

  • -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 ๐Ÿ“ฃ. The default is to warn for each extra fast interrupt handler, since the RX only supports one of them ๐Ÿ™„
  • -mallow-string-insns, -mno-allow-string-insns โ€” Turns the string manipulation instructions "SMOVF", "SCMPU", "SMOVB", "SMOVU", "SUNTIL" "SWHILE" and also the "RMPA" instruction on or off ๐Ÿงต. These can prefetch data, which is not safe if you're touching an I/O register ๐Ÿ˜ฑ (see section 12.2.7 of the RX62N Group User's Manual for more info).

The default is to allow them. GCC can't reliably detect every situation where a string instruction might touch an I/O register, so it can't turn them off automatically. It's on you to use -mno-allow-string-insns if your program accesses I/O space ๐Ÿซถ

When these instructions are enabled, GCC defines the C preprocessor symbol "__RX_ALLOW_STRING_INSNS__". Otherwise it defines "__RX_DISALLOW_STRING_INSNS__" ๐Ÿšจ
  • -mjsr, -mno-jsr โ€” Use only (or not only) "JSR" instructions to access functions ๐Ÿงญ. This can help when your code size goes beyond the range of "BSR" instructions. Note that -mno-jsr doesn't mean "don't use JSR" ๐Ÿ™…โ€โ™€๏ธ. It just means any type of branch may be used.
  • -mlra โ€” Uses the new LRA register allocator ๐Ÿ†•. By default the old "reload" allocator is used.

Note: the generic GCC command-line option -ffixed-reg has special significance for the RX port when used with the "interrupt" function attribute ๐Ÿ’ซ. That attribute marks a function meant to process fast interrupts. GCC makes sure it only uses the registers "r10", "r11", "r12" and/or "r13", and only when the normal use of those registers has been restricted with -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 โ€” Use (or don't 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, the compiler generates IEEE floating-point instructions. This is the default โœจ
  • -mhard-dfp, -mno-hard-dfp โ€” Use (or don't use) the hardware decimal-floating-point instructions for decimal-floating-point operations ๐Ÿงฎ. With -mno-hard-dfp, functions in libgcc.a handle decimal-floating-point operations. With -mhard-dfp, the compiler generates 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 to debug 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 ๐Ÿ“

Code compiled with -mbackchain is call-compatible with code compiled with -mno-backchain. However, using the backchain for debugging usually requires the whole binary to be built with -mbackchain ๐Ÿ™ˆ

The combination of -mbackchain, -mpacked-stack and -mhard-float is not supported ๐Ÿ’€. To build a Linux kernel, use -msoft-float.

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 ๐Ÿ‘‘

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 though: 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 combination 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 โœจ
-m31 is deprecated and support will be removed ๐Ÿ˜ญ
  • -mzarch, -mesa โ€” With -mzarch, GCC generates code using the instructions available on z/Architecture ๐ŸŒŒ. With -mesa, it generates code using 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. When generating code compliant with the GNU/Linux for zSeries ABI, the default is -mzarch ๐Ÿ’–
  • -mhtm, -mno-htm โ€” โœจ okay so -mhtm unlocks a set of S/390 System z Built-in Functions that use instructions from the transactional execution facility, introduced with the IBM zEnterprise EC12 machine generation ๐Ÿงš. It's on by default when you use -march=zEC12 ๐Ÿ’–.
  • -mvx, -mno-vx โ€” bestie, -mvx makes GCC generate code with the vector extension facility from 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 show up somewhere ABI-relevant, GCC adds a GAS .gnu_attribute command so the binary is marked with the ABI it used ๐Ÿ“. -mvx is on by default with -march=z13 โœจ.
  • -mzvector, -mno-zvector โ€” this one is giving vector language extensions ๐Ÿ’…. -mzvector turns on vector language extensions and builtins using the z13 vector extension facility, and it lets you use vector as a keyword for vector type variables and arguments ๐ŸŽ€. Heads up: vector only works when GNU extensions are on, and it's not expanded when you ask for strict standard compliance, like with -std=c99 ๐Ÿ™ˆ. On top of the GCC low-level builtins, -mzvector also adds builtins for compatibility with AltiVec-style implementations like Power and Cell ๐ŸŒˆ. To actually use them, you have to include the header file vecintrin.h ๐Ÿ“š. -mzvector is disabled by default ๐Ÿซถ.
  • -mmvcle, -mno-mvcle โ€” -mmvcle tells GCC to generate code that uses the mvcle instruction for block moves ๐Ÿšš. With -mno-mvcle, it uses an mvc loop instead ๐Ÿ”. This is the default unless you're optimizing for size ๐ŸŒธ.
  • -mdebug, -mno-debug โ€” -mdebug makes the compiler print extra debug info while it compiles ๐Ÿ”. -mno-debug keeps it quiet, and quiet is the default ๐Ÿคซ.
  • -march=cpu-type โ€” the big one ๐Ÿ‘‘. This generates code that runs on cpu-type, which is 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 ๐Ÿ“.

Using native as the cpu type picks the best architecture option for your host processor ๐Ÿ’œ. Just know that -march=native does nothing if GCC doesn't recognize the processor ๐Ÿ’€.

  • -mtune=cpu-type โ€” -mtune tells GCC to tune everything applicable in the generated code to cpu-type ๐ŸŽฏ. The one exception is the ABI and the set of available instructions, which it leaves alone ๐Ÿ™…โ€โ™€๏ธ. The list of values is the same as for -march โœจ. The default is whatever value was used for -march ๐Ÿ’….
  • -mtpf-trace, -mno-tpf-trace โ€” -mtpf-trace makes GCC add TPF OS specific branches to trace routines in the operating system ๐Ÿ›ฐ๏ธ. -mno-tpf-trace skips adding them. This option is off by default, even when you're compiling for TPF OS ๐Ÿ™ƒ.
  • -mtpf-trace-skip, -mno-tpf-trace-skip โ€” -mtpf-trace-skip changes the default branch targets enabled by -mtpf-trace so they point at specialized trace routines ๐ŸŽฏ. These let you selectively skip function trace entries for TPF OS ๐Ÿฆ‹. -mno-tpf-trace-skip leaves those targets alone. This is also off by default, even when you're compiling for TPF OS and passing -mtpf-trace ๐Ÿ˜ญ.
  • -mmain โ€” for the TPF OS target, use this one when linking ๐Ÿ”—. It adds the startup code and other linker options so you get a main object ๐ŸŽ€.
  • -mfused-madd, -mno-fused-madd โ€” -mfused-madd makes GCC use the floating-point multiply and accumulate instructions โž•โœ–๏ธ. -mno-fused-madd tells it not to ๐Ÿ™…โ€โ™€๏ธ. These instructions get generated by default when hardware floating point is in use ๐Ÿ’–.
  • -mwarn-framesize=framesize โ€” this one's a little warning guard ๐Ÿšจ. It makes GCC emit a warning if the current function goes over the frame size you give it ๐Ÿ“. It's a compile-time check, so it doesn't have to be a real problem when the program runs ๐Ÿซถ. It's meant to flag functions that will most likely cause a stack overflow ๐Ÿ’€. It's super useful in environments with a limited stack size, like the Linux kernel ๐Ÿง. A value of zero turns this warning off, and zero is the default ๐ŸŒธ.
  • -mwarn-dynamicstack, -mno-warn-dynamicstack โ€” -mwarn-dynamicstack makes GCC warn if a function calls alloca or uses dynamically-sized arrays โš ๏ธ. This is generally a bad idea when the stack size is limited ๐Ÿ™ˆ. -mno-warn-dynamicstack turns the warning off.
  • -mstack-guard=stack-guard, -mstack-size=stack-size, -mno-stack-guard, -mno-stack-size โ€” these are the stack overflow detectives ๐Ÿ•ต๏ธโ€โ™€๏ธ. When they're enabled, the S/390 back end adds extra instructions to the function prologue that trigger a trap if the stack goes stack-guard bytes above stack-size ๐Ÿชค. (Remember, the stack on S/390 grows downward โฌ‡๏ธ.) If you leave out stack-guard, GCC picks the smallest power of 2 that's larger 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 can run on production-like systems without a big performance hit ๐Ÿš€.

Some rules, bestie: the given 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 ๐Ÿ“. The -mstack-guard= option only works together with -mstack-size= ๐Ÿซ‚. You can override these on the command line with -mno-stack-guard and -mno-stack-size, respectively ๐Ÿฆ‹.

  • -mhotpatch=pre-halfwords,post-halfwords โ€” if hotpatch 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, max 1000000) ๐Ÿซง. After the label, 2 * post-halfwords bytes get appended, using the largest NOP-like instructions the architecture allows (max 1000000) ๐Ÿงฉ.

If both arguments are zero, hotpatching is disabled ๐Ÿ™…โ€โ™€๏ธ. You can override this for individual functions with the hotpatch attribute โœจ.

  • -mpic-data-is-text-relative, -mno-pic-data-is-text-relative โ€” when you compile for S/390 with -fpic or -fPIC, GCC normally uses relative addressing between code and data ๐Ÿ—บ๏ธ. But for hotpatching, you might need to add new .text parts while still using the existing .data and .bss sections ๐Ÿง . In that case, use -mno-pic-data-is-text-relative to force addressing through the GOT instead ๐ŸŽ€.
  • -mstack-protector-guard=guard โ€” this 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) ๐Ÿงต.
  • -mstack-protector-guard-record โ€” this one results in a section called __stack_protector_loc that holds pointers to every instruction that loads the address of the global guard ๐Ÿ“. So it only does something alongside -mstack-protector-guard=global ๐Ÿซถ. It's intended for the Linux kernel ๐Ÿง.
  • -mindirect-branch=choice, -mindirect-branch-jump=choice, -mindirect-branch-call=choice, -mfunction-return=choice, -mfunction-return-mem=choice, -mfunction-return-reg=choice โ€” this whole group is about security ๐Ÿ›ก๏ธ. It deals with vulnerabilities around speculative execution and indirect branch prediction by swapping indirect branches and function returns for direct branches to a thunk ๐Ÿงตโœจ.

Here's how the choice values work, bestie ๐Ÿ’…:

  • keep gives you the default code.
  • thunk generates out-of-line thunks and replaces the old indirect branch with a direct branch to the thunk.
  • thunk-extern does the same branch replacement as thunk, but doesn't emit the thunks ๐Ÿซฅ.
  • thunk-inline is only available with -mindirect-branch-jump. You should prefer it over thunk in user-space applications, since it keeps stack unwinding and exception handling correct ๐Ÿง .

-mindirect-branch sets both -mindirect-branch-jump and -mindirect-branch-call ๐Ÿ”—. -mfunction-return sets both -mfunction-return-reg and -mfunction-return-mem ๐Ÿ”—. And you can set all of these per function using function attributes ๐ŸŽฏ.

  • -mindirect-branch-table, -mno-indirect-branch-table โ€” this is a team player ๐Ÿค. It works with -mindirect-branch and -mfunction-return. When enabled, it generates tables pointing to the branch locations those options patched ๐Ÿ—‚๏ธ. The tables live in sections named .s390_indirect_jump, .s390_indirect_call, .s390_return_reg, and .s390_return_mem ๐Ÿ“ฆ. Each section is an array of 32-bit elements, and each entry holds the offset from the entry to the patched location ๐Ÿ”ข.
  • -mfentry, -mno-fentry โ€” used together with -pg, -mfentry emits profiling code using the __fentry__ hook that GLIBC 2.29 or later provides ๐Ÿ“ˆ. This is only available for 64-bit code ๐Ÿ’ป. If you don't enable it, profiling falls back to the less efficient _mcount hook ๐Ÿข.
  • -mrecord-mcount, -mno-record-mcount โ€” when enabled, GCC generates a __mcount_loc section that holds the locations of all the generated profiling calls to _mcount or __fentry__ ๐Ÿ“‹.
  • mnop-mcount, mno-nop-mcount โ€” instead of generating calls to _mcount or __fentry__ with -pg, this inserts a sequence of no-op instructions of the same length right where those calls would have gone ๐Ÿ˜ด. Pair it with -mrecord-mcount and you can patch the call sequences into the object file without recompiling it ๐Ÿงฉโœจ.
  • -mpreserve-args, -mno-preserve-args โ€” when enabled, GCC saves all the argument registers to the stack and generates the matching CFI information ๐Ÿ—ƒ๏ธ. This is handy for apps that want to build their own stack unwinding and need access to function arguments ๐Ÿซถ.
  • -munaligned-symbols, -mno-unaligned-symbols โ€” this tells GCC to assume that external symbols with a natural alignment of 1 might be unaligned ๐Ÿ™ˆ. By default, every symbol without explicit alignment is assumed to be aligned on a 2-byte boundary, as the IBM Z ABI requires ๐Ÿ“.

๐Ÿฆ‹ 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 โ€” okay bestie, this one is for the SH2a, or an SH2a-FPU that you want to act like it has no FPU ๐Ÿฆ‹. The floating-point unit never gets used.
  • -m2a-single-only โ€” SH2a-FPU code that never does double-precision floating-point math. Very single-precision core ๐Ÿ’œ.
  • -m2a-single โ€” SH2a-FPU code that assumes the FPU starts out in single-precision mode โœจ.
  • -m2a โ€” SH2a-FPU code that assumes the FPU starts out in double-precision mode ๐Ÿ‘‘.
  • -m3 โ€” SH3 code, plain and simple ๐ŸŒธ.
  • -m3e โ€” SH3e code ๐Ÿงš.
  • -m4-nofpu โ€” SH4 code with no floating-point unit at all ๐Ÿซถ.
  • -m4-single-only โ€” SH4 code for a floating-point unit that can only do single-precision arithmetic ๐Ÿ’….
  • -m4-single โ€” SH4 code that assumes the FPU is in single-precision mode by default ๐ŸŽ€.
  • -m4 โ€” SH4 code, the classic ๐Ÿ’–.
  • -m4-100 โ€” SH4-100 code ๐Ÿ“.
  • -m4-100-nofpu โ€” SH4-100 code that keeps the floating-point unit out of it completely ๐ŸŒˆ.
  • -m4-100-single โ€” SH4-100 code that assumes the FPU is in single-precision mode by default โญ.
  • -m4-100-single-only โ€” SH4-100 code where no double-precision floating-point operations sneak in ๐Ÿ”ฅ.
  • -m4-200 โ€” SH4-200 code ๐Ÿ’œ.
  • -m4-200-nofpu โ€” SH4-200 code, written so the floating-point unit is not used. The wording in the original is a bit garbled here, but the vibe is "no FPU, please" ๐Ÿ™ˆ.
  • -m4-200-single โ€” SH4-200 code that assumes the FPU is in single-precision mode by default ๐Ÿฆ‹.
  • -m4-200-single-only โ€” SH4-200 code where no double-precision floating-point operations are used ๐ŸŒธ.
  • -m4-300 โ€” SH4-300 code ๐Ÿงš.
  • -m4-300-nofpu โ€” SH4-300 code, again written so the floating-point unit is not used. Same little wording glitch as the SH4-200 one ๐Ÿ’€.
  • -m4-300-single โ€” SH4-300 code where no double-precision floating-point operations are used. Yes, the original says exactly that, so we're keeping it ๐Ÿซถ.
  • -m4-300-single-only โ€” SH4-300 code where no double-precision floating-point operations are used. Same as the one right above it, periodt ๐Ÿ’….
  • -m4-340 โ€” SH4-340 code. No MMU, no FPU, very minimal, very iconic โœจ.
  • -m4-400 โ€” SH4-400 code. Also no MMU and no FPU ๐ŸŒˆ.
  • -m4-500 โ€” SH4-500 code with no FPU ๐Ÿ”ฅ. It also passes -isa=sh4-nofpu to the assembler, so the assembler knows the deal too ๐Ÿงš.
  • -m4a-nofpu โ€” code for the SH4al-dsp, or for an SH4a that doesn't touch the floating-point unit ๐Ÿ’–.
  • -m4a-single-only โ€” SH4a code where no double-precision floating-point operations are used ๐ŸŽ€.
  • -m4a-single โ€” SH4a code that assumes the FPU is in single-precision mode by default ๐Ÿ“.
  • -m4a โ€” SH4a code ๐Ÿ‘‘.
  • -m4al โ€” basically the same as -m4a-nofpu, except it also quietly passes -dsp to the assembler. Also, GCC doesn't generate any DSP instructions right now, so that part is more of a heads-up than a feature ๐Ÿ™ˆ.
  • -mb โ€” compiles for big-endian mode ๐ŸŒŠ.
  • -ml โ€” compiles for little-endian mode ๐ŸŒŠ.
  • -mdalign โ€” aligns doubles on 64-bit boundaries ๐Ÿ“. Heads up though: this changes the calling conventions, so some standard C library functions won't work unless you recompile them with -mdalign first. Not a vibe, that's a warning โš ๏ธ.
Heads up: -mdalign changes the calling conventions, so standard C library functions that weren't recompiled with -mdalign may break. Recompile first, bestie ๐Ÿ’€.
  • -mrelax โ€” shortens some address references at link time when it's possible, and it does this by using the linker option -relax ๐Ÿฆ‹.
  • -mbigtable โ€” uses 32-bit offsets in "switch" tables. The default is 16-bit offsets, so this one is the upgrade ๐Ÿงš.
  • -mbitops โ€” turns on bit manipulation instructions on SH2A ๐Ÿ’….
  • -mfmovd โ€” turns on the fmovd instruction ๐ŸŒธ. Check -mdalign for the alignment rules that come with it ๐Ÿซถ.
  • -mrenesas, -mno-renesas โ€” -mrenesas makes the compiler follow the calling conventions defined by Renesas ๐Ÿ‘‘. The default for every SH toolchain target is -mno-renesas, which uses the calling conventions GCC had before the Renesas ones existed โœจ.
  • -mnomacsave โ€” marks the "MAC" register as call-clobbered, even when -mrenesas is also given ๐Ÿ’œ.
  • -mieee, -mno-ieee โ€” these control how IEEE compliant floating-point comparisons are, which affects what happens when a comparison result is unordered ๐Ÿ”ฅ. -mieee is implicitly on by default. If -ffinite-math-only is on, -mno-ieee gets implicitly set, and that makes floating-point greater-equal and less-equal comparisons faster โญ. You can override the implicit setting by explicitly passing either -mieee or -mno-ieee ๐Ÿ’….
  • -minline-ic_invalidate, -mno-inline-ic_invalidate โ€” inlines code that invalidates instruction cache entries after setting up nested function trampolines ๐Ÿงš. This option does nothing if -musermode is in effect and your code generation option (like -m4) doesn't allow the icbi instruction ๐Ÿ™ˆ. If the code generation option doesn't allow icbi and -musermode isn't in effect, the inlined code pokes the instruction cache address array directly with an associative write. That needs privileged mode at run time, and it also breaks if the cache line was mapped through the TLB and has since become unmapped ๐Ÿ’€.
  • -misize โ€” dumps instruction size and location into the assembly code ๐Ÿ“.
  • -matomic-model=model โ€” sets the model for atomic operations, plus any extra parameters, as a comma-separated list ๐ŸŒˆ. For the full scoop on atomic built-in functions, check __atomic Builtins. These are the models and parameters you can pick from:
    • none โ€” turns off compiler-generated atomic sequences and emits library calls for atomic operations instead ๐Ÿซถ. This is the default when the target isn't sh*-*-linux*.
    • soft-gusa โ€” generates GNU/Linux-compatible gUSA software atomic sequences for the atomic built-ins ๐Ÿฆ‹. These need extra help from the system's interrupt and exception handling code, and they're only good for single-core SH3* and SH4* systems ๐Ÿ’œ. This option is on by default when the target is sh*-*-linux* and the chip is SH3* or SH4*. When the target is SH4A, it also partly uses the hardware atomic instructions movli.l and movco.l to make the code faster, unless you specify strict ๐Ÿ”ฅ.
    • soft-tcb โ€” generates software atomic sequences that use a variable in the thread control block โญ. It's basically a variation of gUSA sequences that also works on SH1* and SH2* targets ๐ŸŒธ. These sequences also need extra help from the interrupt and 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 turn interrupts off by setting SR.IMASK = 1111 ๐Ÿ™ˆ. This model only works when the program runs in privileged mode, and it's only for single-core systems ๐Ÿ’…. It doesn't need extra help from the system's interrupt and exception handling code ๐Ÿซถ. It's on by default when the target is sh*-*-linux* and the chip is SH1* or SH2* ๐Ÿ‘‘.
    • hard-llcs โ€” generates hardware atomic sequences using only the movli.l and movco.l instructions ๐Ÿ’–. It's only available on SH4A and is good for multi-core systems ๐ŸŒˆ. Those hardware instructions only support 32-bit atomic variables, so 8-bit and 16-bit access gets emulated with 32-bit accesses ๐Ÿ”ฅ. Code built with this option is still compatible with other software atomic model interrupt and exception handling systems, as long as it runs on an SH4A system โœจ. It doesn't need any extra help from the system's interrupt and exception handling code ๐Ÿฆ‹.
    • gbr-offset= โ€” this parameter sets the offset in bytes of the variable in the thread control block structure that the generated atomic sequences use when soft-tcb is picked ๐Ÿงš. For other models it gets ignored ๐Ÿ’€. The value has to be an integer multiple of four and in the range 0-1020 ๐Ÿ“.
    • strict โ€” stops you from mixing multiple atomic models, even if they'd be compatible, and makes the compiler generate atomic sequences for only the model you specified ๐Ÿ”’.
  • -mtas โ€” generates the tas.b opcode for __atomic_test_and_set ๐Ÿ”ฅ. Heads up, this can hurt overall performance depending on your hardware and software setup, because tas.b forces operand cache line flushes โš ๏ธ. On multi-core SH4A processors, tas.b has to be used with real caution, since it can corrupt data with certain cache configurations ๐Ÿ’€.
  • -mprefergot โ€” when generating position-independent code, makes function calls go through the Global Offset Table instead of the Procedure Linkage Table ๐ŸŒธ.
  • -musermode, -mno-usermode โ€” -musermode stops the compiler from generating privileged mode code, and -mno-usermode lets it do so ๐Ÿ‘‘. -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, because those chips have no user mode ๐Ÿ’….
  • -multcost=number โ€” sets the cost the compiler assumes for a multiply instruction ๐Ÿงฎ.
  • -mdiv=strategy โ€” picks the division strategy for integer division operations ๐ŸŒˆ. The strategy can be one of these:
    • call-div1 โ€” calls a library function that does the operation with the single-step division instruction div1 ๐ŸŽ€. 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 an FPU ๐Ÿ‘‘. If you specify this on a target that doesn't have a double-precision FPU, it falls back to call-div1 ๐Ÿฆ‹.
  • call-table โ€” okay so this one calls a library function that uses a lookup table for small divisors ๐Ÿ“š, and for bigger divisors it switches to the div1 instruction with case distinction ๐Ÿ’…. Dividing by zero gives an unspecified result and does not trap. This is the default for SH4 ๐ŸŒธ. Picking it for targets without dynamic shift instructions makes call-div1 the default instead โœจ.

When you don't pick a division strategy, GCC chooses one 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, -mno-accumulate-outgoing-args โ€” reserve space once for outgoing arguments in the function prologue, instead of around each call ๐Ÿงš. This is generally great for performance and size, and it's also needed for unwinding so the stack frame doesn't change around conditional code ๐Ÿ’–. -maccumulate-outgoing-args is enabled by default.
  • -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, and clobbered registers as if this option weren't there ๐Ÿซถ.
  • -mfixed-range=register-range โ€” tells GCC to treat the given register range as fixed registers ๐Ÿ”’. A fixed register is one the register allocator can't use, which is super useful when compiling kernel code ๐Ÿ‘‘. A register range is written as 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 to generate more branch-free code when it can ๐ŸŒˆ. If you don't specify it, the value depends on the processor type you're compiling for ๐ŸŽ€.
  • -mzdcbranch, -mno-zdcbranch โ€” these assume (or don't assume) that zero displacement conditional branch instructions bt and bf are fast โšก. With -mzdcbranch, the compiler prefers zero displacement branch code sequences ๐Ÿฆ‹. It's enabled by default when generating code for SH4 and SH4A, and you can explicitly turn it off with -mno-zdcbranch ๐Ÿ™ˆ.
  • -mcbranch-force-delay-slot โ€” forces the use of delay slots for conditional branches, filling the delay slot with a nop if a suitable instruction can't be found ๐Ÿงธ. It's disabled by default. You can turn it on to work around hardware bugs like the ones found in the original SH7055 ๐Ÿ› ๏ธ.
  • -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. Using -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 with -funsafe-math-optimizations and -ffinite-math-only ๐Ÿซง. It's enabled by default when generating code for SH4A. Using -mno-fsrra disables reciprocal square root approximations even if -funsafe-math-optimizations and -ffinite-math-only are in effect ๐Ÿ’€.
  • -mpretend-cmove, -mno-pretend-cmove โ€” prefer or don't prefer zero-displacement conditional branches for conditional move instruction patterns ๐Ÿ“. This can make code faster on the SH4 processor ๐Ÿš€.
  • -mfdpic, -mno-fdpic โ€” generates code using the FDPIC ABI ๐ŸŒธ.
  • -mlra, -mno-lra โ€” uses the new LRA register allocator ๐Ÿ†•. By default, the old reload allocator is used ๐Ÿ•ฐ๏ธ.

๐ŸŒธ Solaris 2 Options ๐ŸŒธ

These options are supported on Solaris 2 ๐Ÿ’œ:

  • -mclear-hwcap, -mno-clear-hwcap โ€” -mclear-hwcap tells the compiler to remove the hardware capabilities generated by the Solaris assembler ๐Ÿงน. You only need this when object files use ISA extensions the current machine doesn't support, but the code checks at runtime whether or not to use them ๐Ÿ”.
  • -mimpure-text, -mno-impure-text โ€” used together with -shared, -mimpure-text 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 ๐Ÿงฉ.
Yikes, read this part ๐Ÿ˜ญ: -mimpure-text suppresses the "relocations remain against allocatable but non-writable sections" linker error message. However, the necessary relocations trigger copy-on-write, so the shared object is not actually shared across processes ๐Ÿ’”. Instead of using -mimpure-text, you should compile all source code with -fpic or -fPIC โœจ.
  • -gsctf โ€” generates Solaris CTF ๐Ÿ“ฆ. It needs to be used for both compilation and linking ๐Ÿ”—. See ctf(7) for more information ๐Ÿ“–. This is only supported since Solaris 11.4 SRU 84, where the necessary toolchain support was added ๐ŸŽ‰.

๐ŸŒˆ SPARC Options ๐ŸŒˆ

These -m options are supported on the SPARC ๐Ÿฆ‹:

  • -mno-app-regs, -mapp-regs โ€” -mapp-regs generates output using the global registers 2 through 4, which the SPARC SVR4 ABI reserves for applications ๐ŸŽ€. Like global register 1, each of global registers 2 through 4 is then treated as an allocable register that gets clobbered by function calls ๐Ÿ’…. This is the default.

To be fully SVR4 ABI-compliant (with some performance loss) use -mno-app-regs ๐Ÿซถ. You should compile libraries and system software with this option ๐Ÿ“š.

  • -mflat, -mno-flat โ€” with -mflat, the compiler doesn't generate save/restore instructions and uses a "flat" or single register window model โœจ. This model is compatible with the regular register window model ๐Ÿงš. The local registers and the input registers (0--5) are still treated as "call-saved" registers and are saved on the stack as needed ๐Ÿฅž.

With -mno-flat (the default), the compiler generates save/restore instructions, except for leaf functions ๐ŸŒท. This is the normal operating mode ๐Ÿ’–.

  • -mfpu, -mhard-float โ€” generates output containing floating-point instructions ๐Ÿ”ฅ. This is the default ๐Ÿ‘‘.
  • -mno-fpu, -msoft-float โ€” generates output containing library calls for floating point ๐Ÿ“ž.
Warning โš ๏ธ: the requisite libraries are not available for all SPARC targets ๐Ÿ˜ฌ. Normally the facilities of the machine's usual C compiler are used, but this can't be done directly in cross-compilation ๐Ÿ™ƒ. You must make your own arrangements to provide suitable library functions for cross-compilation ๐Ÿ› ๏ธ. The embedded targets sparc-*-aout and sparclite-*-* do provide software floating-point support ๐ŸŒธ.

-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 ๐Ÿงฉ.

  • -mhard-quad-float โ€” generates output containing quad-word (long double) floating-point instructions ๐Ÿ’ซ.
  • -msoft-quad-float โ€” generates output containing library calls for quad-word (long double) floating-point instructions ๐Ÿ“š. The functions called are the ones specified in the SPARC ABI, and this is the default ๐ŸŽ€.

As of this writing, there are no SPARC implementations with hardware support for the quad-word floating-point instructions ๐Ÿ˜ญ. They all invoke a trap handler for one of these instructions, and then the trap handler emulates the effect of the instruction ๐Ÿซ . Because of that trap handler overhead, this is much slower than calling the ABI library routines ๐Ÿข. That's why -msoft-quad-float is the default ๐Ÿ’œ.

  • -mno-unaligned-doubles, -munaligned-doubles โ€” -mno-unaligned-doubles assumes that doubles have 8-byte alignment. This is the default ๐Ÿ“.

With -munaligned-doubles, GCC assumes doubles have 8-byte alignment only if they're contained in another type or have an absolute address ๐Ÿง. Otherwise, it assumes they have 4-byte alignment ๐Ÿค. Using this option avoids some rare compatibility problems with code generated by other compilers ๐Ÿซถ. It isn't the default because it causes a performance loss, especially for floating-point code ๐Ÿ“‰.

  • -muser-mode, -mno-user-mode โ€” -mno-user-mode doesn't generate code that can only run in supervisor mode ๐Ÿ™…. This only matters for the casa instruction emitted for the LEON3 processor ๐Ÿง . This is the default โœจ.
  • -mfaster-structs, -mno-faster-structs โ€” with -mfaster-structs, the compiler assumes structures should have 8-byte alignment ๐Ÿ“. This enables pairs of ldd and std instructions for copies in structure assignment, in place of twice as many ld and st pairs ๐ŸŽ๏ธ. However, this changed alignment directly violates the SPARC ABI ๐Ÿšซ. So it's only meant for targets where the developer acknowledges their resulting code isn't directly in line with the ABI rules ๐Ÿ™ˆ.
  • -mstd-struct-return, -mno-std-struct-return โ€” with -mstd-struct-return, the compiler generates checking code in functions returning structures or unions to detect size mismatches between the two sides of function calls, as per the 32-bit ABI ๐Ÿ”Ž. The default is -mno-std-struct-return ๐Ÿ’…. This option has no effect in 64-bit mode ๐Ÿšซ.
  • -mcpu=cpu_type โ€” sets the instruction set, register set, and instruction scheduling parameters for the machine type cpu_type ๐Ÿ–ฅ๏ธ. Supported values for cpu_type 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 support the value native, which picks the best architecture option for the host processor ๐Ÿง โœจ. -mcpu=native has no effect if GCC doesn't recognize the processor ๐Ÿคท.

Default instruction scheduling parameters are used for values that select an architecture rather than an implementation ๐Ÿ—๏ธ. Those are v7, v8, sparclite, sparclet, and v9 ๐ŸŒŸ.

Here's the lineup of each supported architecture and its implementations ๐Ÿ’…:

  • 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 cooks up code for the V7 flavour of SPARC ๐ŸŒธ Add -mcpu=cypress and it also gets tuned for the Cypress CY7C602 chip, the one living in the SPARCStation/SPARCServer 3xx series ๐Ÿ’… It's also a great fit for the older SPARCStation 1, 2, IPX etc. ๐Ÿซถ

With -mcpu=v8, GCC generates code for the V8 variant of SPARC โœจ The only real difference from V7 code is that the compiler emits the integer multiply and integer divide instructions that exist in SPARC-V8 but not in SPARC-V7 ๐Ÿ”ฅ Add -mcpu=supersparc and it's additionally optimized for the SuperSPARC chip, the one in the SPARCStation 10, 1000 and 2000 series ๐Ÿ‘‘

With -mcpu=sparclite, GCC generates code for the SPARClite variant ๐Ÿงš This adds the integer multiply, integer divide step and scan ("ffs") instructions that exist in SPARClite but not in SPARC-V7. Then -mcpu=f930 further optimizes it for the Fujitsu MB86930 chip, which is the original SPARClite and has no FPU ๐Ÿ™ˆ And -mcpu=f934 optimizes it for the Fujitsu MB86934 chip, the more recent SPARClite that does have an FPU ๐Ÿฆ‹

With -mcpu=sparclet, GCC generates code for the SPARClet variant of SPARC ๐Ÿ’œ This adds the integer multiply, multiply/accumulate, integer divide step and scan ("ffs") instructions that exist in SPARClet but not in SPARC-V7. Add -mcpu=tsc701 and it's optimized for the TEMIC SPARClet chip ๐ŸŽ€

With -mcpu=v9, GCC generates code for the V9 variant of SPARC, and it's giving upgrade ๐ŸŒˆ This adds 64-bit integer and floating-point move instructions, 3 additional floating-point condition code registers and conditional move instructions. Then:

  • -mcpu=ultrasparc optimizes for the Sun UltraSPARC I/II/IIi chips ๐Ÿ’–
  • -mcpu=ultrasparc3 optimizes for the Sun UltraSPARC III/III+/IIIi/IIIi+/IV/IV+ chips โœจ
  • -mcpu=niagara optimizes for Sun UltraSPARC T1 chips ๐Ÿ“
  • -mcpu=niagara2 optimizes for Sun UltraSPARC T2 chips ๐ŸŒธ
  • -mcpu=niagara3 optimizes for Sun UltraSPARC T3 chips ๐Ÿฆ‹
  • -mcpu=niagara4 optimizes for Sun UltraSPARC T4 chips ๐Ÿ’…
  • -mcpu=niagara7 optimizes for Oracle SPARC M7 chips ๐Ÿ‘‘
  • -mcpu=m8 optimizes for Oracle M8 chips ๐Ÿ”ฅ
  • -mtune=cpu_type โ€” sets the instruction scheduling parameters for machine type cpu_type, but it does not touch the instruction set or register set the way -mcpu=cpu_type does ๐Ÿง

The same values you can use for -mcpu=cpu_type work for -mtune=cpu_type, but only the ones that pick out a particular 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. With native Solaris and GNU/Linux toolchains, native can also be used ๐Ÿซถ

Hmm, the bullet list for -mcpu was list items with chips; fineโ€”they're not option lists exactly. Good.

Continue with the rest. Ensure "-mcpu=v9..." list, "-mtune" bullets, etc. Write final.With -mcpu=v8, GCC generates code for the V8 variant of SPARC โœจ The only real difference from V7 code is that the compiler emits the integer multiply and integer divide instructions that exist in SPARC-V8 but not in SPARC-V7 ๐Ÿ”ฅ Add -mcpu=supersparc and it's also optimized for the SuperSPARC chip, the one in the SPARCStation 10, 1000 and 2000 series ๐Ÿ‘‘

With -mcpu=sparclite, GCC generates code for the SPARClite variant ๐Ÿงš This adds the integer multiply, integer divide step and scan ("ffs") instructions that exist in SPARClite but not in SPARC-V7. Then -mcpu=f930 further optimizes it for the Fujitsu MB86930 chip, the original SPARClite, which has no FPU ๐Ÿ™ˆ And -mcpu=f934 optimizes it for the Fujitsu MB86934 chip, the more recent SPARClite with an FPU ๐Ÿฆ‹

With -mcpu=sparclet, GCC generates code for the SPARClet variant of SPARC ๐Ÿ’œ This adds the integer multiply, multiply/accumulate, integer divide step and scan ("ffs") instructions that exist in SPARClet but not in SPARC-V7. Add -mcpu=tsc701 and it's optimized for the TEMIC SPARClet chip ๐ŸŽ€

With -mcpu=v9, GCC generates code for the V9 variant of SPARC, and it's giving upgrade ๐ŸŒˆ This adds 64-bit integer and floating-point move instructions, 3 additional floating-point condition code registers and conditional move instructions. Then the chip-specific flavours go like this:

  • -mcpu=ultrasparc optimizes for the Sun UltraSPARC I/II/IIi chips ๐Ÿ’–
  • -mcpu=ultrasparc3 optimizes for the Sun UltraSPARC III/III+/IIIi/IIIi+/IV/IV+ chips โœจ
  • -mcpu=niagara optimizes for Sun UltraSPARC T1 chips ๐Ÿ“
  • -mcpu=niagara2 optimizes for Sun UltraSPARC T2 chips ๐ŸŒธ
  • -mcpu=niagara3 optimizes for Sun UltraSPARC T3 chips ๐Ÿฆ‹
  • -mcpu=niagara4 optimizes for Sun UltraSPARC T4 chips ๐Ÿ’…
  • -mcpu=niagara7 optimizes for Oracle SPARC M7 chips ๐Ÿ‘‘
  • -mcpu=m8 optimizes for Oracle M8 chips ๐Ÿ”ฅ
  • -mtune=cpu_type โ€” sets the instruction scheduling parameters for machine type cpu_type, but it does not set the instruction set or register set the way -mcpu=cpu_type does ๐Ÿง

The same values you can use for -mcpu=cpu_type work for -mtune=cpu_type, but only the ones that pick out a particular 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. With native Solaris and GNU/Linux toolchains, native can also be used ๐Ÿซถ

  • -mv8plus, -mno-v8plus โ€” with -mv8plus, GCC generates 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 enabled by default on Solaris in 32-bit mode for all SPARC-V9 processors.
  • -mvis, -mno-vis โ€” with -mvis, GCC takes advantage of the UltraSPARC Visual Instruction Set extensions ๐ŸŒˆ The default is -mno-vis.
  • -mvis2, -mno-vis2 โ€” with -mvis2, GCC takes advantage of version 2.0 of the UltraSPARC Visual Instruction Set extensions ๐Ÿ’œ The default is -mvis2 when targeting a cpu that supports such instructions, such as UltraSPARC-III and later. Setting -mvis2 also sets -mvis.
  • -mvis3, -mno-vis3 โ€” with -mvis3, GCC takes advantage of version 3.0 of the UltraSPARC Visual Instruction Set extensions ๐Ÿฆ‹ The default is -mvis3 when targeting a cpu that supports such instructions, such as niagara-3 and later. Setting -mvis3 also sets -mvis2 and -mvis.
  • -mvis3b, -mno-vis3b โ€” with -mvis3b, GCC takes advantage of version 3.0 of the UltraSPARC Visual Instruction Set extensions, plus the additional VIS instructions introduced in the Oracle SPARC Architecture 2011 ๐Ÿ”ฅ The default is -mvis3b when targeting a cpu that supports such instructions, such as niagara-7 and later. Setting -mvis3b also sets -mvis3, -mvis2 and -mvis.
  • -mvis4, -mno-vis4 โ€” with -mvis4, GCC takes advantage of version 4.0 of the UltraSPARC Visual Instruction Set extensions ๐Ÿ‘‘ The default is -mvis4 when targeting a cpu that supports such instructions, such as niagara-7 and later. Setting -mvis4 also sets -mvis3b, -mvis3, -mvis2 and -mvis.
  • -mvis4b, -mno-vis4b โ€” with -mvis4b, GCC takes advantage of version 4.0 of the UltraSPARC Visual Instruction Set extensions, plus the additional VIS instructions introduced in the Oracle SPARC Architecture 2017 ๐ŸŒธ The default is -mvis4b when targeting a cpu that supports such instructions, such as m8 and later. Setting -mvis4b also sets -mvis4, -mvis3b, -mvis3, -mvis2 and -mvis.
  • -mcbcond, -mno-cbcond โ€” with -mcbcond, GCC takes advantage of the UltraSPARC Compare-and-Branch-on-Condition instructions ๐Ÿ“ The default is -mcbcond when targeting a CPU that supports such instructions, such as Niagara-4 and later.
  • -mfmaf, -mno-fmaf โ€” with -mfmaf, GCC takes advantage of the UltraSPARC Fused Multiply-Add Floating-point instructions โœจ The default is -mfmaf when targeting a CPU that supports such instructions, such as Niagara-3 and later.
  • -mfsmuld, -mno-fsmuld โ€” with -mfsmuld, GCC takes advantage of the Floating-point Multiply Single to Double (FsMULd) instruction ๐Ÿ’– The default is -mfsmuld when targeting a CPU supporting the architecture versions V8 or V9 with FPU, except -mcpu=leon.
  • -mpopc, -mno-popc โ€” with -mpopc, GCC takes advantage of the UltraSPARC Population Count instruction ๐Ÿง The default is -mpopc when targeting a CPU that supports such an instruction, such as Niagara-2 and later.
  • -msubxc, -mno-subxc โ€” with -msubxc, GCC takes advantage of the UltraSPARC Subtract-Extended-with-Carry instruction ๐Ÿ’… The default is -msubxc when targeting a CPU that supports such an instruction, such as Niagara-7 and later.
  • -mfix-at697f โ€” enables the documented workaround for the single erratum of the Atmel AT697F processor (which corresponds to erratum #13 of the AT697E processor) ๐Ÿ› ๏ธ
  • -mfix-ut699 โ€” enables the documented workarounds for the floating-point errata and the data cache nullify errata of the UT699 processor ๐Ÿ› ๏ธ
  • -mfix-ut700 โ€” enables the documented workaround for the back-to-back store errata of the UT699E/UT700 processor ๐Ÿ› ๏ธ
  • -mfix-gr712rc โ€” enables 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 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.
  • -mptr32, -mptr64 โ€” uses 32- or 64-bit pointers, respectively ๐Ÿ’œ Unlike the -m32 and -m64 options, this only affects the pointer size and not the ABI.
  • -mcmodel=which โ€” sets the code model to one of the following ๐Ÿฆ‹
    • 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, and 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 in force on the processor to one of the following ๐Ÿง 
    • 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, as set in the processor's "PSTATE.MM" field ๐Ÿ“–

  • -mstack-bias, -mno-stack-bias โ€” with -mstack-bias, GCC assumes that the stack pointer, and frame pointer if present, are offset by -2047, which must be added back when making stack frame references. This is the default in 64-bit mode. Otherwise, it 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 is recommended that -symbolic or -shared be used instead. ๐Ÿ’…
  • -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 โ€” Skips (or turns on) the optimization that copies a pointer into the ep register when a basic block uses the same index pointer 4 or more times, which also lets the compiler use the shorter sld and sst instructions. -mep is on by default when you optimize. ๐ŸŒธ
  • -mno-prolog-function, -mprolog-function โ€” Stops (or starts) using external functions to save and restore registers in a function's prologue and epilogue. Those helper functions are slower, but they take up less code space when more than one function saves the same number of registers. -mprolog-function is on by default when you optimize. ๐Ÿ’…
  • -mspace โ€” Tries to make the code as tiny as possible. Right now it only switches on -mep and -mprolog-function. ๐Ÿงš
  • -mtda=n โ€” Puts static or global variables that are n bytes or smaller into the tiny data area that the ep register points to. That area holds up to 256 bytes total, or 128 bytes for byte references. ๐Ÿฆ‹
  • -msda=n โ€” Puts static or global variables that are n bytes or smaller into the small data area that the gp register points to. That area can hold up to 64 kilobytes. โœจ
  • -mzda=n โ€” Puts static or global variables that are n bytes or smaller into the first 32 kilobytes of memory. ๐ŸŽ€
  • -mv850 โ€” Tells GCC the target processor is the V850. ๐ŸŒˆ
  • -mv850e3v5 โ€” Tells GCC the target processor is the V850E3V5. The preprocessor constant __v850e3v5__ gets defined when you use this. ๐Ÿ‘‘
  • -mv850e2v4 โ€” Tells GCC the target processor is the V850E3V5. It's an alias for -mv850e3v5. ๐Ÿซถ
  • -mv850e2v3 โ€” Tells GCC the target processor is the V850E2V3. The preprocessor constant __v850e2v3__ gets defined when you use this. ๐Ÿ”ฅ
  • -mv850e2 โ€” Tells GCC the target processor is the V850E2. The preprocessor constant __v850e2__ gets defined when you use this. ๐Ÿ’œ
  • -mv850e1 โ€” Tells GCC the target processor is the V850E1. The preprocessor constants __v850e1__ and __v850e__ get defined when you use this. ๐Ÿ“
  • -mv850es โ€” Tells GCC the target processor is the V850ES. It's an alias for -mv850e1. ๐ŸŒธ
  • -mv850e โ€” Tells GCC the target processor is the V850E. The preprocessor constant __v850e__ gets defined when you use this. โญ

If none of -mv850, -mv850e, -mv850e1, -mv850e2, -mv850e2v3 or -mv850e3v5 is set, GCC picks a default target processor and defines the matching __v850*__ preprocessor constant. ๐Ÿงš

The preprocessor constants __v850 and __v851__ are always defined, no matter which processor variant you're targeting. ๐Ÿ’–

  • -mdisable-callt, -mno-disable-callt โ€” Suppresses generation of the CALLT instruction for the v850e, v850e1, v850e2, v850e2v3 and v850e3v5 flavors of the v850 architecture. This is on by default when the RH850 ABI is in use (see -mrh850-abi) and off by default when the GCC ABI is in use. If CALLT instructions are being generated, the C preprocessor symbol __V850_CALLT__ gets defined. ๐Ÿ™ˆ
  • -mrelax, -mno-relax โ€” Passes (or doesn't pass) the -mrelax command-line option along to the assembler. ๐Ÿฆ‹
  • -mlong-jumps, -mno-long-jumps โ€” Turns off (or back on) generating PC-relative jump instructions. โœจ
  • -msoft-float, -mhard-float โ€” Turns off (or back on) generating hardware floating point instructions. This only matters when the target architecture is V850E2V3 or higher. If hardware floating point instructions are being generated, the C preprocessor symbol __FPU_OK__ gets defined. Otherwise, __NO_FPU__ gets defined. ๐ŸŒˆ
  • -mloop โ€” Turns on the e3v5 LOOP instruction. It isn't enabled by default when the e3v5 architecture is selected, because it's still experimental. ๐Ÿงช
  • -mrh850-abi, -mghs โ€” Turns on support for the RH850 version of the V850 ABI. This is the default. With this ABI version, these rules apply:
  • Integer sized structures and unions get returned through a memory pointer instead of a register.
  • Large structures and unions (more than 8 bytes in size) get passed by value.
  • Functions get 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 ABI version is on, the C preprocessor symbol __V850_RH850_ABI__ gets defined. ๐Ÿ‘‘

  • -mgcc-abi โ€” Turns on support for the old GCC version of the V850 ABI. With this ABI version, these rules apply:
  • Integer sized structures and unions get returned in register r10.
  • Large structures and unions (more than 8 bytes in size) get passed by reference.
  • Functions get 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 ABI version is on, the C preprocessor symbol __V850_GCC_ABI__ gets defined. ๐Ÿซถ

  • -m8byte-align, -mno-8byte-align โ€” Lets double and long long types align on 8-byte boundaries. By default, every object is restricted to at most 4-byte alignment. When -m8byte-align is in effect, the C preprocessor symbol __V850_8BYTE_ALIGN__ gets defined. ๐Ÿ’…
  • -mbig-switch, -mno-big-switch โ€” Generates code that works with big switch tables. Only use this if the assembler or linker complains about out of range branches inside a switch table. ๐Ÿ˜ญ
  • -mapp-regs, -mno-app-regs โ€” -mapp-regs makes the compiler use r2 and r5 in the code it generates. This is the default. -mno-app-regs makes r2 and r5 fixed registers that the compiler won't touch. ๐Ÿ”ฅ
  • -msmall-sld, -mno-small-sld โ€” Turns on (or off) the use of the short load instructions. ๐ŸŒธ
  • -mno-strict-align โ€” Doesn't enforce strict data alignment. ๐Ÿ“
  • -mjump-tables-in-data-section, -mno-jump-tables-in-data-section โ€” Puts jump tables for switch statements in the .data section instead of the .code section (or keeps them in .code). The default is -mno-jump-tables-in-data-section. ๐Ÿงš

โœจ VAX Options โœจ

These -m options are defined for the VAX: ๐Ÿฆ‹

  • -munix โ€” Doesn't output certain jump instructions (aobleq and so on) that the Unix assembler for the VAX can't handle across long ranges. ๐ŸŒˆ
  • -mgnu โ€” Does output those jump instructions, on the assumption that you're using the GNU assembler. โœจ
  • -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. It's still experimental for the VAX, so by default the compiler uses standard reload. ๐Ÿงช
  • -mvaxc-alignment โ€” Uses VAXC conventions for aligning structure members. ๐ŸŽ€
  • -mqmath, -mno-qmath โ€” Turns new instruction patterns for 64-bit integer addition and subtraction on or off. โญ

โœจ Visium Options โœจ

  • -mdebug โ€” Use this when linking programs that do file I/O and are meant to run on an MCM target. It links the libraries libc.a and libdebug.a. You should run the program on the target under the control of the GDB remote debugging stub. ๐Ÿ”ฅ
  • -msim โ€” Use this when linking programs that do file I/O and are meant to run on the simulator. It links the libraries libc.a and libsim.a. ๐Ÿ“
  • -mfpu, -mhard-float โ€” Generates code with floating-point instructions. This is the default. ๐Ÿ’…
  • -mno-fpu, -msoft-float โ€” Generates code with library calls for floating-point instead. ๐Ÿ™ˆ
-msoft-float changes the calling convention in the output file, so it's only useful if you compile your whole program with this option. You also 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 the machine type cpu_type. Supported values for cpu_type are mcm, gr5 and gr6. ๐Ÿ‘‘

mcm is a synonym for 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 the machine type cpu_type, but doesn't 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 can't 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 values 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 no 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: Okay so GCC can make code for VxWorks kernels and real time processes (RTPs) ๐ŸŒธ. This flag switches it over to RTPs, and it also defines the preprocessor macro __RTP__ โœจ.
  • -msmp: This picks the SMP runtimes for linking ๐Ÿ’œ. It's only a thing on PowerPC ๐Ÿ™ˆ and isn't available on VxWorks version 7 or later. On 7+, the choice is part of the VxWorks build configuration, and the library paths are the same either way ๐Ÿงš.
  • -mvthreads: Links against the VxWorks AE vThread runtime environment ๐Ÿฆ‹.
  • -non-static: Links an RTP executable against shared libraries instead of static ones ๐Ÿ”—. -static and -shared work for RTPs too, and -static is the default ๐Ÿ’….
  • -Bstatic, -Bdynamic: These get passed down to the linker ๐Ÿซถ. They're here for Diab compatibility, so Diab folks feel right at home ๐Ÿก.
  • -Xbind-lazy: Turns on lazy binding of function calls ๐Ÿ’ค. It's equivalent to -Wl,-z,now, and it's defined for Diab compatibility too.
  • -Xbind-now: Turns lazy binding off ๐Ÿ™…. This is the default, and it's also there for Diab compatibility.

โœจ x86 Options โœจ

This section is all about the -m options for the x86 family of computers ๐Ÿ’ป.

The next group lets you target a specific processor ๐ŸŽฏ.

  • -march=cpu-type: Generates instructions for the machine type cpu-type ๐Ÿš€. Quick difference check: -mtune=cpu-type only tunes the generated code for that CPU, but -march=cpu-type lets GCC generate code that may not run at all on other processors โš ๏ธ. Specifying -march=cpu-type implies -mtune=cpu-type, except where noted otherwise.

The choices for cpu-type are:

  • native: Picks the CPU to generate code for at compile time by checking the processor type of the machine doing the compiling ๐Ÿ–ฅ๏ธ. -march=native enables all instruction subsets supported by the local machine, so the result might not run on different machines ๐Ÿ’€. -mtune=native makes 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 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 the x86-64 psABI documents for that micro-architecture level. These values don't have a matching -mtune setting, so using -march with them enables generic tuning. Want specific tuning? Use -mtune=other-cpu-type with an appropriate other-cpu-type value.
  • i386: Original Intel i386 CPU ๐Ÿฆ•.
  • i486: Intel i486 CPU. No scheduling is implemented for this chip.
  • i586, pentium: Intel Pentium CPU with no MMX support.
  • lakemont: Intel Lakemont MCU, based on the Intel Pentium CPU.
  • pentium-mmx: Intel Pentium MMX CPU, based on the Pentium core with MMX instruction set support.
  • pentiumpro: 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: Intel Pentium II CPU, based on the Pentium Pro core with MMX and FXSR instruction set support.
  • pentium3, pentium3m: Intel Pentium III CPU, based on the Pentium Pro core with MMX, FXSR and SSE instruction set support.
  • pentium-m: Intel Pentium M, a low-power version of the Intel Pentium III CPU with MMX, SSE, SSE2 and FXSR instruction set support โ˜•. It's used by Centrino notebooks.
  • pentium4, pentium4m: Intel Pentium 4 CPU with MMX, SSE, SSE2 and FXSR instruction set support.
  • prescott: An improved Intel Pentium 4 CPU with MMX, SSE, SSE2, SSE3 and FXSR instruction set support โญ.
  • nocona: An improved Intel Pentium 4 CPU with 64-bit extensions, MMX, SSE, SSE2, SSE3 and FXSR instruction set support โญ.
  • core2: Intel Core 2 CPU with 64-bit extensions, MMX, SSE, SSE2, SSE3, SSSE3, CX16, SAHF and FXSR instruction set support ๐Ÿ’œ.
  • nehalem, corei7: 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: 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: 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 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 โœจ.
  • 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 ๐Ÿงš.
  • 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 ๐Ÿ’….
  • 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 ๐Ÿ‘‘.
  • 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 ๐Ÿ”ฅ.
  • 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 ๐ŸŒˆ.
  • 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 ๐ŸงŠ.
  • 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 ๐Ÿซถ.
  • 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 ๐ŸงŠ.
  • 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 ๐ŸงŠ.
  • 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 ๐Ÿฆ‹.
  • 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 ๐ŸŒธ.
  • alderlake, raptorlake, meteorlake, gracemont: Intel Alder Lake/Raptor Lake/Meteor Lake/Gracemont CPUs, which come 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. ๐Ÿ’–โœจ
  • 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. ๐Ÿ‘‘
  • arrowlake-s, lunarlake: Intel Arrow Lake S/Lunar Lake CPUs, which come 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. ๐ŸŒˆ
  • pantherlake, wildcatlake: Intel Panther Lake/Wildcat Lake CPUs 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 and SM4 instruction set support. ๐Ÿงš
  • novalake: Intel Nova 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, PREFETCHI, APX_F, AVX10.1, AVX10.2 and MOVRS instruction set support. ๐ŸŒธ
  • sapphirerapids, emeraldrapids: Intel Sapphire Rapids/Emerald Rapids CPUs 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. ๐Ÿ’…
  • 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. ๐Ÿ”ฅ
  • 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. ๐Ÿฆ‹
  • diamondrapids: Intel Diamond Rapids CPU with 64-bit extensions, 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, 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, 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, MOVRS and AMX-MOVRS 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: Improved versions of 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: Improved AMD Athlon CPU with MMX, 3DNow!, enhanced 3DNow! and full SSE instruction set support. ๐Ÿ‘‘
  • k8, opteron, athlon64, athlon-fx: Processors based 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: Improved versions of AMD K8 cores with SSE3 instruction set support. ๐Ÿซถ
  • amdfam10, barcelona: CPUs based 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 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 CPUs with x86-64 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 CPUs with x86-64 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 CPUs with x86-64 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 CPUs with x86-64 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 CPUs with x86-64 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 CPUs with x86-64 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 CPUs with x86-64 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 CPUs with x86-64 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.)
  • znver6 โ€” AMD Family 1ah core CPUs with x86-64 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, AVXVNNIINT8, AVXIFMA, AVX512FP16, AVXNECONVERT, AVX512BMM and 64-bit instruction set extensions.)
  • btver1 โ€” CPUs built on AMD Family 14h cores with x86-64 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 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 ๐Ÿ’… It's handled exactly like i486, plus extra MMX instruction set support.
  • winchip2 โ€” IDT WinChip 2 CPU ๐ŸŽ€ It's handled exactly like i486, plus extra 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, RDSEED instruction set support ๐Ÿ’– The CPUs do support AVX and F16C, but -march=lujiazui doesn't turn these 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, 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, LZCNT instruction set support ๐Ÿงš
  • geode โ€” AMD Geode embedded processor with MMX and 3DNow! instruction set support ๐ŸŒˆ

โœจ -mtune=cpu-type โœจ

Tune cpu-type for everything that applies to the generated code, except the ABI and the set of available instructions ๐ŸŽ€ Picking a specific cpu-type schedules things nicely for that exact chip, but the compiler won't spit out code 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 those, -mtune supports 2 extra choices for cpu-type:

  • generic โ€” Optimizes 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 CPUs your users will have, this is the one to use ๐Ÿซถ

As new processors hit the market, the behavior of this option changes. So if you upgrade to a newer GCC, code generation under this option will shift to match the processors that are most popular when that GCC version gets released ๐ŸŒธ

There's no -march=generic option, because -march describes the instruction set the compiler can use, and there's no generic instruction set that fits every processor. -mtune, on the other hand, describes the processor (or, in this case, the whole collection of processors) that the code is optimized for โœจ

  • intel โ€” Optimizes for the most current Intel processors, which are Haswell and Silvermont for this version of GCC ๐Ÿ”ฅ If you know which 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 better on both Diamond Rapids and Clearwater Forest, this is the option you want ๐Ÿ‘‘

As new Intel processors hit the market, this option's behavior is going to change ๐Ÿ”„. So if you upgrade to a newer GCC, the code generated by this option will shift to match the newest Intel chips available when that GCC version comes out โœจ.

There's no -march=intel option, bestie ๐Ÿ™…โ€โ™€๏ธ. -march controls the instruction set the compiler is allowed to use, and there's no single instruction set that every Intel processor shares. -mtune is different: it tells GCC which processor (or, in this case, group of processors) the code is optimized for ๐Ÿ’….

The following options give you more detailed control over which instruction set extensions GCC targets ๐ŸŽ€. Each one has a matching -mno- option that turns those instructions off ๐Ÿ’–.

These extensions are also available as built-in functions. Check out x86 Built-in Functions for the details on which functions get enabled and disabled by these switches ๐Ÿงš.

These options let GCC use the extended instructions in generated code. Apps that do run-time CPU detection have to compile separate files for each supported architecture, using the right flags ๐Ÿซถ.

Heads up ๐Ÿ™ˆ: the file containing the CPU detection code should be compiled without these options.

To control whether 387 or SSE/AVX instructions get generated automatically for floating-point arithmetic, see -mfpmath=, below ๐Ÿ“.

  • -mmmx โ€” Turns on MMX built-in functions ๐ŸŒธ.
  • -msse โ€” Turns on MMX and SSE built-in functions and code generation ๐Ÿฆ‹.
  • -msse2 โ€” Turns on MMX, SSE and SSE2 built-in functions and code generation โœจ.
  • -msse3 โ€” Turns on MMX, SSE, SSE2 and SSE3 built-in functions and code generation โœจ.
  • -mssse3 โ€” Turns on MMX, SSE, SSE2, SSE3 and SSSE3 built-in functions and code generation โœจ.
  • -msse4.1 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3 and SSE4.1 built-in functions and code generation โœจ.
  • -msse4.2 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1 and SSE4.2 built-in functions and code generation โœจ.
  • -msse4 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1 and SSE4.2 built-in functions and code generation ๐Ÿ‘‘.
Quick note ๐Ÿ“: -msse4 turns on both SSE4.1 and SSE4.2 support, while -mno-sse4 turns those features off. Neither form of the option touches SSE4A support, which is controlled separately by -msse4a.
  • -msse4a โ€” Turns on MMX, SSE, SSE2, SSE3 and SSE4A built-in functions and code generation ๐ŸŒˆ.
  • -mavx โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 and AVX built-in functions and code generation ๐Ÿ”ฅ.
  • -mavx2 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX and AVX2 built-in functions and code generation ๐Ÿ”ฅ.
  • -mavx512f โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2 and AVX512F built-in functions and code generation ๐Ÿ’œ.
  • -mavx512cd โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512CD built-in functions and code generation ๐Ÿ’œ.
  • -mavx512vl โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512VL built-in functions and code generation ๐Ÿ’œ.
  • -mavx512bw โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512BW built-in functions and code generation ๐Ÿ’œ.
  • -mavx512dq โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512DQ built-in functions and code generation ๐Ÿ’œ.
  • -mavx512ifma โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512IFMA built-in functions and code generation ๐Ÿ’œ.
  • -mavx512vbmi โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512VBMI built-in functions and code generation ๐Ÿ’œ.
  • -mavx512vpopcntdq โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512VPOPCNTDQ built-in functions and code generation ๐Ÿ’œ.
  • -mavx512vp2intersect โ€” Turns on AVX512VP2INTERSECT built-in functions and code generation ๐Ÿฆ‹.
  • -mavx512vnni โ€” Turns on AVX512VNNI built-in functions and code generation ๐Ÿฆ‹.
  • -mavx512vbmi2 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512VBMI2 built-in functions and code generation ๐Ÿ’œ.
  • -mavx512bf16 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512BF16 built-in functions and code generation ๐Ÿ’œ.
  • -mavx512fp16 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512-FP16 built-in functions and code generation ๐Ÿ’œ.
  • -mavx512bitalg โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512BITALG built-in functions and code generation ๐Ÿ’œ.
  • -mavx512bmm โ€” Turns on AVX512BMM built-in functions and code generation ๐Ÿฆ‹.
  • -mavxvnni โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, and AVXVNNI built-in functions and code generation ๐ŸŒธ.
  • -mavxifma โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, and AVXIFMA built-in functions and code generation ๐ŸŒธ.
  • -mavxvnniint8 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2 and AVXVNNIINT8 built-in functions and code generation ๐ŸŒธ.
  • -mavxneconvert โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, and AVXNECONVERT built-in functions and code generation ๐ŸŒธ.
  • -mavxvnniint16 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2 and AVXVNNIINT16 built-in functions and code generation ๐ŸŒธ.
  • -mavx10.1 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, and AVX10.1 built-in functions and code generation ๐Ÿ‘‘.
  • -mavx10.2 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX10.1 and AVX10.2 built-in functions and code generation ๐Ÿ‘‘.
  • -msha โ€” Turns on SHA1 and SHA256 built-in functions and code generation ๐Ÿ”.
  • -maes โ€” Turns on AES built-in functions and code generation ๐Ÿ”.
  • -mpclmul โ€” Turns on PCLMUL built-in functions and code generation ๐Ÿ”.
  • -mclflushopt โ€” Turns on CLFLUSHOPT instructions ๐Ÿงน.
  • -mclwb โ€” Turns on the CLWB instruction ๐Ÿงน.
  • -mfsgsbase โ€” Turns on FSGSBASE built-in functions and code generation ๐Ÿง .
  • -mptwrite โ€” Turns on PTWRITE built-in functions and code generation ๐Ÿง .
  • -mrdrnd โ€” Turns on RDRND built-in functions and code generation ๐ŸŽฒ.
  • -mf16c โ€” Turns on F16C built-in functions and code generation ๐Ÿง .
  • -mfma โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX and FMA built-in functions and code generation ๐Ÿ”ฅ.
  • -mfma4 โ€” Turns on FMA4 built-in functions and code generation ๐Ÿ”ฅ.
  • -mpconfig โ€” Turns on PCONFIG built-in functions and code generation โš™๏ธ.
  • -mwbnoinvd โ€” Turns on WBNOINVD built-in functions and code generation โš™๏ธ.
  • -mprfchw โ€” Turns on the PREFETCHW instruction ๐ŸŽ๏ธ.
  • -mrdpid โ€” Turns on RDPID built-in functions and code generation ๐Ÿ†”.
  • -mrdseed โ€” Turns on the RDSEED instruction ๐ŸŽฒ.
  • -msgx โ€” Turns on SGX built-in functions and code generation ๐Ÿ”’.
  • -mxop โ€” Turns on XOP built-in functions and code generation ๐Ÿงš.
  • -mlwp โ€” Turns on LWP built-in functions and code generation ๐Ÿงš.
  • -m3dnow โ€” Turns on 3DNow! built-in functions ๐Ÿ•น๏ธ.
  • -m3dnowa โ€” Turns on Athlon 3Dnow! built-in functions ๐Ÿ•น๏ธ.
  • -mpopcnt โ€” Turns on code generation for the popcnt instruction ๐Ÿ”ข.
  • -mabm โ€” Turns on code generation for Advanced Bit Manipulation (ABM) instructions ๐Ÿ”ข.
  • -madx โ€” Turns on flag-preserving add-carry instructions โž•.
  • -mbmi โ€” Turns on BMI built-in functions and code generation ๐Ÿ”ข.
  • -mbmi2 โ€” Turns on BMI2 built-in functions and code generation ๐Ÿ”ข.
  • -mlzcnt โ€” Turns on the LZCNT built-in function and code generation ๐Ÿ”ข.
  • -mfxsr โ€” Turns on FXSAVE and FXRSTOR instructions ๐Ÿ’พ.
  • -mxsave โ€” Turns on XSAVE and XRSTOR instructions ๐Ÿ’พ.
  • -mxsaveopt โ€” Turns on the XSAVEOPT instruction ๐Ÿ’พ.
  • -mxsavec โ€” Turns on XSAVEC instructions ๐Ÿ’พ.
  • -mxsaves โ€” Turns on XSAVES and XRSTORS instructions ๐Ÿ’พ.
  • -mrtm โ€” Turns on RTM built-in functions and code generation ๐Ÿงต.
  • -mhle โ€” Turns on Hardware Lock Elision prefixes ๐Ÿงต.
  • -mtbm โ€” Turns on TBM built-in functions and code generation ๐Ÿ”ข.
  • -mmwaitx โ€” Turns on MWAITX and MONITORX built-in functions and code generation โณ.
  • -mclzero โ€” Turns on CLZERO built-in functions and code generation ๐Ÿงน.
  • -mpku โ€” Turns on PKU built-in functions and code generation ๐Ÿ”‘.
  • -mgfni โ€” Turns on GFNI built-in functions and code generation ๐Ÿ”.
  • -mvaes โ€” Turns on VAES built-in functions and code generation ๐Ÿ”.
  • -mwaitpkg โ€” Turns on WAITPKG built-in functions and code generation โณ.
  • -mvpclmulqdq โ€” Turns on VPCLMULQDQ built-in functions and code generation ๐Ÿ”.
  • -mmovdiri โ€” Turns on MOVDIRI built-in functions and code generation ๐Ÿšš.
  • -mmovdir64b โ€” Turns on MOVDIR64B built-in functions and code generation ๐Ÿšš.
  • -menqcmd โ€” Turns on ENQCMD built-in functions and code generation ๐Ÿ“จ.
  • -muintr โ€” Turns on UINTR built-in functions and code generation ๐Ÿ“จ.
  • -mtsxldtrk โ€” Turns on TSXLDTRK built-in functions and code generation ๐Ÿงต.
  • -mcldemote โ€” Turns on CLDEMOTE built-in functions and code generation ๐Ÿงน.
  • -mserialize โ€” Turns on SERIALIZE built-in functions and code generation ๐Ÿงต.
  • -mamx-tile โ€” Turns on AMX-TILE built-in functions and code generation ๐Ÿงฑ.
  • -mamx-int8 โ€” Turns on AMX-INT8 built-in functions and code generation ๐Ÿงฑ.
  • -mamx-bf16 โ€” Turns on AMX-BF16 built-in functions and code generation ๐Ÿงฑ.
  • -mhreset โ€” Turns on HRESET built-in functions and code generation ๐Ÿ”„.
  • -mkl โ€” Turns on KL built-in functions and code generation ๐Ÿ”‘.
  • -mwidekl โ€” Turns on WIDEKL built-in functions and code generation ๐Ÿ”‘.
  • -mcmpccxadd โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, and CMPCCXADD built-in functions and code generation ๐ŸŒˆ.
  • -mamx-fp16 โ€” Turns on AMX-FP16 built-in functions and code generation ๐Ÿงฑ.
  • -mprefetchi โ€” Turns on PREFETCHI built-in functions and code generation ๐ŸŽ๏ธ.
  • -mraoint โ€” Turns on RAOINT built-in functions and code generation ๐Ÿง .
  • -mamx-complex โ€” Turns on AMX-COMPLEX built-in functions and code generation ๐Ÿงฑ.
  • -msm3 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX and SM3 built-in functions and code generation ๐ŸŒธ.
  • -msm4 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX and SM4 built-in functions and code generation ๐ŸŒธ.
  • -msha512 โ€” Turns on MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX and SHA512 built-in functions and code generation ๐ŸŒธ.
  • -mapxf โ€” Turns on code generation for APX features, including EGPR, PUSH2POP2, NDD, PPX, NF, CCMP and ZU ๐Ÿ‘‘.

Okay bestie, let's get into the x86 extras ๐Ÿฆ‹โœจ

  • -musermsr โ€” slay ๐Ÿ’… this one turns on USER_MSR built-in functions and code generation ๐Ÿ’–
  • -mamx-avx512 โ€” the full glow-up ๐Ÿ‘‘ it supports MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX10.1, AVX10.2 and AMX-AVX512 built-in functions and code generation ๐Ÿ”ฅ
  • -mamx-tf32 โ€” adds AMX-TF32 built-in functions and code generation ๐ŸŒธ
  • -mamx-fp8 โ€” adds AMX-FP8 built-in functions and code generation ๐Ÿงš
  • -mmovrs โ€” adds MOVRS built-in functions and code generation ๐ŸŽ€
  • -mamx-movrs โ€” adds AMX-MOVRS built-in functions and code generation ๐Ÿซถ

These extra options are all for the x86 processor family ๐ŸŒˆ


๐ŸŒˆ -mfpmath=unit

This one picks which unit does your floating-point math, unit. The choices are:

  • 387 โ€” uses the standard 387 floating-point coprocessor that's in most chips, and emulates it on the ones that don't have it ๐Ÿซง Code built this way runs almost everywhere ๐ŸŒ Temporary results get computed in 80-bit precision instead of the precision the type asks for, so you can get slightly different answers than most other chips. Check out -ffloat-store for the more detailed deets. This is the default choice for non-Darwin x86-32 targets.
  • sse โ€” uses the scalar floating-point instructions from the SSE instruction set ๐Ÿ’ซ Pentium III and newer chips have it, and on the AMD side it's Athlon-4, Athlon XP and Athlon MP chips. The earlier SSE version only handles single-precision arithmetic, so double and extended-precision still go through 387. A later version, found only in Pentium 4 and AMD x86-64 chips, handles double-precision too. For the x86-32 compiler you need -march=cpu-type, -msse or -msse2 to turn on the SSE extensions and make this option work. For the x86-64 compiler those extensions are on by default. The result should be considerably faster in most cases and dodge the numerical instability problems of 387 code ๐Ÿƒโ€โ™€๏ธ but it might 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 x86-32 targets with the SSE2 instruction set when -ffast-math is enabled. Also, GCC switches SSE instructions off when -mavx (or another option enabling AVX extensions) is used. Instead it generates new AVX instructions, or AVX equivalents for all SSE instructions, when needed.
  • sse,387, sse+387, both โ€” tries to use both instruction sets at once ๐Ÿง  This 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 ๐Ÿ˜ญ

  • -mieee-fp, -mno-ieee-fp โ€” controls whether or not the compiler uses IEEE floating-point comparisons ๐Ÿ” These handle the case where the result of a comparison 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 ๐Ÿ“š
Warning: the requisite libraries are not part of GCC! Normally the facilities of the machine's usual C compiler get used, but that can't be done directly in cross-compilation. You have to make your own arrangements to provide 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 be emitted even if -msoft-float is used ๐Ÿ™ˆ
  • -mno-fp-ret-in-387 โ€” stops using the FPU registers for function return values ๐Ÿซ  The usual calling convention has functions return float and double values in an FPU register, even when there's no FPU, since the operating system is supposed to emulate one. -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, so this one stops GCC from generating them ๐Ÿงš It gets overridden when -march says the target CPU always has an FPU, since then the instruction doesn't need emulating. These instructions aren't generated unless you also use the -funsafe-math-optimizations switch.
  • -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 somewhat faster on a Pentium, but at the expense of more memory. On x86-64, -malign-double is on by default.
Warning: if you use -malign-double, structures containing the above types get aligned differently than the published application binary interface specifications for x86-32, so they aren't binary compatible with structures in code compiled without that switch ๐Ÿšจ
  • -m96bit-long-double, -m128bit-long-double โ€” these control the size of the long double type ๐Ÿ“ The x86-32 application binary interface says the size is 96 bits, so -m96bit-long-double is the default in 32-bit mode. Modern architectures (Pentium and newer) prefer long double aligned to an 8- or 16-byte boundary, but in arrays or structures that conform to the ABI, that isn't possible. So -m128bit-long-double aligns long double to a 16-byte boundary by padding it with an additional 32-bit zero. In the x86-64 compiler, -m128bit-long-double is the default, since its ABI specifies that long double is aligned on a 16-byte boundary. Heads up: neither of these options enables any extra precision over the x87 standard of 80 bits for a long double.
Warning: if you override the default value for your target ABI, this changes the size of structures and arrays containing long double variables, and also modifies the function calling convention for functions taking long double. Hence they aren't binary-compatible with code compiled without that switch ๐Ÿซฃ
  • -mlong-double-64 โ€” controls the size of the long double type ๐ŸŽ€ A size of 64 bits makes long double equivalent to double. This is the default for the 32-bit Bionic C library.
  • -mlong-double-80 โ€” part of the same family of switches that set the size of long double, alongside the two around it ๐Ÿ“
  • -mlong-double-128 โ€” a size of 128 bits makes long double equivalent to __float128. This is the default for the 64-bit Bionic C library ๐Ÿ’œ
Warning: if you override the default value for your target ABI, this changes the size of structures and arrays containing long double variables, and also modifies the function calling convention for functions taking long double. Hence they aren't binary-compatible with code compiled without that switch ๐Ÿซฃ
  • -malign-data=type โ€” controls how GCC aligns variables ๐Ÿงฉ The supported values for type are:
    • compat โ€” uses an increased alignment value that's compatible with GCC 4.8 and earlier. This is the default.
    • abi โ€” uses the alignment value specified by the psABI.
    • cacheline โ€” uses an increased alignment value to match the cache line size.
  • -mlarge-data-threshold=threshold โ€” when -mcmodel=medium or -mcmodel=large is specified, data objects bigger than threshold get placed in large data sections ๐Ÿ“ฆ The default is 65535.
  • -mrtd โ€” switches to a different function-calling convention ๐Ÿ”„ In it, functions that take a fixed number of arguments return with the ret num instruction, which pops their arguments as they return. That saves one instruction in the caller, since there's no need to pop the arguments there. You can make an individual function use this calling sequence with the function attribute stdcall, and you can override the -mrtd option for a function with the function attribute cdecl.
Warning: 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 ๐Ÿ™…โ€โ™€๏ธ 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.) ๐Ÿ’€
  • -mregparm=num โ€” controls how many registers get used to pass integer arguments ๐ŸŽฏ By default, no registers are used to pass 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. That includes the system libraries and startup modules ๐Ÿงฑ
  • -mvect8-ret-in-mem โ€” returns 8-byte vectors in memory instead of MMX registers ๐Ÿซง This is the default on VxWorks, to match the ABI of the Sun Studio compilers until version 12. Only use this option if you need to stay compatible with existing code produced by those earlier compiler versions or older versions of GCC ๐Ÿ•ฐ๏ธ
  • -mpc32, -mpc64, -mpc80 โ€” okay so these bestie options set the 80387 floating-point precision to 32, 64 or 80 bits ๐Ÿ’…. With -mpc32, the significands of floating-point results get rounded to 24 bits (single precision) โœจ. -mpc64 rounds them to 53 bits (double precision) ๐ŸŽ€. -mpc80 rounds them to 64 bits (extended double precision) and it's the default, so it's giving classic ๐Ÿ‘‘. Heads up: once you use one of these, higher precisions aren't available to you as the programmer unless you set the FPU control word explicitly ๐Ÿ™ˆ.

Dropping the rounding below the default 80 bits can speed some programs up by 2% or more ๐Ÿ”ฅ. Ngl though, some math libraries assume extended-precision (80-bit) operations are on by default. Routines in those libraries can lose a lot of accuracy, usually through so-called "catastrophic cancellation" ๐Ÿ’€, if you set the precision to less than extended.

  • -mdaz-ftz, -mno-daz-ftz โ€” the FTZ (flush-to-zero) and DAZ (denormals-are-zero) flags in the MXCSR register are what control floating-point calculations here ๐Ÿฆ‹. Normally, -Ofast, -ffast-math, or -funsafe-math-optimizations link in startup code that sets these flags, except when you're building a shared library (-shared) ๐ŸŒˆ. Use -mdaz-ftz or -mno-daz-ftz to explicitly turn that setting on or off, no matter what other options GCC gets ๐Ÿ’–.
  • -mstackrealign โ€” realigns the stack at entry ๐Ÿงš. On x86, this generates an alternate prologue and epilogue that realigns the run-time stack when needed. It's how you mix legacy code that keeps 4-byte stack alignment with modern code that keeps 16-byte alignment for SSE compatibility ๐Ÿซถ. See also the attribute "force_align_arg_pointer", which works on individual functions.
  • -mstack-arg-probe โ€” emits stack probing code in the function prologue ๐ŸŒธ.
  • -mpreferred-stack-boundary=num โ€” tries to keep the stack boundary aligned to a 2 raised to num byte boundary ๐Ÿ“. If you don't specify this, the default is 4 (16 bytes, or 128 bits) ๐Ÿ’œ.
โš ๏ธ Bestie, this one is dangerous. 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. The x86-64 ABI requires 16-byte stack alignment, so this is ABI incompatible ๐Ÿ˜ญ. It's only meant for a controlled environment where stack space is a seriously limited resource. It produces wrong code when functions compiled with 16-byte stack alignment (like functions from a standard library) get called with a misaligned stack, and then SSE instructions can cause misaligned memory access traps ๐Ÿ’€. Variable arguments are also handled incorrectly for 16-byte aligned objects (including x87 long double and __int128), which gives wrong results. You have to build all modules with -mpreferred-stack-boundary=3, including any libraries, and that covers 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 it, the one set 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 you'll take big run-time performance hits ๐Ÿข. On Pentium III, the SSE data type __m128 may not work properly if it isn't 16-byte aligned ๐Ÿฅบ.

To make sure these values are aligned on the stack, the stack boundary has to be at least as aligned as whatever any value on the stack requires. Every function also has to be generated so it keeps the stack aligned ๐Ÿงฉ. So calling a function compiled with a higher preferred stack boundary from one compiled with a lower one will most likely misalign the stack ๐Ÿ˜ฌ. Libraries that use callbacks should always stick with the default setting, as recommended.

This extra alignment eats up stack space and usually bumps up code size ๐Ÿ“ฆ. If your code is picky about stack space, like embedded systems and operating system kernels, you might want to drop the preferred alignment to -mpreferred-stack-boundary=2 ๐Ÿ“.

  • -mdump-tune-features โ€” tells GCC to dump the names of the x86 performance tuning features and their default settings ๐Ÿ“œ. You can use those names in -mtune-ctrl=feature-list.
  • -mtune-ctrl=feature-list โ€” fine-grain control of x86 code generation features ๐ŸŽ›๏ธ. feature-list is a comma-separated list of feature names. When you specify one, it gets turned on unless it's preceded by ^, in which case it gets turned off. See also -mdump-tune-features.
๐Ÿšจ This one is intended for GCC developers. Using it may lead to code paths that aren't covered by testing, and it can potentially cause compiler ICEs or runtime errors.
  • -mno-default โ€” turns off all tunable features ๐Ÿงน. See also -mtune-ctrl=feature-list and -mdump-tune-features.
  • -mcld โ€” makes GCC emit a "cld" instruction in the prologue of functions that use string instructions ๐ŸŒ€. String instructions depend on the DF flag to pick between autoincrement and autodecrement mode. The ABI says DF should be cleared on function entry, but some operating systems don't clear it in their exception dispatchers, so the handler can run with DF set and string instructions go 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 โ€” makes GCC emit a "vzeroupper" instruction before control flow leaves the function ๐Ÿงผ. This cuts down the AVX-to-SSE transition penalty and removes unnecessary "zeroupper" intrinsics.
  • -mstv, -mno-stv โ€” enable or disable the Scalar to Vectorization pass, which turns 64-bit integer computation into vector ones ๐Ÿ’ซ. This optimization is restricted to -O2 and higher.
  • -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 an opt-bit vector width in instructions instead of the default for the selected platform ๐Ÿ“.
  • -mpartial-vector-fp-math, -mno-partial-vector-fp-math โ€” lets GCC generate floating-point operations that might affect the floating-point status flags on partial vectors, where the vector elements live in the low part of the 128-bit SSE register ๐Ÿง . Unless you specify -fno-trapping-math, the compiler guarantees correct behavior 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 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 fix that 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.
    • none โ€” no extra limitations on GCC beyond whatever the selected platform defines ๐Ÿคทโ€โ™€๏ธ.
    • 128 โ€” prefers a 128-bit vector width for instructions ๐Ÿ’œ.
    • 256 โ€” prefers a 256-bit vector width for instructions ๐Ÿ’™.
    • 512 โ€” prefers a 512-bit vector width for instructions ๐Ÿฉต.
  • -mnoreturn-no-callee-saved-registers โ€” optimizes functions with the "noreturn" attribute or the _Noreturn specifier by not saving the callee-saved registers they use in the function prologue (except for the "BP" register) ๐Ÿช„. This can mess with debugging the caller of the "noreturn" function, or any function further up the call stack, so it's 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 handy 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, from before the Pentium 4 G1 step in December 2005, lacked 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 โ€” enables the shadow stack built-in functions from x86 Control-flow Enforcement Technology (CET) ๐Ÿ›ก๏ธ.
  • -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 โ€” okay so this one turns on the RCPSS and RSQRTSS instructions (plus their vectorized buddies RCPPS and RSQRTPS) and adds an extra Newton-Raphson step for better precision. It swaps them in for DIVSS and SQRTSS (and their vectorized versions) when you're working with single-precision floating-point arguments. These only get generated when -funsafe-math-optimizations is on together with -ffinite-math-only and -fno-trapping-math. Heads up: 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 ๐Ÿ’…

GCC already turns 1.0f/sqrtf(x) into RSQRTSS (or RSQRTPS) with -ffast-math (or that same option combo), so you don't need -mrecip for that โœจ

Also, GCC emits the sequence with the extra Newton-Raphson step for vectorized single-float division and vectorized sqrtf(x) already with -ffast-math (or that same option combo). Again, no -mrecip needed ๐Ÿซถ

  • -mrecip=opt โ€” this one lets you pick exactly which reciprocal estimate instructions are allowed ๐ŸŽ€ The opt part is a comma-separated list, and you can put a ! in front of any option to invert it.
    • all โ€” enables every estimate instruction ๐Ÿ”ฅ
    • default โ€” enables the default instructions, same as -mrecip ๐ŸŒธ
    • none โ€” disables all estimate instructions, same as -mno-recip ๐Ÿ™ˆ
    • div โ€” enables the approximation for scalar division
    • vec-div โ€” enables the approximation for vectorized division
    • sqrt โ€” enables the approximation for scalar square root
    • vec-sqrt โ€” enables the approximation for vectorized square root

So, for example, -mrecip=all,!sqrt turns on all the reciprocal approximations except square root. It's giving main character energy for picking and choosing ๐Ÿ‘‘

  • -mveclibabi=type โ€” this sets the ABI type for vectorizing intrinsics with an external library. type can be svml (the Intel short vector math library), aocl (the LibM math library from AMD Optimizing CPU Libraries, AOCL), or acml (the end-of-life AMD core math library, which AOCL-LibM succeeded) ๐Ÿงš To use it, you need both -ftree-vectorize and -funsafe-math-optimizations enabled, and you have to specify an SVML or ACML ABI-compatible library at link time. Otherwise it's a no-go ๐Ÿ˜ญ

GCC currently emits calls to these functions for svml:

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, vmlsAcos4

For aocl, it's these corresponding function types:

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,
amd_vrs16_tanhf

And for acml, it's these:

__vrd2_sin, __vrd2_cos, __vrd2_exp, __vrd2_log, __vrd2_log2, __vrd2_log10,
__vrs4_sinf, __vrs4_cosf, __vrs4_expf, __vrs4_logf, __vrs4_log2f,
__vrs4_log10f, __vrs4_powf
  • -mabi=name โ€” picks the calling convention to generate code for ๐ŸŽ€ The valid values are sysv (the ABI used on GNU/Linux and other systems) and ms (the Microsoft ABI). By default you get the Microsoft ABI when targeting Microsoft Windows and the SysV ABI everywhere else. You can override this per function with the ms_abi and sysv_abi function attributes ๐Ÿ’–
  • -masm=dialect โ€” chooses the assembly dialect for output ๐Ÿ“ It also decides which dialect is used for basic asm and extended asm. The choices are att or intel, and the default is att. Fun fact (well, a limitation): Darwin doesn't support intel ๐Ÿ’€
  • -mforce-indirect-call โ€” forces all function calls to be indirect โœจ This is handy with Intel Processor Trace, since it gives you more precise timing info for function calls ๐Ÿฆ‹
  • -mmanual-endbr โ€” only inserts the ENDBR instruction at function entry when you use the cf_check function attribute ๐Ÿซถ It's useful together with -fcf-protection=branch if you want to control ENDBR insertion at the function entry yourself.
  • -mcet-switch โ€” by default, CET instrumentation is off for switch statements that use a jump table, and indirect branch track is disabled ๐Ÿ™ˆ Since jump tables live in read-only memory, that's not a direct loss of hardening. BUT if the jump table index is attacker-controlled, the indirect jump might not be constrained by CET. This option turns CET instrumentation on 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 of differences in 64-bit ABIs, 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 for saving and restoring those registers is emitted inline, which makes the prologues and epilogues pretty lengthy. With -mcall-ms2sysv-xlogues, the prologues and epilogues use stubs in the static portion of libgcc for 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, so be careful ๐Ÿ˜ญ
  • -mpush-args, -mno-push-args โ€” uses PUSH operations to store outgoing parameters ๐ŸŒธ This method is shorter and usually just as fast as the SUB/MOV approach, and it's enabled by default. In some cases, disabling it can improve performance thanks to better scheduling and fewer dependencies.
  • -maccumulate-outgoing-args โ€” if enabled, the maximum amount of space needed for outgoing arguments gets computed in the function prologue ๐Ÿฆ‹ This is faster on most modern CPUs because of reduced dependencies, improved scheduling, and reduced stack usage when the preferred stack boundary is not equal to 2. The tradeoff is a notable increase in code size. This switch implies -mno-push-args ๐Ÿ“

  • -mms-bitfields, -mno-ms-bitfields โ€” enables or disables bit-field layout that's compatible with the native Microsoft Windows compiler ๐Ÿ’œ
Okay, here's the tea: 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 you're moving packed data between GCC-compiled functions and the native Microsoft compiler (whether through a function call or as data in a file), you might need to access either format ๐Ÿ‘€

This option is enabled by default for Microsoft Windows targets. You can also control this behavior locally with variable or type attributes. For more info, check out x86 Attributes โœจ

The Microsoft structure layout algorithm is pretty simple, except for the bit-field packing. The padding and alignment of structure members, and whether a bit-field can straddle a storage-unit boundary, are determined by 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 its members. Every object is allocated an offset so that:
offset % alignment_requirement == 0
  1. Adjacent bit-fields are packed into the same 1-, 2-, or 4-byte allocation unit if the integral types are the same size, and if 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 are normally 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. okay so bestie, if a zero-length bit-field gets slotted in right after a bit-field called foo, and its alignment is bigger than the member right after it (bar), then bar gets aligned as the type of that 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 lands at offset 2 instead of offset 1, so the size of t2 is 4 โœจ. For t3, the zero-length bit-field doesn't mess with the alignment of bar, so the size of the struct doesn't change ๐Ÿ’–

Taking this into account, here's what you need to know ๐Ÿ‘‘

  1. If a zero-length bit-field follows a normal bit-field, its type can affect the alignment of the whole structure. Like, t2 is 4 bytes big because the zero-length bit-field comes after a normal bit-field and is type short ๐Ÿ’…
  2. Even if a zero-length bit-field isn't followed by a normal bit-field, it can still change the alignment of the structure ๐Ÿ˜ญ
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 get ignored, no cap ๐Ÿ™ˆ
struct
 {
   char foo;
   long : 0;
   char bar;
 } t5;

Here, t5 takes up 2 bytes ๐Ÿฆ‹


  • -mno-align-stringops โ€” Skips aligning the destination of inlined string operations ๐Ÿซถ. This makes your code smaller and can speed things up when the destination is already aligned, but GCC doesn't know that, so it's kind of on you ๐Ÿ’€
  • -minline-all-stringops โ€” By default GCC only inlines string operations when it knows the destination is aligned to at least a 4-byte boundary. This option makes it inline way more, which bumps up code size but can make short memcpy and memset calls faster ๐Ÿ”ฅ. It also turns on inline expansion of strlen for all pointer alignments โœจ
  • -minline-stringops-dynamically โ€” For string operations where the size isn't known, it adds run-time checks: inline code for small blocks, and a library call for big ones ๐Ÿงš
  • -mstringop-strategy=alg โ€” Overrides GCC's internal heuristic for picking which algorithm gets used to inline string operations. The allowed values for alg are:
    • rep_byte, rep_4byte, rep_8byte โ€” Expands using the i386 rep prefix at the specified size ๐ŸŒˆ
    • byte_loop, loop, unrolled_loop โ€” Expands into an inline loop ๐Ÿ”
    • libcall โ€” Always uses a library call ๐Ÿ“ž
  • -mmemcpy-strategy=strategy โ€” Overrides the internal heuristic that decides whether __builtin_memcpy gets inlined, and which inline algorithm to use when the expected size of the copy is known ๐Ÿ‘€. The 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 inline algorithm is allowed to handle. The last triplet must have a max_size of -1, and 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 โ€” Basically the same vibe as -mmemcpy-strategy=, except this one controls the __builtin_memset expansion ๐Ÿ“
  • -momit-leaf-frame-pointer โ€” Doesn't keep the frame pointer in a register for leaf functions. That skips the instructions that save, set up, and restore frame pointers, and frees up an extra register in leaf functions ๐Ÿ™Œ. Heads up though: it removes the frame pointer for leaf functions, which 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 your operating system, and on whether it maps the segment to cover the entire 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 a VEX prefix. The -mavx option turns this on by default โœจ
  • -mfentry, -mno-fentry โ€” If profiling is active (-pg), this puts the profiling counter call before the prologue. Note: On x86 architectures, 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), this generates a section containing pointers to each profiling call, which is super handy for patching those calls automatically ๐Ÿฉน. You can use the mfentry-section= option to set the name of that section. It defaults to __mcount_loc ๐ŸŽ€
  • -mnop-mcount, -mno-nop-mcount โ€” If profiling is active (-pg), this generates the calls to the profiling functions as NOPs. That's useful when they should get patched in later on, dynamically. This is likely only useful together with -mrecord-mcount ๐Ÿฆ‹
  • -minstrument-return=type โ€” With -pg -mfentry, this instruments function exits according to type, which can be one of: none (don't instrument), call (generate a call to "__return__"), or nop5 (generate a 5-byte nop sequence) ๐ŸŒธ. This only instruments true returns that end with a "ret", not sibling calls made via a jump ๐Ÿ™…โ€โ™€๏ธ
  • -mrecord-return, -mno-record-return โ€” Generates a "__return_loc" section that points to all 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, but only when no variable arguments are passed in vector registers ๐Ÿง 
  • -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 divisor are in the range 0 to 255, it uses 8-bit unsigned integer divide instead of 32-bit/64-bit integer divide ๐Ÿ’…
  • -mavx256-split-unaligned-load, -mavx256-split-unaligned-store โ€” Splits 32-byte AVX unaligned loads and stores into smaller pieces โœ‚๏ธ
  • -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, which is the default) ๐Ÿ›ก๏ธ. These only do something if -fstack-protector or -fstack-protector-all is specified ๐Ÿ™ˆ

With the latter choice (tls), -mstack-protector-guard-reg=reg and -mstack-protector-guard-offset=offset get to 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 are whatever the relevant ABI says ๐Ÿ“–

-mstack-protector-guard-symbol=symbol overrides the offset with a symbol reference to a canary in the TLS block ๐Ÿ”‘

  • -mgeneral-regs-only โ€” Generates 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 super 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 to a call and return thunk. thunk-inline converts them to an inlined call and return thunk. thunk-extern converts them to an external call and return thunk, provided in a separate object file ๐Ÿ“ฆ. You can control this behavior for a specific function using the function attribute "indirect_branch" ๐ŸŽฏ
Heads up: -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 ๐Ÿšง

Note: -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 a call and return thunk. thunk-inline converts them to an inlined call and return thunk. thunk-extern converts them to an external call and return thunk, provided in a separate object file ๐Ÿ“ฆ. You can control this behavior for a specific function using the function attribute "function_return" ๐ŸŽฏ

Note: -mindirect-return=thunk-extern is compatible with -fcf-protection=branch, since the external thunk can be made to enable control-flow check ๐Ÿซถ

Heads up: -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 โ€” okay bestie, this one's a whole security glow-up ๐Ÿ›ก๏ธ It makes GCC generate code that guards against straight line speculation (SLS), with the level set by choice. The default is none, which turns off all SLS hardening. return covers function returns, indirect-jmp covers indirect jumps, and all turns on every SLS hardening option ๐Ÿ”ฅ
  • -mindirect-branch-cs-prefix โ€” this one is giving very specific main character energy โœจ It adds a CS prefix to call and jmp instructions that go to an indirect thunk, where the branch target is in the r8-r15 registers. That makes each call or jmp instruction 6 bytes long, so at run time they can potentially be swapped out for lfence; call *%r8-r15 or lfence; jmp *%r8-r15 ๐Ÿฆ‹
  • -mapx-inline-asm-use-gpr32 โ€” so by default, EGPR usage is turned off in inline asm constraints ๐Ÿ™ˆ The reason is that GCC has no way of knowing whether the asm instructions can handle GP32 or not. If your inline asm can handle EGPR, you add -mapx-inline-asm-use-gpr32 and you're good to go ๐Ÿ’…
  • -mgather, -mscatter โ€” these two are a matching set ๐ŸŒˆ They enable vectorization for gather and scatter instructions, respectively.

These -m switches are supported on top of everything above for x86-64 processors in 64-bit environments ๐ŸŒธ

  • -m32, -m64, -mx32, -m16 โ€” okay this is the environment squad ๐Ÿ’– They generate code for a 16-bit, 32-bit or 64-bit environment.
    • -m32 sets int, long, and pointer types to 32 bits, and generates code that runs in 32-bit mode.
    • -m64 sets int to 32 bits, sets long and pointer types to 64 bits, and generates code for the x86-64 architecture. For Darwin, only -m64 also turns off the -fno-pic and -mdynamic-no-pic options ๐Ÿ“
    • -mx32 sets int, long, and pointer types to 32 bits, and generates code for the x86-64 architecture โœจ
    • -m16 is basically -m32, except it also outputs the .code16gcc assembly directive at the very start of the assembly output, so the binary can run in 16-bit mode ๐Ÿงš
  • -miamcu โ€” this one generates code that conforms to the Intel MCU psABI. It requires -m32 to be turned on ๐ŸŽ€
  • -mno-red-zone โ€” okay so the x86-64 ABI says there's a so-called "red zone" ๐Ÿงš It's a 128-byte area beyond the stack pointer that signal or interrupt handlers don't touch, so you can stash temporary data there without adjusting the stack pointer. -mno-red-zone turns that red zone off ๐Ÿ™…โ€โ™€๏ธ
  • -mcmodel=small โ€” the default, and it's the chill one ๐ŸŒธ It generates code for the small code model, where 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.
  • -mcmodel=kernel โ€” this one lives in the negative 2 GB of the address space, and it has to be used for Linux kernel code ๐Ÿ‘‘
  • -mcmodel=medium โ€” the program gets linked in the lower 2 GB, and small symbols go there too. Symbols bigger than -mlarge-data-threshold get put into large data or BSS sections, and those can sit above 2GB. Programs can be statically or dynamically linked ๐Ÿฆ‹
  • -mcmodel=large โ€” this model makes no assumptions at all about addresses and sizes of sections ๐ŸŒˆ
  • -maddress-mode=long โ€” generates code for long address mode. It's 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. It's only supported for 32-bit and x32 environments, and it's the default address mode for 32-bit and x32 environments ๐Ÿซถ
  • -mneeded, -mno-needed โ€” these emit the GNU_PROPERTY_X86_ISA_1_NEEDED GNU property for the Linux target, which tells you the micro-architecture ISA level needed to run the binary โญ
  • -mno-direct-extern-access โ€” okay so without -fpic or -fPIC, this one always uses the GOT pointer to reach external symbols. With -fpic or -fPIC, it treats access to protected symbols as local symbols. The default is -mdirect-extern-access ๐Ÿ’…
โš ๏ธ Warning: 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, bestie ๐Ÿ˜ญ
  • -munroll-only-small-loops, -mno-unroll-only-small-loops โ€” this is conservative small loop unrolling ๐ŸŒธ It's enabled by default with -O2, and it unrolls loops with fewer than 4 instructions by 1 time. That gives better utilization of the instruction decoding pipeline on modern processors. You can turn it off with -mno-unroll-only-small-loops, and it's also disabled if the more general options -funroll-loops or -funroll-all-loops are either enabled or explicitly disabled ๐Ÿ“
  • -mdispatch-scheduler โ€” enables instruction scheduling โœจ It's only supported on bdver1, bdver2, bdver3, bdver4, and znver1 processors, and it additionally requires -fschedule-insns -fsched-pressure ๐Ÿ”ฅ
  • -mlam=choice โ€” LAM (linear-address masking) lets special bits in the pointer be used for metadata ๐Ÿงš The default is none. With u48, pointer bits in positions 62:48 can be used for metadata. With u57, pointer bits in positions 62:57 can be used for metadata ๐Ÿ’–

๐ŸŒธ x86 Windows Options ๐ŸŒธ


๐ŸŒต Xstormy16 Options ๐ŸŒต

These options are defined for Xstormy16 ๐Ÿฆ‹

  • -msim โ€” chooses startup files and a linker script that are suitable for 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 ๐Ÿ’… When enabled, "CONST16" instructions are always used in place of the standard "L32R" instructions. The use of "CONST16" is enabled by default only if the "L32R" instruction is not available ๐Ÿ™ˆ
  • -mserialize-volatile, -mno-serialize-volatile โ€” when this one is enabled, GCC inserts "MEMW" instructions before "volatile" memory references to guarantee sequential consistency ๐Ÿซถ The default is -mserialize-volatile. Use -mno-serialize-volatile to leave out the "MEMW" instructions ๐Ÿ’€
  • -mforce-no-pic, -mno-force-no-pic โ€” for targets like GNU/Linux, where all user-mode Xtensa code has to be position-independent code (PIC), this option turns PIC off when compiling kernel code ๐Ÿ‘‘
  • -mtext-section-literals, -mno-text-section-literals โ€” these control the treatment of literal pools ๐ŸŽ€ The default is -mno-text-section-literals, which puts literals in a separate section in the output file. That lets the literal pool sit in data RAM/ROM, and it also lets the linker combine literal pools from separate object files to remove redundant literals and improve code size. With -mtext-section-literals, the literals are interspersed in the text section so they stay as close as possible to their references. This may be necessary for large assembly files. Literals for each function are placed right before that function ๐ŸŒธ
  • -mauto-litpools, -mno-auto-litpools โ€” these also control the treatment of literal pools ๐Ÿฆ‹ The default is -mno-auto-litpools, which puts literals in a separate section in the output file unless -mtext-section-literals is used. With -mauto-litpools, the assembler interspersses the literals in the text section. The compiler doesn't produce explicit ".literal" directives, and it loads literals into registers with "MOVI" instructions instead of "L32R", so the assembler can do relaxation and place literals as necessary. This option lets the assembler create several literal pools per function and assemble very big functions, which may not be possible with -mtext-section-literals ๐Ÿงš
  • -mtarget-align, -mno-target-align โ€” when enabled, GCC tells the assembler to automatically align instructions to reduce branch penalties, at the cost of some code density ๐ŸŒˆ The assembler tries to widen density instructions to align branch targets and the instructions following call instructions. If there aren't enough preceding safe density instructions to align a target, no widening happens. The default is -mtarget-align. These options don't affect auto-aligned instructions like "LOOP", which the assembler always aligns, either by widening density instructions or by inserting NOP instructions ๐Ÿ’–
  • -mlongcalls, -mno-longcalls โ€” when enabled, GCC tells the assembler to translate direct calls into indirect calls, unless it can tell that the target of a direct call is in the range the call instruction allows ๐Ÿ˜ญ This usually happens for calls to functions in other source files. Specifically, the assembler turns a direct "CALL" instruction into an "L32R" followed by a "CALLX" instruction. The default is -mno-longcalls. Use this option in programs where the call target might be out of range ๐Ÿซถ It's implemented in the assembler, not the compiler, so the assembly code GCC generates still shows direct call instructions. Look at the disassembled object code to see the actual instructions ๐Ÿ‘€ The assembler uses an indirect call for every cross-file call, not just the ones that really are out of range ๐Ÿ™ƒ
  • -mabi=name โ€” generates code for the specified ABI ๐Ÿ’… Permissible values are call0 and windowed. The default ABI is chosen by the Xtensa core configuration.
    • -mabi=call0 โ€” function parameters are 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 version is enabled, the C preprocessor symbol "__XTENSA_CALL0_ABI__" is defined โœจ
    • -mabi=windowed โ€” function parameters are passed in registers "a10" through "a15", and the called function rotates the register window by 8 registers on entry, so its arguments are found in registers "a2" through "a7" ๐Ÿงš Register "a7" may be used as a frame pointer. The register window is rotated 8 registers back upon return. When this ABI version is enabled, the C preprocessor symbol "__XTENSA_WINDOWED_ABI__" is defined ๐Ÿ‘‘
  • -mextra-l32r-costs=n โ€” okay so this one lets you dial in an extra cost for instruction RAM/ROM access on "L32R" instructions, measured in clock cycles โฑ๏ธ ๐ŸŒธ When you're optimizing for speed, it decides whether a constant gets loaded from the literal pool with "L32R" or built from a small one using a couple of arithmetic instructions ๐Ÿงฎ The default is 0.
  • -mstrict-align, -mno-strict-align โ€” these control whether GCC avoids (-mstrict-align) or allows (-mno-strict-align) memory accesses that might not line up on a natural object boundary, as the architecture spec describes ๐Ÿ™ˆ The default is -mno-strict-align for cores that handle both unaligned loads and stores in hardware, and -mstrict-align for every other core ๐Ÿ’–

โœจ zSeries Options โœจ

These are listed under


โœจ ENVIRONMENT โœจ

okay so this section is all about environment variables that change how GCC behaves ๐Ÿ’… Some of them point GCC at directories or prefixes to search when it's hunting for files, and others set up other aspects of the compilation environment ๐ŸŒˆ

Also, you can name search spots directly with options like -B, -I and -L ๐Ÿ—บ๏ธ Those take precedence over environment variables, and environment variables take precedence over whatever the GCC configuration says. Main character rules ๐Ÿ‘‘

โœจ LANG โœจ

โœจ LC_CTYPE โœจ

โœจ LC_MESSAGES โœจ

โœจ LC_ALL โœจ

These variables control how GCC uses localization info so it can work with different national conventions ๐ŸŒ GCC looks at the LC_CTYPE and LC_MESSAGES locale categories if it's been configured to do so. You can set these categories to any value your installation supports. A typical value is en_GB.UTF-8, which is English for the United Kingdom encoded in UTF-8 ๐Ÿ‡ฌ๐Ÿ‡ง

  • LC_CTYPE โ€” this one handles character classification ๐Ÿ”ค GCC uses it to figure out where character boundaries fall in a string. That matters for some multibyte encodings that have quote and escape characters which would otherwise be read as the end of a string or as an escape ๐Ÿ˜ญ
  • LC_MESSAGES โ€” this one picks the language used in diagnostic messages ๐Ÿ’ฌ
  • LC_ALL โ€” if this is set, it overrides LC_CTYPE and LC_MESSAGES. If it isn't, then LC_CTYPE and LC_MESSAGES default to the value of LANG โœจ If none of these variables are set, GCC defaults to traditional C English behavior ๐ŸŽ€

โœจ TMPDIR โœจ

If TMPDIR is set, it names the directory GCC uses for temporary files ๐Ÿ“ GCC makes temp files to hold the output of one stage of compilation that gets fed into the next stage. For example, the preprocessor's output is the input to the compiler proper ๐Ÿงš

โœจ GCC_DIAGNOSTICS_LOG โœจ

If GCC_DIAGNOSTICS_LOG is set, extra info about the diagnostics subsystem gets emitted ๐Ÿ” If it's set to an empty value, that info goes to stderr. Otherwise, GCC tries to open the file you named and writes the info there ๐Ÿ“

The exact content and format of this info can change at any time. It's meant for GCC developers rather than end-users, so don't be surprised if it looks like it's for the insiders ๐Ÿ™ˆ

โœจ GCC_COMPARE_DEBUG โœจ

Setting GCC_COMPARE_DEBUG is nearly the same as passing -fcompare-debug to the compiler driver ๐Ÿชž Check that option's documentation for more details ๐Ÿ“–

โœจ GCC_EXEC_PREFIX โœจ

If GCC_EXEC_PREFIX is set, it names a prefix to use in the names of the subprograms GCC runs ๐Ÿงฉ No slash gets added when the prefix is combined with a subprogram name, but you can end your prefix with a slash if you want one ๐Ÿฆ‹

If GCC_EXEC_PREFIX isn't set, GCC tries to figure out a good prefix based on the pathname it was invoked with ๐Ÿ•ต๏ธโ€โ™€๏ธ

If GCC can't find the subprogram using your prefix, it checks 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 lots of cases, prefix is the value of "prefix" you used when you ran the configure script โš™๏ธ

Prefixes you give with -B take precedence over this one ๐Ÿ‘‘

This prefix is also used for finding files like crt0.o that get used for linking ๐Ÿ”—

It also gets used in a kind of unusual way when GCC hunts for header directories ๐ŸŽฏ For each standard directory whose name normally starts with /usr/local/lib/gcc (or, more precisely, with the value of GCC_INCLUDE_DIR), GCC tries swapping that beginning out for your 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 starts with the configured prefix, then the value of prefix gets 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, very much like PATH ๐Ÿ›ค๏ธ GCC tries these directories when searching for subprograms, but only if it can't find them using GCC_EXEC_PREFIX first ๐Ÿซถ

โœจ LIBRARY_PATH โœจ

The value of LIBRARY_PATH is also a colon-separated list of directories, just 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. Just remember that directories given with -L come first ๐Ÿฅ‡

โœจ LANG โœจ

This variable passes locale info to the compiler ๐ŸŒ One thing it's used for is deciding which character set to use when character literals, string literals and comments get parsed in C and C++. When the compiler is configured to allow multibyte characters, these values for LANG are recognized:

  • C-JIS โ€” recognizes JIS characters ๐Ÿ‡ฏ๐Ÿ‡ต
  • C-SJIS โ€” recognizes SJIS characters ๐Ÿฃ
  • C-EUCJP โ€” recognizes EUCJP characters ๐ŸŽŒ

If LANG isn't defined, or it has some other value, then the compiler uses "mblen" and "mbtowc" as defined by the default locale to recognize and translate multibyte characters ๐ŸŒ

โœจ GCC_EXTRA_DIAGNOSTIC_OUTPUT โœจ

If GCC_EXTRA_DIAGNOSTIC_OUTPUT is set to one of the following values, extra text gets emitted 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, equivalent to -fdiagnostics-parseable-fixits ๐Ÿงพ In particular, columns are expressed as a count of bytes, starting at byte 1 for the initial column ๐Ÿ”ข
  • fixits-v2 โ€” same as fixits-v1, except columns are expressed as display columns, per -fdiagnostics-column-unit=display ๐Ÿ“

โœจ EXPERIMENTAL_SARIF_SOCKET โœจ

If EXPERIMENTAL_SARIF_SOCKET is set in the environment, the compiler will try to connect to a UNIX domain stream socket with that name ๐Ÿ”Œ It then sends an "OnSarifResult" JSON-RPC 2.0 notification to it for each diagnostic that occurs, where the value of the notification is a SARIF "result" object ๐Ÿ“จ

The compiler will fail immediately if EXPERIMENTAL_SARIF_SOCKET is set and it can't connect to it ๐Ÿ’€

This feature is experimental and subject to change or removal without notice. Don't build your whole life on it yet ๐Ÿ™ƒ

Some additional environment variables affect the behavior of the preprocessor ๐Ÿงผ

โœจ 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, to look in for header files ๐Ÿ“‚ The special character, "PATH_SEPARATOR", depends on the target and is determined when GCC is built. For Microsoft Windows-based targets it's a semicolon, and for almost all other targets it's a colon โšก

CPATH specifies a list of directories to be searched as if you'd given them with -I, but after any paths given with -I options on the command line ๐Ÿ—‚๏ธ This environment variable is 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 you'd used -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 show up at the beginning or the 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 the compiler processes ๐Ÿ› ๏ธ System header files get 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 take 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 ๐Ÿ•ฐ๏ธ That way 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-16/README.Bugs> ๐Ÿ›๐Ÿซถ

โœจ FOOTNOTES โœจ

  1. okay bestie, this one's a lil spicy ๐ŸŒถ๏ธ On some systems, gcc -shared needs to build supplementary stub code for constructors to work ๐Ÿงš On multi-libbed systems, gcc -shared has to select the correct support libraries to link against. Skip the correct flags and you can get subtle defects ๐Ÿ’€ but supplying them when they aren't needed is totally innocuous ๐Ÿซถ Also, -shared suppresses the addition of startup code that alters the floating-point environment, which happens with -ffast-math, -Ofast or -funsafe-math-optimizations on some targets โœจ

โœจ SEE ALSO โœจ

Go check these out, they're giving everything ๐Ÿ“š

โœจ AUTHOR โœจ

the contributors are the real MVPs ๐Ÿ‘‘ Check the Info entry for gcc, or peep <https://gcc.gnu.org/onlinedocs/gcc/Contributors.html> to see everyone who helped build GCC ๐ŸŒˆ

Copyright (c) 1988-2026 Free Software Foundation, Inc. ๐Ÿ’œ

Permission is granted 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; with the Invariant Sections being "GNU General Public License" and "Funding Free Software", the Front-Cover texts being (a) (see below), and with the Back-Cover Texts being (b) (see below). A copy of the license is included in the gfdl(7) man page ๐Ÿ“œ

  • (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 a wrap, bestie ๐Ÿซถ This page is from gcc-16, dated 2026-03-22. Now go be a compiler girlie ๐Ÿ’…โœจ

๐Ÿ“ฆ where this page comes from