Turn LLVM 18 Bitcode into Readable IR with llvm-dis
llvm-dis-18 turns an opaque LLVM bitcode file into human-readable LLVM IR, no surprise file landing somewhere you did not choose. You will pick the output destination deliberately and learn to tell a missing file from a genuinely bad input. The examples use llvm-dis-18 from Ubuntu package llvm-18, version 1:18.1.3-1ubuntu1.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell, an installed LLVM 18 toolchain, and a bitcode file you are allowed to read. This command only disassembles bitcode: it does not execute the module, optimise it, or turn it into native machine code. No example here needs sudo.
1. Check the installed tool
Start by confirming that the command on your path is the one you expect. This is an ordinary read-only check:
$ command -v llvm-dis-18
/usr/bin/llvm-dis-18
$ llvm-dis-18 --version
Ubuntu LLVM version 18.1.3
Optimized build.
$ dpkg-query -W -f='${Package} ${Version}\n' llvm-18
llvm-18 1:18.1.3-1ubuntu1
The binary is named llvm-dis-18 on this installation. Keep the versioned name in scripts whenever you need LLVM 18 specifically, rather than silently picking up a different llvm-dis from another toolchain.
Checkpoint
If command -v prints nothing, stop and install or enable the appropriate LLVM package through your normal system-management process. Do not work around a missing executable by downloading an unrelated binary into a project directory.
2. Disassemble a bitcode file to the default .ll file
Give the input path as the final argument:
$ llvm-dis-18 /path/to/module.bc
For a regular file, the default destination is derived from the input name: an existing .bc suffix is removed and .ll is added, so the command above writes /path/to/module.ll. The result is LLVM IR text, not a native assembly listing.
Warning
That default can be inconvenient in a source tree, and it can silently replace a file already sitting at the destination. Check before running a command that writes beside valuable work:
$ test ! -e /path/to/module.ll && echo 'destination is free'
$ llvm-dis-18 /path/to/module.bc
$ test -s /path/to/module.ll && echo 'LLVM IR written'
Recovery
Do not use > to redirect over a useful .ll file unless that replacement is intentional. If you have already overwritten an output and still need the previous version, recover it from version control or your backup system: there is no undo operation inside llvm-dis-18 itself.
3. Choose an output path explicitly
Use -o when the destination matters:
$ llvm-dis-18 /path/to/module.bc -o /tmp/module-inspection.ll
$ test -s /tmp/module-inspection.ll && echo 'inspection file is non-empty'
inspection file is non-empty
The output argument belongs to llvm-dis-18, not to the bitcode module. A path such as /tmp/module-inspection.ll suits a temporary investigation because it keeps generated text out of the source directory. Do not drop sensitive IR into a shared temporary directory without thinking about its permissions and lifetime.
If you want the text on standard output instead, set the output filename to a single hyphen:
$ llvm-dis-18 /path/to/module.bc -o - | less
This is a read-only inspection pipeline from the shell's point of view: llvm-dis-18 creates no output file at all. The displayed module may contain source paths, symbol names or other build details, so treat captured output as potentially sensitive.
4. Read bitcode from standard input
Omit the input filename, or pass -, when another command supplies the bitcode. Send the result to standard output if you do not want a file:
$ cat /path/to/module.bc | llvm-dis-18 -o - | less
Keep this distinction straight: a named input defaults to a derived .ll file, while standard input defaults to standard output. Making both sides explicit, as above, keeps an unexpected file from appearing in the current directory.
For a quick machine-checkable probe, look for a known symbol in the resulting text with a tool such as grep:
$ llvm-dis-18 -o - < /path/to/module.bc | grep -F 'define i32 @answer'
define i32 @answer() {
The exact line depends on the module. A missing match means that symbol is not present; it is not proof that disassembly failed. Check the exit status of llvm-dis-18 separately when a pipeline is part of automation.
5. Use help for the installed option set
LLVM tools expose more options than the short manpage bothers to describe. Ask this exact binary when you need the local interface:
$ llvm-dis-18 -help
OVERVIEW: llvm .bc -> .ll disassembler
USAGE: llvm-dis-18 [options] [input bitcode]...
The options that matter for this workflow are -o for the output filename, -help for a command summary, and -f to enable binary output on terminals. Leave -f alone for normal IR inspection: it is a terminal-safety override for raw bitcode output, not a way to make a source listing clearer.
6. Diagnose a failed run
A successful disassembly exits with status 0. An error exits non-zero:
$ llvm-dis-18 /path/to/missing.bc -o - >/tmp/llvm-dis.stdout 2>/tmp/llvm-dis.stderr
$ printf 'status: %s\n' "$?"
status: 1
$ sed -n '1p' /tmp/llvm-dis.stderr
error: /path/to/missing.bc: No such file or directory
Check the path and read permission before changing anything:
$ ls -l /path/to/module.bc
$ test -r /path/to/module.bc && echo readable
If the file exists but is not valid LLVM bitcode, leave the original untouched and capture the diagnostic in a separate file. Do not rename a native executable, object file or arbitrary archive to .bc: the suffix does not convert its contents. If a producer created textual LLVM IR instead, use the matching assembler such as llvm-as-18 to make bitcode first, then run llvm-dis-18 on that.
Done means
- Version confirmed. You checked that
llvm-dis-18is LLVM 18.1.3 from the installedllvm-18package. - Default destination understood. You can disassemble a named
.bcfile and predict its default.lldestination. - Output chosen deliberately. You use
-owhen an explicit file or standard output is safer. - Stdin/stdout rule known. You know that omitted input reads standard input and
-o -writes standard output. - Failures handled properly. You check the exit status and preserve the original bitcode while diagnosing errors.