CodeDiffs

Compare different types of code and display it in the terminal. For cleaner results, syntax highlighting is separated from the difference calculation.

Supports:

  • native CPU assembly (output of @code_native, highlighted by InteractiveUtils.print_native)
  • LLVM IR (output of @code_llvm, highlighted by InteractiveUtils.print_llvm)
  • Typed Julia IR (output of @code_typed, highlighted through the Base.show method of Core.CodeInfo)
  • Julia AST (an Expr), highlighting is done with OhMyREPL.jl's Julia syntax highlighting in Markdown code blocks
  • GPU typed Julia IR / LLVM IR / native assembly (see GPU Extensions)

CodeDiffs.jl exports two macros:

  • @code_for will display the code for a function call. The output is cleaned and highlighted to maximize clarity.
  • @code_diff will compare the code of two function calls.

Both support all code types. If possible, the code type will be detected automatically, otherwise add e.g. type=:llvm for LLVM IR comparison:

julia> f1(a) = a + 1f1 (generic function with 1 method)
julia> @code_diff type=:llvm debuginfo=:none color=false f1(Int64(1)) f1(Int8(1)); Function Signature: f1(Int64) ; Function Signature: f1(Int8) define i64 @f1(i64 signext %"a::Int64define i64 @f1(i8 signext %"a::Int8")… top: ┃ top: %0 = add i64 %"a::Int64", 1 ┫ ┣ %0 = sext i8 %"a::Int8" to i64 ┣ %1 = add nsw i64 %0, 1 ret i64 %0 ret i64 %1 } ┃ }
julia> f2(a) = a - 1f2 (generic function with 1 method)
julia> @code_diff type=:llvm debuginfo=:none color=false f1(1) f2(1); Function Signature: f1(Int64) ; Function Signature: f2(Int64) define i64 @f1(i64 signext %"a::Int64…define i64 @f2(i64 signext %"a::Int64… top: ┃ top: %0 = add i64 %"a::Int64", 1 %0 = add i64 %"a::Int64", -1 ret i64 %0 ┃ ret i64 %0 } ┃ }

Setting the environment variable "CODE_DIFFS_LINE_NUMBERS" to true will display line numbers on each side:

julia> ENV["CODE_DIFFS_LINE_NUMBERS"] = truetrue
julia> @code_diff type=:llvm debuginfo=:none color=false f1(1) f2(1)1 ; Function Signature: f1(Int64) ; Function Signature: f2(Int64) 1 2 define i64 @f1(i64 signext %"a::Int…define i64 @f2(i64 signext %"a::Int…2 3 top: ┃ top: 3 4 %0 = add i64 %"a::Int64", 1 %0 = add i64 %"a::Int64", -1 4 5 ret i64 %0 ┃ ret i64 %0 5 6 } ┃ } 6

Main API

CodeDiffs.@code_forMacro
@code_for [type=:native] [color=true] [io=stdout] [cleanup=true] [dbinfo=true] [option=value...] f(...)
@code_for [option=value...] :(expr)

Display the code of f(...) to io for the given code type, or the expression expr (AST only).

options are passed to get_code. To display an Expr in a variable, use @code_for :($expr).

io defaults to stdout. If io == String, then the code is not printed and is simply returned.

If the type option is the first option, "type" can be omitted: i.e. @code_for :llvm f() is valid.

cleanup == true will use cleanup_code on the code.

dbinfo is a broader version of the debuginfo option of @code_typed and @code_llvm. dbinfo works for all code types. dbinfo=true is equivalent to debuginfo=:source (or :default) and dbinfo=false is debuginfo=:none. For code types with no option to control the verbosity of the output, dbinfo is silently ignored.

# Default display
@code_for type=:native f()

# Without debuginfo
@code_for type=:llvm debuginfo=:none f()

# Options sets can be passed from variables with the `extra` options
opts = (; debuginfo=:none, world=Base.get_world_counter())
@code_for type=:typed extra=opts f()

# Same as above, but shorter since we can omit "type"
@code_for :typed extra=opts f()
source
CodeDiffs.@code_diffMacro
@code_diff [type=:native] [color=true] [cleanup=true] [dbinfo=true] [option=value...] f₁(...) f₂(...)
@code_diff [option=value...] :(expr₁) :(expr₂)

Compare the methods called by the f₁(...) and f₂(...) or the expressions expr₁ and expr₂, then return a CodeDiff.

options are passed to get_code. Option names ending with _1 or _2 are passed to the call of get_code for f₁ and f₂ respectively. They can also be packed into extra_1 and extra_2.

To compare Expr in variables, use @code_diff :($a) :($b).

cleanup == true will use cleanup_code to make the codes more prone to comparisons (e.g. by renaming variables with names which change every time).

dbinfo is a broader version of the debuginfo option of @code_typed and @code_llvm. dbinfo works for all code types. dbinfo=true is equivalent to debuginfo=:source (or :default) and dbinfo=false is debuginfo=:none. For code types with no option to control the verbosity of the output, dbinfo is silently ignored.

# Default comparison
@code_diff type=:native f() g()

# No debuginfo for `f()` and `g()`
@code_diff type=:native debuginfo=:none f() g()

# No debuginfo for `f()`
@code_diff type=:native debuginfo_1=:none f() g()

# No debuginfo for `g()`
@code_diff type=:native debuginfo_2=:none f() g()

# Options can be passed from variables with `extra_1` and `extra_2`
opts = (; debuginfo=:none, world=Base.get_world_counter())
@code_diff type=:native extra_1=opts extra_2=opts f() g()

# `type` and `color` can also be made different in each side
@code_diff type_1=:native type_2=:llvm f() f()
source