Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
24d2fea
Demangling LLVM names in PTX and SASS
Keluaa Dec 17, 2024
9fe46da
Cleanup external functions in PTX
Keluaa Dec 19, 2024
a3dfb10
Replace mangled names in PTX and SASS with readable ones
Keluaa Dec 19, 2024
a138ed4
Extract the KA's backend kernel params for CUDA
Keluaa Mar 28, 2025
d7d1475
Merge branch 'gpu_cleanup' of https://github.com/Keluaa/CodeDiffs.jl …
Keluaa Mar 28, 2025
5420f84
Added the `dbinfo` option. Improved docs.
Keluaa Dec 20, 2024
6e5b726
Use demumble instead of cxx-filt
Keluaa Jan 18, 2025
410fb95
Highlighting support for PTX and GCN with Pygments
Keluaa Mar 28, 2025
0bb6081
Demangle with`demumble` instead of `binutils`. Added `dbinfo` and `cl…
Keluaa Mar 29, 2025
0828740
CI for the lastest Julia version
Keluaa Apr 6, 2025
fd9ad4c
Cleanup and highlighting for inline LLVM IR in typed IR
Keluaa Apr 6, 2025
179ceb3
Detect inline LLVM from `Base.llvmcall` expressions
Keluaa Apr 6, 2025
d99e2cb
PTX: indent function calls, remove the "End function" comment
Keluaa Apr 6, 2025
f3a45c9
Test PTX highlighting in CI
Keluaa Apr 6, 2025
91940cf
Fixes for `InteractiveUtils` in 1.12
Keluaa Apr 6, 2025
677e846
Don't use `jldoctest` with explicit output
Keluaa Apr 6, 2025
cfa713f
Test on prereleases instead of nightly
Keluaa Apr 7, 2025
c0a09b5
`:cuda_stats` code type to get an overview of a kernel's ressources
Keluaa Apr 16, 2025
22007f6
PTX statistics for load and store instructions. Ignore variables decl…
Keluaa Apr 17, 2025
c66fc6e
Support `CodeInfo` cleanup when the function returns `Union{}`
Keluaa Apr 17, 2025
5254fe1
PTX: align predicates. Match more external functions. Remove loop com…
Keluaa Apr 18, 2025
b1c5bd8
Fix LLVM call cleaning in typed IR
Keluaa Apr 20, 2025
677bd78
Separate cleanup logic from extensions. Cleanup tests without GPU pac…
Keluaa Jun 5, 2025
c2a6c10
Code stats for PTX, SASS and GCN, without requiring the GPU packages.
Keluaa Jun 5, 2025
bd3386e
Update AMDGPU compat
Keluaa Jun 5, 2025
083f9c9
Remove files added too quickly
Keluaa Jun 6, 2025
458a919
Put tests outputs in a artifact
Keluaa Jun 6, 2025
39aaef6
Fix and update some tests. Tests CI artifacts adjustments.
Keluaa Jun 6, 2025
2919133
Fix PTX cleanup of 'begin' comments. Test GCN cleanup.
Keluaa Jun 6, 2025
f46a83f
Upload artifacts even if tests fail
Keluaa Jun 6, 2025
6f3336f
Docs fixes for code cleanup and stats
Keluaa Jun 6, 2025
c9703a8
Trying once more to make Ci artifacts work...
Keluaa Jun 6, 2025
6cf3432
Small doc fix
Keluaa Jun 6, 2025
54911e5
I HATE WINDOWS NEWLINES
Keluaa Jun 8, 2025
38f2bc6
Support for more flexible testing workflows with Revise.jl
Keluaa Jun 8, 2025
4cd331c
More cleanup fixes for PTX and GCN, more tests
Keluaa Jun 8, 2025
ce8d294
Tests for a PTX call with no params. Remove redundant comments.
Keluaa Jun 8, 2025
102cf84
SASS cleanup tests
Keluaa Jun 8, 2025
2c7864e
[GCN] Remove trailing spaces after instruction mnemonics
Keluaa Jun 8, 2025
6c3489e
[Julia-1.12] More lenient version checks to include prereleases
Keluaa Jun 9, 2025
7900268
[GCN] Simply align operands to the n-th column. Minor cleanup fixes.
Keluaa Jun 9, 2025
ecc670a
[SASS] Better regex to find the kernel name
Keluaa Jun 9, 2025
7fada94
[PTX] Minor stats fixes and tests
Keluaa Jun 9, 2025
408db42
[GCN] Stats fixes. Better GCN instruction regex. Tests.
Keluaa Jun 9, 2025
3192299
[Julia-1.12] World age fixes. Adapt to changes made for global module…
Keluaa Jun 9, 2025
5272321
[PTX] Oops I forgot the parentheses
Keluaa Jun 9, 2025
c88c691
[GCN] Extract stats from the 'AMDGPU.csdata' section. More tests.
Keluaa Jun 10, 2025
926be55
[SPIRV] Basic cleanup and demangling. Tests.
Keluaa Jun 24, 2025
9dac561
Removed CUDA.jl from the test dependencies
Keluaa Jun 24, 2025
566e77f
Revamp docs
Keluaa Jun 24, 2025
ee9574b
[PTX/GCN] Replace tabs into spaces such that `@code_for` and `@code_d…
Keluaa Jun 25, 2025
68a3e3f
[PTX] Remove demoting comments
Keluaa Jun 25, 2025
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
16 changes: 14 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,34 @@ jobs:
fail-fast: false
matrix:
version:
- '1.10'
- '1.9'
- 'nightly'
- '1'
- 'pre'
os:
- ubuntu-latest
arch:
- x64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- run: # For syntax highlighting of PTX and GCN
pip install pygments
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: actions/upload-artifact@v4
if: always()
with:
name: "tests-output-jl-${{ matrix.version }}-${{ matrix.os }}-${{ matrix.arch }}"
path: |
test/tests_output.txt
test/references_mismatch/
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
env:
Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
*.jl.*.cov
*.jl.cov
*.jl.mem
/Manifest.toml
/docs/Manifest.toml
/Manifest*.toml
/docs/Manifest*.toml
/docs/build/
/test/references_mismatch/
/test/tests_output.txt
11 changes: 7 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
StringDistances = "88034a9c-02f8-509d-84a9-84ec65e18404"
WidthLimitedIO = "b8c1c048-cf81-46c6-9da0-18c1d99e41f2"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
demumble_jll = "1e29f10c-031c-5a83-9565-69cddfc27673"

