Skip to content

Commit 0ec6218

Browse files
timholyaviatesk
andauthored
Add Cthulhu.ascend integration (#648)
Closes #511 Also adds `reportkey` --------- Co-authored-by: Shuhei Kadowaki <[email protected]>
1 parent 8fe3d8d commit 0ec6218

File tree

9 files changed

+195
-41
lines changed

9 files changed

+195
-41
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
<!-- links start -->
9+
[0.9.8]: https://github.com/aviatesk/JET.jl/compare/v0.9.7...v0.9.8
910
[0.9.6]: https://github.com/aviatesk/JET.jl/compare/v0.9.5...v0.9.6
1011
[0.9.5]: https://github.com/aviatesk/JET.jl/compare/v0.9.4...v0.9.5
1112
[0.9.4]: https://github.com/aviatesk/JET.jl/compare/v0.9.3...v0.9.4
@@ -25,6 +26,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2526
[0.8.0]: https://github.com/aviatesk/JET.jl/compare/v0.7.15...v0.8.0
2627
<!-- links end -->
2728

29+
## [0.9.8]
30+
### Added
31+
- An extension that integrates `@report_opt` with Cthulhu (aviatesk/JET.jl#648)
32+
- `reportkey` for trimming multiple reports that resolve to the same runtime-dispatch caller/callee pair (aviatesk/JET.jl#648)
33+
2834
## [0.9.6]
2935
### Fixed
3036
- `report_opt` no longer raises reports from callees on `throw` code path when the

Project.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "JET"
22
uuid = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
33
authors = ["Shuhei Kadowaki <[email protected]>"]
4-
version = "0.9.7"
4+
version = "0.9.8"
55

66
[deps]
77
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
@@ -15,15 +15,18 @@ Preferences = "21216c6a-2e73-6563-6e65-726566657250"
1515
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1616

1717
[weakdeps]
18+
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
1819
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
1920

2021
[extensions]
22+
JETCthulhuExt = "Cthulhu"
2123
ReviseExt = "Revise"
2224

2325
[compat]
2426
Aqua = "0.8.2"
2527
BenchmarkTools = "1.3.2"
2628
CodeTracking = "1.3.1"
29+
Cthulhu = "2.14.0"
2730
Example = "0.5.3"
2831
InteractiveUtils = "1.10"
2932
JuliaInterpreter = "0.9"
@@ -43,6 +46,7 @@ julia = "1.10"
4346
[extras]
4447
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
4548
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
49+
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
4650
Example = "7876af07-990d-54b4-ab0e-23690620f79a"
4751
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
4852
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
@@ -52,4 +56,4 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
5256
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
5357

5458
[targets]
55-
test = ["Aqua", "BenchmarkTools", "Example", "Libdl", "Logging", "Random", "Revise", "StaticArrays", "Test"]
59+
test = ["Aqua", "BenchmarkTools", "Cthulhu", "Example", "Libdl", "Logging", "Random", "Revise", "StaticArrays", "Test"]

docs/src/optanalysis.md

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ JET implements such an analyzer that investigates the optimized representation o
2727
anywhere the compiler failed in optimization. Especially, it can find where Julia creates captured variables, where
2828
runtime dispatch will happen, and where Julia gives up the optimization work due to unresolvable recursive function call.
2929

30-
[SnoopCompile also detects inference failures](https://timholy.github.io/SnoopCompile.jl/stable/snoopi_deep_analysis/), but JET and SnoopCompile use different mechanisms: JET performs *static* analysis of a particular call, while SnoopCompile performs *dynamic* analysis of new inference. As a consequence, JET's detection of inference failures is reproducible (you can run the same analysis repeatedly and get the same result) but terminates at any non-inferable node of the call graph: you will miss runtime dispatch in any non-inferable callees. Conversely, SnoopCompile's detection of inference failures can explore the entire callgraph, but only for those portions that have not been previously inferred, and the analysis cannot be repeated in the same session.
30+
[SnoopCompile also detects inference failures](https://timholy.github.io/SnoopCompile.jl/stable/tutorials/snoop_inference_analysis/), but JET and SnoopCompile use different mechanisms: JET performs *static* analysis of a particular call, while SnoopCompile performs *dynamic* analysis of new inference. As a consequence, JET's detection of inference failures is reproducible (you can run the same analysis repeatedly and get the same result) but terminates at any non-inferable node of the call graph: you will miss runtime dispatch in any non-inferable callees. Conversely, SnoopCompile's detection of inference failures can explore the entire callgraph, but only for those portions that have not been previously inferred, and the analysis cannot be repeated in the same session.
3131

3232
## [Quick Start](@id optanalysis-quick-start)
3333

@@ -164,6 +164,59 @@ using Test
164164
end
165165
```
166166

167+
## [Integration with Cthulhu](@id cthulhu-integration)
168+
169+
If you identify inference problems, you may want to fix them. Cthulhu can be a useful tool for gaining more insight, and JET integrates nicely with Cthulhu.
170+
171+
To exploit Cthulhu, you first need to split the overall report into individual inference failures:
172+
173+
```@repl quickstart
174+
report = @report_opt sumup(sin);
175+
rpts = JET.get_reports(report)
176+
```
177+
178+
!!! tip
179+
If `rpts` is a long list, consider using `urpts = unique(reportkey, rpts)` to trim it.
180+
See [`reportkey`](@ref).
181+
182+
Now you can `ascend` individual reports:
183+
184+
```
185+
julia> using Cthulhu
186+
187+
julia> ascend(rpts[1])
188+
Choose a call for analysis (q to quit):
189+
runtime dispatch to make_vals(%1::Any)::Any
190+
> sumup(::typeof(sin))
191+
192+
Open an editor at a possible caller of
193+
Tuple{typeof(make_vals), Any}
194+
or browse typed code:
195+
> "REPL[7]", sumup: lines [4]
196+
Browse typed code
197+
```
198+
199+
`ascend` will show the full call-chain to reach a particular runtime dispatch; in this case, it was our entry point, but in other cases it may be deeper in the call graph. In this case, we've interactively moved the selector `>` down to the `sumup` call (you cannot descend into the `"runtime dispatch to..."` as there is no known code associated with it) and hit `<Enter>`, at which point Cthulhu showed us that the call to `make_vals(::Any)` occured only on line 4 of the definition of `sumup` (which we entered at the REPL). Cthulhu is now prompting us to either open the code in an editor (which will fail in this case, since there is no associated file!) or view the type-annoted code. If we select the "Browse typed code" option we see
200+
201+
```
202+
sumup(f) @ Main REPL[7]:1
203+
1 function sumup(f::Core.Const(sin))::Any
204+
2 # this function uses the non-constant global variable `n` here
205+
3 # and it makes every succeeding operations type-unstable
206+
4 vals::Any = make_vals(n::Any)::Any
207+
5 s::Any = zero(eltype(vals::Any)::Any)::Any
208+
6 for v::Any in vals::Any::Any
209+
7 (s::Any += f::Core.Const(sin)(v::Any)::Any)::Any
210+
8 end
211+
9 return s::Any
212+
10 end
213+
Select a call to descend into or ↩ to ascend. [q]uit. [b]ookmark.
214+
215+
```
216+
217+
with red highlighting to indicate the non-inferable arguments.
218+
219+
For more information, you're encouraged to read Cthulhu's documentation, which includes a video tutorial better-suited to this interactive tool.
167220

168221
## [Entry Points](@id optanalysis-entry)
169222

ext/JETCthulhuExt.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module JETCthulhuExt
2+
3+
using JET: JET, InferenceErrorReport, VirtualFrame, PrintConfig, print_signature
4+
using Cthulhu: Cthulhu, Node, Data, callstring
5+
using Core: MethodInstance
6+
7+
const _emptybackedges = MethodInstance[]
8+
9+
struct CallFrames
10+
frames::Vector{VirtualFrame}
11+
end
12+
13+
function Cthulhu.treelist(r::InferenceErrorReport)
14+
io = IOBuffer()
15+
cf = CallFrames(r.vst)
16+
print_signature(IOContext(io, :color=>true), r.sig, PrintConfig())
17+
# printstyled(IOContext(io, :color=>true), r.sig.tt, color=:red)
18+
Cthulhu.treelist!(Node(Data{Union{MethodInstance,Type}}("runtime dispatch to " * String(take!(io)), r.sig.tt)), io, cf, "", Base.IdSet{Union{MethodInstance,Nothing}}([nothing]))
19+
end
20+
21+
Cthulhu.instance(cf::CallFrames) = isempty(cf.frames) ? nothing : cf.frames[end].linfo
22+
Cthulhu.backedges(cf::CallFrames) = isempty(cf.frames) ? _emptybackedges : [cf.frames[end].linfo]
23+
Cthulhu.nextnode(cf::CallFrames, ::MethodInstance) = CallFrames(cf.frames[1:end-1])
24+
25+
end

src/JET.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module JET
66
export
77
# jetanalyzer
88
@report_call, report_call, @test_call, test_call,
9-
report_file, test_file, report_package, test_package, report_text, test_text,
9+
report_file, test_file, report_package, test_package, report_text, reportkey, test_text,
1010
watch_file,
1111
# optanalyzer
1212
@report_opt, report_opt, @test_opt, test_opt,
@@ -294,6 +294,8 @@ get_linfo(linfo::MethodInstance) = linfo
294294
is_constant_propagated(frame::InferenceState) = is_constant_propagated(frame.result)
295295
is_constant_propagated(result::InferenceResult) = CC.any(result.overridden_by_const)
296296

297+
struct TypeUnassigned end # for when inference doesn't bother assigning a type to a slot (e.g. dead code)
298+
297299
# lattice
298300

299301
ignorenotfound(@nospecialize(t)) = t === NOT_FOUND ? Bottom : t

0 commit comments

Comments
 (0)