Getting LLVM Bitcode from Julia Script

I’m new to Julia and I’m trying to get the LLVM IR or bitcode file of a Julia script. Two ways are mentioned in documents:

1- export JULIA_LLVM_ARGS = -print-after-all` dumps IR after each pass.

2- Passing --outout-unopt-bc unopt.bc option to system image build process, which will output the unoptimized IR to the unopt.bc file.

I want the finalized IR and getting IR after each pass is not useful for me. For the second option, as I understand, previous Julia versions accept --output-unopt-bc and --outpu-bc options but version 1.0 does not.

Is there any straightforward way to get the LLVM bitcode file?

Put it in a function and use @code_llvm

You might want to enable the dump_module option, eg. @code_llvm dump_module=true 1+1, if you want IR that’s compatible with the rest of LLVM tooling.

1 Like

Is it possible to use the @code_llvm in a script or it works only for the interactive mode of Julia?

I tried it in the interactive mode and it does not print the IR of all functions which are called in the main function.

Thanks but still it does not print the whole code’s IR, it only prints that function’s IR in another way.

Use the code_llvm function with the io argument set to a IOBuffer.

That’s not true.

julia> code_llvm(+, Tuple{Int, Int})

; Function +
; Location: int.jl:53
define i64 @"julia_+_33814"(i64, i64) {
top:
  %2 = add i64 %1, %0
  ret i64 %2
}

julia> code_llvm(+, Tuple{Int, Int}; dump_module=true)
; ModuleID = '+'
source_filename = "+"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13"
target triple = "x86_64-unknown-linux-gnu"

%jl_value_t = type opaque

; Function +
; Location: int.jl:53
define i64 @"julia_+_33814"(i64, i64) {
top:
  %2 = add i64 %1, %0
  ret i64 %2
}

define nonnull %jl_value_t addrspace(10)* @"jsys_+_33813"(%jl_value_t addrspace(10)*, %jl_value_t addrspace(10)**, i32) #0 {
top:
  %3 = getelementptr inbounds %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %1, i64 1
  %4 = bitcast %jl_value_t addrspace(10)** %3 to i64 addrspace(10)**
  %5 = load i64 addrspace(10)*, i64 addrspace(10)** %4, align 8
  %6 = addrspacecast i64 addrspace(10)* %5 to i64 addrspace(11)*
  %7 = load i64, i64 addrspace(11)* %6, align 8
  %8 = getelementptr inbounds %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %1, i64 2
  %9 = bitcast %jl_value_t addrspace(10)** %8 to i64 addrspace(10)**
  %10 = load i64 addrspace(10)*, i64 addrspace(10)** %9, align 8
  %11 = addrspacecast i64 addrspace(10)* %10 to i64 addrspace(11)*
  %12 = load i64, i64 addrspace(11)* %11, align 8
  %13 = call i64 @"julia_+_33814"(i64 %7, i64 %12)
  %14 = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %13)
  ret %jl_value_t addrspace(10)* %14
}

declare %jl_value_t addrspace(10)* @jl_box_int64(i64 signext)

attributes #0 = { "thunk" }

!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}

!0 = !{i32 2, !"Dwarf Version", i32 4}
!1 = !{i32 1, !"Debug Info Version", i32 3}
!2 = distinct !DICompileUnit(language: DW_LANG_C89, file: !3, producer: "julia", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4)
!3 = !DIFile(filename: "int.jl", directory: ".")
!4 = !{}
2 Likes