[weakdeps]
AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e"
Expand All @@ -21,13 +23,13 @@ oneAPI = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b"

[extensions]
CodeDiffsAMDGPU = "AMDGPU"
CodeDiffsCUDA = "CUDA"
CodeDiffsCUDA = ["CUDA", "KernelAbstractions"]
CodeDiffsKernelAbstractions = "KernelAbstractions"
CodeDiffsMetal = "Metal"
CodeDiffsOneAPI = "oneAPI"

[compat]
AMDGPU = "0.9"
AMDGPU = "1"
Aqua = "0.7"
CUDA = "4, 5"
CodeTracking = "1"
Expand All @@ -41,12 +43,13 @@ ReferenceTests = "0.10"
StringDistances = "0.11"
Test = "1"
WidthLimitedIO = "1"
YAML = "0.4"
demumble_jll = "1"
julia = "1.9"
oneAPI = "1.5"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
DeepDiffs = "ab62b9b5-e342-54a8-a765-a90f495de1a6"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
Expand All @@ -56,4 +59,4 @@ Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Aqua", "CUDA", "InteractiveUtils", "KernelAbstractions", "OhMyREPL", "ReferenceTests", "Revise", "Test"]
test = ["Aqua", "InteractiveUtils", "KernelAbstractions", "OhMyREPL", "ReferenceTests", "Revise", "Test"]
8 changes: 7 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ makedocs(;
),
pages=[
"Home" => "index.md",
"Extensions" => "extensions.md"
"GPU Extensions" => "extensions.md",
"API" => [
"Main API" => "code_disp_and_diff.md",
"Cleanup" => "cleanup.md",
"Code stats" => "stats.md",
"Defining a new extension" => "new_extension.md",
],
],
doctest = can_doctest
)
Expand Down
22 changes: 22 additions & 0 deletions docs/src/cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
```@meta
CurrentModule = CodeDiffs.Cleanup
```

# Cleanup

```@docs
cleanup_code
```

## LLVM and mangling

```@docs
replace_llvm_module_name
function_unique_gen_name_regex
global_var_unique_gen_name_regex
demangle
demangle_all
mangled_base_name
clean_function_name
cleanup_inline_llvmcall_modules
```
36 changes: 36 additions & 0 deletions docs/src/code_disp_and_diff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
```@meta
CurrentModule = CodeDiffs
```

# Comparison entry points

```@docs
@code_diff
code_diff
code_for_diff
CodeDiff
```

# Code fetching

```@docs
@code_for
code_native
code_llvm
code_typed
code_ast
get_code
```

# Highlighting

```@docs
code_highlighter
```

# Diff display

