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 byInteractiveUtils.print_native) - LLVM IR (output of
@code_llvm, highlighted byInteractiveUtils.print_llvm) - Typed Julia IR (output of
@code_typed, highlighted through theBase.showmethod ofCore.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_forwill display the code for a function call. The output is cleaned and highlighted to maximize clarity.@code_diffwill 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::Int64…⟪╋⟫define 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"] = truetruejulia> @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_for — Macro@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()CodeDiffs.@code_diff — Macro@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()