# gcc ## What is GCC? **GCC** (the GNU Compiler Collection) is a compiler suite that turns C, C++, Fortran, and other source languages into machine code. It is the default compiler on most Linux distributions and the reference implementation that most other C/C++ compilers (Clang included) are measured against for language conformance. A compiler's job is to translate a high-level program into instructions the target CPU can execute, but GCC does much more than a literal translation. It performs optimisation passes that reorder, eliminate, vectorise, and inline code, decisions that can change performance by an order of magnitude without changing program behaviour. Understanding what GCC does with your source, not just how to invoke it, is central to writing fast C/C++ code. In HPC work GCC matters twice over: as the compiler for the application code, and as the thing whose optimisation reports and generated assembly you read when a loop is slower than it should be. `-O3` is a starting point, not an answer. ## Install On Debian and Ubuntu: ```bash sudo apt install build-essential ``` `build-essential` pulls in `gcc`, `g++`, `make`, and the standard C library headers. On Fedora and RHEL: ```bash sudo dnf groupinstall "Development Tools" ``` To check the installed version and default target: ```bash gcc --version gcc -dumpmachine ``` ## Practice The basic invocation compiles and links in one step: ```bash gcc -o hello hello.c ``` Under the hood this runs four stages, preprocessing, compilation to assembly, assembly to object code, and linking, but `gcc` hides them behind a single command by default. Each stage can be inspected separately: ```bash gcc -E hello.c -o hello.i # preprocessed source gcc -S hello.c -o hello.s # assembly gcc -c hello.c -o hello.o # object file gcc hello.o -o hello # link ``` For a program split across multiple files, each `.c` file becomes its own object file and the linker combines them: ```bash gcc -c a.c -o a.o gcc -c b.c -o b.o gcc a.o b.o -o app ``` ## Concepts ### Optimisation levels `-O0` through `-O3` (and `-Ofast`, `-Os`) select a bundle of optimisation passes. `-O0` is the default and disables optimisation entirely, which is why debug builds compiled without any `-O` flag are useful for [[gdb]] but say nothing about real performance. ```bash gcc -O0 -g -o app app.c # debug build, unoptimised, symbols kept gcc -O3 -o app app.c # optimised build ``` `-O2` enables most optimisations that don't risk increasing code size significantly; `-O3` adds aggressive loop and vectorisation transforms on top. `-Ofast` additionally relaxes strict IEEE floating-point semantics, which can break numerically sensitive code that depends on exact rounding behaviour. ### Vectorisation and target flags By default GCC compiles for a conservative baseline instruction set so the binary runs on any machine of the target architecture. To let GCC use the SIMD instructions actually available on the build machine, pass `-march=native`: ```bash gcc -O3 -march=native -o app app.c ``` `-march=native` is unsafe to redistribute (the binary may use instructions the deployment machine lacks) but is the right choice when building and running on the same node, which is the common case in HPC benchmarking. `-fopt-info-vec` reports which loops were successfully vectorised and, more usefully, which ones were not and why: ```bash gcc -O3 -march=native -fopt-info-vec-missed -c hot_loop.c ``` ### Warnings as a correctness tool `-Wall -Wextra` catch a large class of bugs (uninitialised variables, signed/unsigned comparison mismatches, unused results) before they become runtime failures. `-Wall` is not "all warnings" despite the name; it's a curated subset considered low false-positive. ```bash gcc -Wall -Wextra -Wpedantic -o app app.c ``` `-Wpedantic` additionally flags non-standard GNU extensions, useful when portability to another compiler matters. ### Generated assembly Reading what GCC actually emitted is the ground truth when a "should be fast" loop isn't. `-S` combined with `-fverbose-asm` annotates the assembly with the corresponding source variable names: ```bash gcc -O3 -march=native -S -fverbose-asm hot_loop.c -o hot_loop.s ``` Cross-referencing this with a [[perf]] annotate session on the compiled binary is the standard loop to close when chasing a specific instruction-level performance problem.