```@docs
optimize_line_changes!
side_by_side_diff
```
58 changes: 29 additions & 29 deletions docs/src/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
CurrentModule = CodeDiffs
```

# KernelAbstractions.jl
# GPU Extensions

## KernelAbstractions.jl

[`@code_diff`](@ref) will automatically detect calls to [KernelAbstractions.jl](https://github.com/JuliaGPU/KernelAbstractions.jl)
and get the code for the actual underlying kernel function (whatever the backend is).
Expand All @@ -12,7 +14,7 @@ or when calling the kernel (`gpu_kernel(a, b, c; ndrange=1000)`).

There is no support for AST comparison with KA.jl kernels.

# GPU kernels
## GPU kernels

[`@code_diff`](@ref) supports functions compiled in a GPU context with any of the GPU packages:

Expand All @@ -32,36 +34,34 @@ CUDA has one additional layer of assembly code, SASS, available with `:sass`.
!!! info

Unlike with the `@device_code_*` macros, no kernel code is executed by [`@code_diff`](@ref).
This also means that kernels launched indirectly by the function will be ignored.
The `@device_code_*` macros work by capturing kernel launches, while `@code_diff` or `@code_for`
work with the kernel function directly: this means that kernels launched indirectly by the
function call will be ignored.

!!! info

Note that behind the scenes, `GPUCompiler.jl` only cares about the most recent methods.
Hence the `world` keyword is unsupported for all GPU backends, as we cannot compile back in time.

# Defining a new extension

Defining a new `code_type` involves four functions:
- `CodeDiffs.get_code_dispatch(::Val{code_type}, f, types; kwargs...)` (**not** `get_code`!)
should return a printable object (usually a `String`) representing the code for `f(types)`.
`kwargs` are the options passed to `@code_diff`.
- `CodeDiffs.cleanup_code(::Val{:code_type}, obj)` does some cleanup on the code object to
make it more `diff`-able
- `CodeDiffs.code_highlighter(::Val{code_type})` returns a `f(io, obj)` to print the `obj`
to as text in `io`. This is done twice: once without highlighting (`get(io, :color, false) == false`),
and another with highlighting.
- `CodeDiffs.argconvert(::Val{code_type}, arg)` converts `arg` as needed (by default `arg` is unchanged)

Defining a new pre-processing step for functions and its arguments (like for KernelAbstractions.jl kernels)
involves two functions:
- `CodeDiffs.extract_extra_options(f, kwargs)` returns some additional `kwargs` which are passed to `get_code`
- `CodeDiffs.get_code(code_type, f, types; kwargs...)` allows to change `f` depending on its type.
To avoid method ambiguities, do not put type constraints on `code_type`.

Defining a new object type which can be put as an argument to `@code_diff` or `@code_for`
invoves at one function: `CodeDiffs.code_for_diff(obj::YourType; kwargs...)`.
It must return two `String`s, one without and the other without highlighting.
When calling `@code_for obj`, [`code_for_diff(obj)`](@ref) will be called only if `obj` is
not a call expression or a quoted `Expr`.
`kwargs` are the options passed to `@code_for` or the options passed to `@code_diff` for
the side of `obj`.
### GPU kernel statistics

With the `:cuda_stats` code type, you can get an overview of your CUDA kernel through statistics
inferred from its PTX and SASS code.

Other supported types are `:cuda_stats`, `:ptx_stats` and `:sass_stats` for CUDA kernels, and
`:gcn_stats` for AMDGPU kernels.
See [CodeDiffs.Stats.extract_stats](@ref) for more about them.

Example usage:
```julia
@code_for :cuda_stats some_kernel(a, b, c)
```
Output:
```@eval
using Markdown
using CodeDiffs
ptx_source = readchomp("../../test/samples/extern_func_with_no_params.ptx")
sass_source = readchomp("../../test/samples/extern_func_with_no_params.sass")
cuda_stats = CodeDiffs.Stats.extract_stats(Val(:cuda_stats), (ptx_source, sass_source))
Markdown.MD(Markdown.julia, Markdown.Code("", sprint(Base.show, MIME"text/plain"(), cuda_stats)))
```
90 changes: 20 additions & 70 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,86 +12,36 @@ Supports:
- 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](@ref))

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:
`CodeDiffs.jl` exports two macros:
- [`@code_for`](@ref) will display the code for a function call. The output is
cleaned and highlighted to maximize clarity.
- [`@code_diff`](@ref) will compare the code of two function calls.

```jldoctest f1_vs_f2; setup=:(using CodeDiffs)
julia> f1(a) = a + 1
f1 (generic function with 1 method)
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> @code_diff type=:llvm debuginfo=:none color=false f1(Int64(1)) f1(Int8(1))
define i64 @f1(i64 signext %0) #0 { ⟪╋⟫define i64 @f1(i8 signext %0) #0 {
top: ┃ top:
┣⟫ %1 = sext i8 %0 to i64
%1 = add i64 %0, 1 ⟪╋⟫ %2 = add nsw i64 %1, 1
ret i64 %1 ⟪╋⟫ ret i64 %2
} ┃ }

