Home / Alt manpages / llvm-diff-20(1)

  • llvm-diff-20(1)
  • User command
  • linux

Compare LLVM IR Modules with llvm-diff

llvm-diff-20 checks whether two LLVM IR modules still match in structure. You can narrow that check to selected global names once a whole-module report gets too noisy to read. The examples use the installed Ubuntu LLVM 20.1.8 build and temporary text IR files. Allow about fifteen minutes if you already have the two modules; the comparison itself never modifies either input.

1. Check the installed tool

Use the versioned command supplied by the llvm-20 package. This matters when several LLVM releases are installed side by side, because a module and its diagnostic output can be tied to a particular toolchain:

$ command -v llvm-diff-20
/usr/bin/llvm-diff-20
$ llvm-diff-20 --version
Ubuntu LLVM version 20.1.8
  Optimized build.
$ dpkg-query -W -f='${Package} ${Version}\n' llvm-20
llvm-20 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139

The local manual describes llvm-diff as a debugging tool for LLVM pass and frontend writers. It is a structural comparison, not a general textual diff, and its output format is not stable enough to parse as an interface.

2. Confirm the input convention

The command takes two module paths, followed optionally by global names:

$ llvm-diff-20 [options] <first-file> <second-file> [global-name ...]
  • .ll means LLVM assembly. Any other suffix is read as bitcode. Do not rename a bitcode file to .ll just to make it look familiar.
  • Keep text IR consistent. If you have hand-written or generated text IR, match the suffix to the syntax.
  • No diff-style modes. The installed build exposes help, version and a colour switch, but no context or unified diff flag.

3. Compare two complete modules

For a reproducible smoke test, create two small modules doing the same operation but with different local value names. Local names are insignificant to this comparison:

$ cat > /tmp/add-a.ll <<'EOF'
define i32 @add(i32 %a, i32 %b) {
entry:
  %sum = add i32 %a, %b
  ret i32 %sum
}
EOF
$ cat > /tmp/add-b.ll <<'EOF'
define i32 @add(i32 %x, i32 %y) {
entry:
  %result = add i32 %x, %y
  ret i32 %result
}
EOF
$ llvm-diff-20 /tmp/add-a.ll /tmp/add-b.ll
$ printf 'exit status: %s\n' "$?"
exit status: 0

No output and status 0 mean this comparison found no structural difference. The tool ignores presentation-only changes such as local value names and global ordering, which makes it useful right after a transformation that rewrites those details without touching the structure that matters.

4. Read a structural difference

Change the operation in the second module, then run the same comparison again, capturing both streams if the result is going into a test log:

$ llvm-diff-20 /tmp/add-a.ll /tmp/add-sub.ll
in function add:
  in block %entry:
    >   %sum = sub i32 %a, %b
    >   ret i32 %sum
    <   %sum = add i32 %a, %b
    <   ret i32 %sum
$ printf 'exit status: %s\n' "$?"
exit status: 1

The arrows tell you which side is which: > is the second module, < is the first. The report names the function and basic block before showing the differing instructions. A control-flow change can stop the matching downstream, so a long report does not necessarily mean every later instruction is independently different.

5. Limit the comparison to named globals

When you are investigating one function, add its global name after the two module paths, using the LLVM name without the leading @:

$ llvm-diff-20 /tmp/add-a.ll /tmp/module-with-other-functions.ll add

With a global-name list, only those values are compared. Without it, the tool considers every global value and can report a function present in only one module. Multiple names can be supplied as separate arguments:

$ llvm-diff-20 old.ll new.ll parse_token lower_case

Quote a name only when your shell would otherwise mangle characters in it, and never build the argument list by blindly expanding untrusted text.

6. Check status and diagnostics together

Do not use the exit code as your only evidence. The manual documents zero for no differences and a non-zero value for differences, but the installed 20.1.8 build can print a one-sided global diagnostic while still returning zero:

$ llvm-diff-20 /tmp/add-a.ll /tmp/module-with-extra-function.ll
function @extra exists only in right module
$ printf 'exit status: %s\n' "$?"
exit status: 0

Checkpoint

Treat this as a version-specific quirk of the packaged binary, and inspect captured output as well as status, especially when comparing whole modules. For an automated gate, decide explicitly whether one-sided globals count as an error and encode that policy in a wrapper. Do not parse the wording as a stable API: the manual warns that the output format can change.

7. Keep the comparison safe

llvm-diff-20 reads the modules and writes diagnostics. It does not rewrite IR, run the functions, load plugins, or touch a service, so ordinary user privileges are normally enough. Avoid sudo here: elevated access can mask an ownership or path problem instead of fixing one.

Warning

Keep the original modules when a result matters. The comparison does not prove memory behaviour, linkage, or function attributes are equal, and the manual lists several important differences it never diagnoses. If a result surprises you, inspect the IR with matching LLVM tools and compare the generated artefacts by a separate method. Remove only temporary files you created yourself, and never delete a source module as part of this check.

Done means

  • Version confirmed. You checked that /usr/bin/llvm-diff-20 is LLVM 20.1.8 from llvm-20.
  • Suffix rule understood. You matched .ll text input and non-.ll bitcode input to the tool's suffix rule.
  • Clean result recognised. No output with status 0 is the clean result for the tested comparison.
  • Report readable. You can read function, basic-block, and arrow markers in a structural report.
  • Scope narrowed when needed. You can restrict a noisy comparison to one or more global names.
  • Status and text both checked. Your check reviews diagnostics as well as exit status, and treats neither as a stable format.