Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code structure overhaul #12

Merged
merged 3 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ 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
- (Julia ≥ v1.11) [JuliaSyntaxHighlighting.jl](https://github.com/JuliaLang/JuliaSyntaxHighlighting.jl)
- Julia AST (an `Expr`), highlighting is done with OhMyREPL.jl's Julia syntax highlighting in Markdown code blocks

The [`@code_diff`](@ref) macro is the main entry point. If possible, the code type will be
detected automatically, otherwise add e.g. `type=:llvm` for LLVM IR comparison:
Expand Down Expand Up @@ -56,29 +54,42 @@ julia> @code_diff type=:llvm debuginfo=:none color=false f1(1) f2(1)
5 } ┃ } 5
```

# Main functions
# Comparison entry points

```@docs
CodeDiff
compare_code_native
compare_code_llvm
compare_code_typed
compare_ast
code_diff(::Any, ::Any)
code_diff(::Val{:ast}, ::Any, ::Any)
@code_diff
code_diff
code_for_diff
CodeDiff
```

# Display functions
# Code fetching

```@docs
optimize_line_changes!
replace_llvm_module_name
side_by_side_diff
code_native
code_llvm
code_typed
code_ast
get_code
```

# Highlighting

```@docs
code_highlighter
```

# Internals
# Cleanup

```@docs
cleanup_code
replace_llvm_module_name
LLVM_MODULE_NAME_REGEX
```

# Diff display

```@docs
optimize_line_changes!
side_by_side_diff
```
5 changes: 4 additions & 1 deletion src/CodeDiffs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ using Markdown
using StringDistances
using WidthLimitedIO

export @code_diff
export @code_diff, code_diff

const ANSI_REGEX = r"(?>\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))+"
const OhMYREPL_PKG_ID = Base.PkgId(Base.UUID("5fb14364-9ced-5910-84b2-373655c76a03"), "OhMyREPL")
const Revise_PKG_ID = Base.PkgId(Base.UUID("295af30f-e4ad-537b-8983-00126c2a3abe"), "Revise")

include("CodeDiff.jl")
include("get_code.jl")
include("highlighting.jl")
include("cleanup.jl")
include("compare.jl")
include("display.jl")

Expand Down
78 changes: 78 additions & 0 deletions src/cleanup.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

"""
LLVM_MODULE_NAME_REGEX

Should match the LLVM module of any function which does not have any of `'",;-` or spaces
in it.

It is `'get_function_name'`, in `'julia/src/codegen.cpp'` which builds the function name
for the LLVM module used to get the function code. The regex is built to match any output
from that function.
Since the `'globalUniqueGeneratedNames'` counter (the number at the end of the module name)
is incremented at each call to `'get_function_name'`, and since `code_llvm` or `code_native`
forces a compilation, it should be guaranteed that the match with the highest number at
the end is the name of our function in `code`.
"""
const LLVM_MODULE_NAME_REGEX = r"(?>julia|japi3|japi1)_([^\"\s,;\-']*)_(\d+)"


"""
replace_llvm_module_name(code::AbstractString)

Remove in `code` the trailing numbers in the LLVM module names, e.g. `"julia_f_2007" => "f"`.
This allows to remove false differences when comparing raw code, since each call to
`code_native` (or `code_llvm`) triggers a new compilation using an unique LLVM module name,
therefore each consecutive call is different even though the actual code does not
change.

```jldoctest; setup = :(using InteractiveUtils; import CodeDiffs: replace_llvm_module_name)
julia> f() = 1
f (generic function with 1 method)

julia> buf = IOBuffer();

julia> code_native(buf, f, Tuple{}) # Equivalent to `@code_native f()`

julia> code₁ = String(take!(buf));

julia> code_native(buf, f, Tuple{})

julia> code₂ = String(take!(buf));

julia> code₁ == code₂ # Different LLVM module names...
false

julia> replace_llvm_module_name(code₁) == replace_llvm_module_name(code₂) # ...but same code
true
```
"""
replace_llvm_module_name(code::AbstractString) = replace(code, LLVM_MODULE_NAME_REGEX => s"\1")


"""
replace_llvm_module_name(code::AbstractString, function_name)

Replace only LLVM module names for `function_name`.
"""
function replace_llvm_module_name(code::AbstractString, function_name)
function_name = string(function_name)
if Sys.islinux() && startswith(function_name, '@')
# See 'get_function_name' in 'julia/src/codegen.cpp'
function_name = function_name[2:end]
end
func_re = Regex("(?>julia|japi3|japi1)_\\Q$(function_name)\\E_(\\d+)")
return replace(code, func_re => function_name)
end


"""
cleanup_code(::Val{code_type}, code)

Perform minor changes to `code` to improve readability and the quality of the differences.

Currently only [`replace_llvm_module_name`](@ref) is applied to `:native` and `:llvm` code.
"""
cleanup_code(_, c) = c

cleanup_code(::Val{:native}, c) = replace_llvm_module_name(c)
cleanup_code(::Val{:llvm}, c) = replace_llvm_module_name(c)
Loading
Loading