julia> f2(a) = a - 1
f2 (generic function with 1 method)

julia> @code_diff type=:llvm debuginfo=:none color=false f1(1) f2(1)
define i64 @f1(i64 signext %0) #0 { ⟪╋⟫define i64 @f2(i64 signext %0) #0 {
top: ┃ top:
%1 = add i64 %0, 1 ⟪╋⟫ %1 = add i64 %0, -1
ret i64 %1 ┃ ret i64 %1
} ┃ }
```@repl 1
using CodeDiffs # hide
f1(a) = a + 1
@code_diff type=:llvm debuginfo=:none color=false f1(Int64(1)) f1(Int8(1))
f2(a) = a - 1
@code_diff type=:llvm debuginfo=:none color=false f1(1) f2(1)
```

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

```jldoctest f1_vs_f2
julia> ENV["CODE_DIFFS_LINE_NUMBERS"] = true
true

julia> @code_diff type=:llvm debuginfo=:none color=false f1(1) f2(1)
1 define i64 @f1(i64 signext %0) #0 { ⟪╋⟫define i64 @f2(i64 signext %0) #0 { 1
2 top: ┃ top: 2
3 %1 = add i64 %0, 1 ⟪╋⟫ %1 = add i64 %0, -1 3
4 ret i64 %1 ┃ ret i64 %1 4
5 } ┃ } 5
```@repl 1
ENV["CODE_DIFFS_LINE_NUMBERS"] = true
@code_diff type=:llvm debuginfo=:none color=false f1(1) f2(1)
```

# Comparison entry points
# Main API

```@docs
@code_diff
```@docs; canonical=false
@code_for
code_diff
code_for_diff
CodeDiff
```

# Code fetching

```@docs
code_native
code_llvm
code_typed
code_ast
get_code
```

# Highlighting

```@docs
code_highlighter
```

# Cleanup

```@docs
cleanup_code
replace_llvm_module_name
function_unique_gen_name_regex
global_var_unique_gen_name_regex
```

# Diff display

```@docs
optimize_line_changes!
side_by_side_diff
@code_diff
```
30 changes: 30 additions & 0 deletions docs/src/new_extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
```@meta
CurrentModule = CodeDiffs
```

# Defining a new extension

Defining a new `code_type` involves four functions:
- `CodeDiffs.get_code_dispatch(::Val{code_type}, f, types; kwargs...)` (**not** `get_code`!)
should return a printable object (usually a `String`) representing the code for `f(types)`.
`kwargs` are the options passed to `@code_diff`.
- `CodeDiffs.Cleanup.cleanup_code(::Val{:code_type}, code, dbinfo, cleanup_opts)` does some cleanup
on the code object to make it more `diff`-able.
- `CodeDiffs.code_highlighter(::Val{code_type})` returns a `f(io, obj)` to print the `obj`
to as text in `io`. This is done twice: once without highlighting (`get(io, :color, false) == false`),
and another with highlighting.
- `CodeDiffs.argconvert(::Val{code_type}, arg)` converts `arg` as needed (by default `arg` is unchanged)

Defining a new pre-processing step for functions and its arguments (like for KernelAbstractions.jl kernels)
involves two functions:
- `CodeDiffs.extract_extra_options(f, kwargs)` returns some additional `kwargs` which are passed to `get_code`
- `CodeDiffs.get_code(code_type, f, types; kwargs...)` allows to change `f` depending on its type.
To avoid method ambiguities, do not put type constraints on `code_type`.

Defining a new object type which can be put as an argument to `@code_diff` or `@code_for`
invoves at one function: `CodeDiffs.code_for_diff(obj::YourType; kwargs...)`.
It must return two `String`s, one without and the other without highlighting.
When calling `@code_for obj`, [`code_for_diff(obj)`](@ref) will be called only if `obj` is
not a call expression or a quoted `Expr`.
`kwargs` are the options passed to `@code_for` or the options passed to `@code_diff` for
the side of `obj`.
9 changes: 9 additions & 0 deletions docs/src/stats.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
```@meta
CurrentModule = CodeDiffs.Stats
```

# Code statistics

```@docs
extract_stats
```
Loading