Home / Alt manpages / llc-20(1)

  • llc-20(1)
  • User command
  • linux

Compile LLVM IR to Assembly or an Object File with llc-20

A forum copy of an llc-20 command usually works once, then breaks the moment the target changes underneath you. This guide builds a version you can trust because every target and format choice is spelled out. Examples use llc-20 from the installed llvm-20 package, version 20.1.8 on Ubuntu. Allow about fifteen minutes.

You need a shell and LLVM 20's command-line tools; no root privileges, since these commands only read input and write files you own. It starts with textual LLVM IR, then shows the same interface for bitcode and cross-target output. It does not link an executable: a native assembler and linker are separate stages.

1. Check the installed compiler

Confirm the binary and version before you trust a flag to behave the way you expect. This is a normal, read-only command:

$ command -v llc-20
/usr/bin/llc-20
$ llc-20 --version
Ubuntu LLVM version 20.1.8
Optimized build.
Default target: x86_64-pc-linux-gnu

The registered-target list is longer than this snippet and depends on how LLVM was built. The default target here is the host's x86-64 Linux target. Don't assume a target available in another LLVM installation is available in this one.

Checkpoint

If command -v finds a different executable on your machine, treat its own version and help output as the authority for the rest of the session, not this guide.

2. Compile a small IR module to assembly

llc-20 accepts LLVM assembly text in a .ll file, bitcode in a .bc file, or input on standard input. This harmless module defines one function that returns 42, and piping it avoids creating a source file just for a test:

$ printf '%s\n' '; ModuleID = "demo"' 'define i32 @answer() {' 'entry:' '  ret i32 42' '}' | llc-20 -o /tmp/llc-20-demo.s
$ sed -n '1,18p' /tmp/llc-20-demo.s
        .file   "<stdin>"
        .text
        .globl  answer
answer:
        movl    $42, %eax
        retq

Surrounding directives and comments can vary, but a host-targeted result should always contain a public answer function returning 42. The output file is ordinary text assembly. Skip sudo here: it only risks leaving a root-owned file in a directory you normally edit as yourself.

3. Make the output filename and format explicit

Use -o whenever a later command will consume the result. With a named input, omitting -o makes llc-20 strip a trailing .bc and add .s; with standard input, omitted output goes to standard output. Naming it explicitly stops that default from becoming a distraction in a build script.

To produce a native relocatable object instead of textual assembly, add -filetype=obj:

$ printf '%s\n' '; ModuleID = "demo"' 'define i32 @answer() {' 'entry:' '  ret i32 42' '}' | llc-20 -filetype=obj -o /tmp/llc-20-demo.o
$ file /tmp/llc-20-demo.o
/tmp/llc-20-demo.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

Supported file types are target-dependent. The manpage lists asm, obj and null: null emits nothing at all and is only useful for performance measurement, never as linker input. A successful run exits 0; anything else means the compilation didn't finish.

4. Set a target instead of trusting the host

For a portable build, choose the target triple with -mtriple, which overrides whatever's encoded in the IR. You can also set an architecture with -march, or a particular CPU with -mcpu. These choices affect instructions and ABI assumptions, so don't pick them purely to silence an error message.

$ printf '%s\n' '; ModuleID = "demo"' 'define i32 @answer() {' 'entry:' '  ret i32 42' '}' | llc-20 -mtriple=x86_64-pc-linux-gnu -o /tmp/llc-20-demo-triple.s
$ head -n 8 /tmp/llc-20-demo-triple.s
        .file   "<stdin>"
        .text
        .globl  answer
        .p2align        4
        .type   answer,@function
answer:
        .cfi_startproc

Ask this installation which architectures it knows about:

$ llc-20 -help | grep -E -- '--(march|mtriple|mcpu)'
  --march=<string>       - Architecture to generate code for
  --mcpu=<cpu-name>      - Target a specific cpu type (-mcpu=help for details)
  --mtriple=<string>     - Override target triple for module

For CPU-specific choices, use the target's own help query rather than guessing a name. The documented probe is:

$ llvm-as-20 < /dev/null | llc-20 -march=xyz -mcpu=help

Replace xyz with a real architecture. If that target isn't built in, the probe prints an error, which is useful evidence rather than something to work around.

5. Change x86 assembly syntax only when required

On x86, the default assembly syntax is AT&T. If the next tool or a reviewer needs Intel syntax, pass --x86-asm-syntax=intel:

$ printf '%s\n' '; ModuleID = "demo"' 'define i32 @answer() {' 'entry:' '  ret i32 42' '}' | llc-20 -mtriple=x86_64-pc-linux-gnu --x86-asm-syntax=intel -o /tmp/llc-20-demo-intel.s
$ grep -E 'intel_syntax|mov' /tmp/llc-20-demo-intel.s
        .intel_syntax noprefix
        mov     eax, 42

This option is x86-specific: it does not convert an object file, and it will not make assembly portable between architectures. Keep the setting beside the assembler invocation in a build definition so a later reader can see why the syntax differs.

6. Keep optimisation and unsafe float choices deliberate

-O=uint selects levels matching Clang's -O0 through -O3. Start at -O0 when comparing generated code or debugging a pipeline, then pick a level that matches the surrounding build:

$ printf '%s\n' '; ModuleID = "demo"' 'define i32 @answer() {' 'entry:' '  ret i32 42' '}' | llc-20 -O=2 -o /tmp/llc-20-demo-O2.s
$ test -s /tmp/llc-20-demo-O2.s && printf '%s\n' 'assembly written'
assembly written

The floating-point options that enable unsafe assumptions, or assume NaN and infinity never appear, change what the generated code is allowed to assume about your inputs. Don't add them as generic performance switches: they can change results for inputs that break those assumptions. Check llc-20 -help for their exact names, and test any target-specific CPU or attribute choice on the actual hardware and ABI it will run on.

Common traps

Use -help for the complete option list and keep diagnostics separate from the output file. A frequent error is passing a format or target supported by a different LLVM build; check the local help and --version again before changing the command.

Warning

If a command wrote a partial result before failing, treat that file as unusable and remove only the named temporary file after checking its path:

$ rm -- /tmp/llc-20-demo.s

That is the only destructive command in this guide. Don't substitute a project path or use a wildcard. Files in /tmp are disposable test outputs; source, object and build directories need their own review and recovery plan before you touch them.

Done means

  • You confirmed the installed llc-20 version and default target.
  • You generated assembly from LLVM IR and checked the resulting function.
  • You generated an object file with -filetype=obj and verified its type.
  • You know when -mtriple, -march and -mcpu change the result.
  • Intel syntax or optimisation were chosen only for a stated downstream need.
  • You can remove a failed temporary output without touching source or build state.