Home / Alt manpages / llc-20(1)

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

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

You will finish with a repeatable command for turning LLVM IR into target assembly or a relocatable object file, while keeping the target and output format explicit. The 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 are needed: these commands only read input and write files you own. This guide 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 relying on a flag. 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 exact registered-target list is longer and depends on how LLVM was built. The default target shown here is the host's x86-64 Linux target. Do not assume that a target available in another LLVM installation is available in this one.

Checkpoint

If command -v finds a different executable, use its version and help output as the authority for the rest of the session.

2. Compile a small IR module to assembly

llc-20 accepts LLVM assembly text in a .ll file, LLVM bitcode in a .bc file, or input on standard input. This harmless module defines one function that returns 42. Piping it avoids creating a source file just for the 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

The surrounding directives and comments can vary, but this host-targeted result should contain a public answer function and a return of 42. The output file is ordinary text assembly. There is no need for sudo; using it would risk leaving a root-owned file in a directory you normally edit.

3. Make the output filename and format explicit

Use -o whenever a later command will consume the result. With an input filename, omitting -o makes llc-20 remove a trailing .bc and add .s. With standard input, omitted output goes to standard output. Explicit names prevent that default from becoming a distraction in a build script.

To produce a native relocatable object instead of textual assembly, select -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 describes asm, obj and null; null emits nothing and is useful for performance measurements, not for a linker input. A successful exit status is zero. A non-zero status means that the compilation did not complete successfully.

4. Set a target instead of relying on the host

For portable builds, choose the target triple with -mtriple. It overrides the triple encoded in the IR. You can also choose an architecture with -march, or a particular CPU with -mcpu. These choices affect the instructions and ABI assumptions, so do not select them merely to silence an error.

$ 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 help query rather than guessing a CPU name. The documented probe is:

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

Replace xyz with a real architecture. The probe may print an error if that target is not built in, which is useful evidence rather than a problem 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 reviewer requires 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 does 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 floating-point choices deliberate

The -O=uint option selects levels corresponding to Clang's -O0 through -O3. Start with -O0 when comparing generated code or debugging a pipeline, then choose 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 that NaN and infinity values are absent, change the assumptions made about input values. Do not add them as generic performance switches: they can change results for inputs that violate those assumptions. Use llc-20 -help to inspect their exact names. Likewise, target-specific CPU and attribute choices should be tested on the hardware and ABI where the output will run.

7. Diagnose failures without changing the system

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

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

This is the only destructive command in the guide. Do not 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.

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.
  • You selected Intel syntax or optimisation only for a stated downstream need.
  • You can remove a failed temporary output without touching source or build state.