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.show
method 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_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 + 1
f1 (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 - 1
f2 (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"] = true
true
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_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).
option
s 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
.
option
s 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()