Generating Assembly Yourself
Produce your own listings with g++ -S and objdump, explore Compiler Explorer, and use the in-editor assembly panel to inspect any code you write on this site.
Making Your Own Listings
So far, every assembly listing in this course has been handed to you. That was deliberate: you needed the vocabulary before the tools. But the real skill you are building is the ability to take any piece of C++ that interests you, generate its assembly, and read it. The examples in this course are a starting set, not the whole game. Once you can produce listings yourself, every function you ever write becomes something you can inspect, and every "I wonder what the compiler did with that" becomes a question you can answer in under a minute.
This lesson covers the four ways you will do that: asking the compiler directly with -S, disassembling a finished binary with objdump, using Compiler Explorer in the browser, and using the assembly view built into this site's editor.
Asking the Compiler: g++ -S
You already know the compilation pipeline: preprocess, compile, assemble, link. The compile stage is the one that produces assembly, and the -S flag tells the compiler to stop right there and write the assembly to a file instead of continuing on to machine code:
g++ -S square.cpp
This produces square.s, a plain text file you can open in any editor. No executable is created; the pipeline stops before the assembler runs.
Run exactly that command, though, and the output will fight you in two ways. First, GNU tools emit AT&T syntax by default, and you learned last lesson that this course reads Intel. Second, the file is cluttered with bookkeeping directives, lines starting with a dot, like .cfi_startproc and .cfi_def_cfa_offset, that exist for exception handling and debuggers, not for you. A better incantation is:
g++ -S -O0 -masm=intel -fno-asynchronous-unwind-tables square.cpp
Three flags do the cleanup:
-masm=intelswitches the output to Intel syntax.-fno-asynchronous-unwind-tablessuppresses most of the.cfi_directive noise, leaving mostly real instructions.-O0or-O2chooses the optimization level.-O0means "do not optimize", producing verbose but literal code that maps line-by-line onto your source.-O2enables the full standard optimizer, producing the short, rearranged code that actually ships.
Get in the habit of always stating the optimization level explicitly. It changes the output more than any other flag, and comparing the -O0 and -O2 listings of the same function is one of the most instructive exercises there is.
If you use clang++ instead of g++, everything above works identically; Clang accepts the same flags and produces a .s file the same way.
Disassembling a Binary: objdump -d
-S asks the compiler for assembly on the way down. Sometimes you need to go the other direction: you have a compiled program or library and want to see what is inside it. That is disassembly, turning machine code back into assembly, and the standard tool is objdump:
g++ -O2 -o square square.cpp
objdump -d -M intel square
The -d flag disassembles the executable sections, and -M intel selects Intel syntax. The output shows each instruction alongside the actual machine-code bytes it was encoded as, plus the address where it lives.
When would you reach for objdump instead of -S?
- You do not have the source. A third-party library or an already-built program can only be disassembled.
- You want the final, linked result. The
.sfile from-Sis pre-linking;objdumpshows the program as it will truly execute, with real addresses and any link-time changes applied. - You are matching a crash address. Debuggers and crash reports give you addresses, and
objdumpoutput includes them.
The trade-off is that disassembly loses information. Variable names are gone, most labels are gone, and there is no color-coded connection back to your source. For studying your own code, -S or the tools below are friendlier.
Compiler Explorer
Compiler Explorer, at godbolt.org, is a free website with C++ source on the left and the generated assembly on the right, updating live as you type. It is, without much competition, the tool professionals reach for first when they want to see what a compiler does.
Three things make it exceptional:
- Color-coded mapping. Each line of your C++ is highlighted in a color, and the assembly it produced is highlighted in the same color. Hover over a statement and you instantly see which instructions it became. This mapping is the single fastest way to learn to read assembly.
- Every compiler, every version, every flag. A dropdown offers GCC, Clang, MSVC, and dozens of others, across many versions and architectures. A flags box takes anything you would pass on the command line, so typing
-O2there is all it takes to compare optimization levels. It shows Intel syntax by default and filters out directive noise for you. - Shareable links. Any snippet-plus-compiler-plus-flags setup can be turned into a short URL. This is why performance discussions online are full of godbolt links: they are reproducible evidence, not screenshots.
When you see an assembly listing in a blog post or a conference talk, odds are it came from Compiler Explorer.
The Assembly Panel in This Editor
You do not need to leave this site to practice any of this. Every exercise on the platform has a live assembly panel beside the code editor, which shows the x86-64 assembly for the code you have written, in Intel syntax, just like the listings in these lessons. It compiles when the page loads and again after every run, and the -O0 and -O2 buttons in its header switch optimization level. Clicking a line of assembly moves the editor cursor to the C++ line it came from, and moving the cursor in the editor highlights the instructions that line produced.
The workflow the course exercises are built around is simple:
- Write your C++ in the editor.
- Run it and confirm it behaves the way you expect.
- Read the assembly panel and work out what the compiler produced.
Step 3 is where the learning happens. Change something small, a type, an initializer, a loop bound, and watch what moves in the assembly. From this chapter onward, the exercises will ask you to do exactly that.
A Worked Example: -O0 vs -O2
Here is the whole round trip on one tiny function:
int square(int x)
{
return x * x;
}
Compile it without optimization:
g++ -S -O0 -masm=intel -fno-asynchronous-unwind-tables square.cpp
The interesting part of square.s looks like this:
square(int):
push rbp
mov rbp, rsp
mov DWORD PTR -4[rbp], edi
mov eax, DWORD PTR -4[rbp]
imul eax, DWORD PTR -4[rbp]
pop rbp
ret
Seven instructions. At -O0 the compiler is completely literal: it sets up bookkeeping, stores the parameter into memory on the stack, reads it back (twice) to multiply, then tears the bookkeeping down. Nothing is clever, which is exactly why -O0 output is the easiest to match against source code.
Now the same command with -O2:
square(int):
imul edi, edi
mov eax, edi
ret
Three instructions. The optimizer threw away the bookkeeping and the trips to memory, multiplied the argument register by itself, and moved the result into the return register. Same function, same behavior, less than half the code. Being able to produce this comparison yourself, for any function you care about, is the skill this lesson exists to give you.
Key Takeaways
- Generating your own listings turns every piece of C++ you write into practice material; do not wait for a course to hand you assembly.
g++ -Sstops the pipeline after the compile stage and writes a.sassembly file; add-masm=intelfor Intel syntax and-fno-asynchronous-unwind-tablesto cut directive noise.clang++works the same way.- Always state the optimization level:
-O0gives literal, source-shaped code;-O2gives the optimized code that really ships. objdump -d -M inteldisassembles a compiled binary; use it when you have no source or need the final linked program, at the cost of names and source mapping.- Compiler Explorer (godbolt.org) shows live, color-coded source-to-assembly mapping across compilers and flags, and is the tool most professionals use.
- This site's editor has a live assembly panel giving an x86-64 view of your exercise code: write, run, then read the assembly.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Generating Assembly Yourself - Quiz
Test your understanding of the lesson.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!