Skip to content

Commit 8274a67

Browse files
authored
Split compiler integration to avoid duplicating Cthulhu when loading the compiler extension (#677)
* Initial implementation * WIP: Overhaul internal architecture & user-facing API * Bump major version * Fix highlighter command naming * Implement initial external AbstractInterpreter support * Separate commands and their values This way, commands merely register shortcuts to execute callbacks, so they get simplified, and at the same time we no longer have to worry about keeping command "values" and configuration values in sync. * Properly implement way to conditionally disable commands * Move quit/bookmark commands to beginning of action list * Fix a few tests * Adjust conditional Command printing * `with_effects` -> `effects`, re-enable remark/effect/exception type annotations The renaming was done to improve consistency with similar settings, `remarks` and `exception_type`. * Re-enable VSCode diagnostics & inlay types * Add backspace shortcut to go back (ascend) * Refactor bookmark functionality * Remove disabled AbstractCursor interface * Fix & clarify IRCode/CodeInfo interactions in LookupResult `IRCode` is now used primarily for callsite detection and display. `CodeInfo` may also be used for that purpose, if no `IRCode` is available. The main requirement is that they are consistent with the provided call infos. `CodeInfo` is mainly used to extract slotnames, get more accurate sources via TypedSyntax, and for module dumping for LLVM/native views. * Minor interface refactors * Move terminal utility functions to a separate file * Start testing various AbstractProvider implementations * Start adding docs * Re-enable Compiler extension, test DefaultProvider with both integrations * Prevent terminal tests from hanging * Fix test_terminal.jl * FakeTerminal -> VirtualTerminal * Revert minor printing changes * Fix AbstractInterpreter tests * Fix test_codeview * Use unoptimized lookup if source is not available * More fixes, add a few more tests * Reimplement `descend_code_`typed/warntype * Attempt to fix tests for 1.13 * Remove dead code * Add ExternalProvider example * Improve interface documentation * exception_type -> exception_types To match the plural used for `remarks`, `effects`, etc * inline_cost -> inlining_costs To improve consistency with `remarks`, `effects`, `exception_type` * Update example `descend` usage in README * Test fixes, avoid corrupting the global method table * Remove `interruptexc`, add a few more docs Now that we have backspace to ascend, we can have 'q' quit unconditionally and remove this option. Furthermore, we now exit without having to handle an interrupt. * Only reevaluate compiler-related functionality in CthulhuCompilerExt * Fix tests * Add more comments, make ir_to_src common * Mention docstring of AbstractProvider in changelog * Allow returning `nothing` after lookup to log an error
1 parent 0d4a949 commit 8274a67

29 files changed

+931
-822
lines changed

CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
### Improvements
66

7-
- The way that packages integrate with Cthulhu to customize the behavior of code introspection has been redesigned (see https://github.com/JuliaDebug/Cthulhu.jl/pull/662 for more details).
7+
- The way that packages integrate with Cthulhu to customize the behavior of code introspection has been redesigned (https://github.com/JuliaDebug/Cthulhu.jl/pull/662 and https://github.com/JuliaDebug/Cthulhu.jl/pull/677). See the docstring of `Cthulhu.AbstractProvider` for more details.
88
- A new UI command mapped to `DEL` (backspace, `'\x7f'`) now allows to go back (ascend) with a single key press.
99

1010
### Breaking changes
@@ -17,4 +17,3 @@
1717
- The `with_effects` configuration option was renamed to `effects` for consistency with `remarks` and `exception_types`.
1818
- The `inline_cost` configuration option was renamed to `inlining_costs`, also for consistency reasons.
1919
- The `interruptexc` configuration option was removed. It used to control whether `q` exited (by throwing an `InterruptException`) or ascended, but now that backspace was added as a shortcut to ascend, we can now unconditionally exit with `q` (which actually matches its action description).
20-

ext/CthulhuCompilerExt.jl

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
module CthulhuCompilerExt
22

33
using Compiler: Compiler as CC
4-
using Compiler.IRShow: IRShow
54
using Cthulhu: Cthulhu
65

7-
function __init__()
8-
Cthulhu.CTHULHU_MODULE[] = @__MODULE__
9-
read_config!()
10-
end
6+
@static if CC.AbstractInterpreter !== Cthulhu.CC.AbstractInterpreter
7+
using Compiler.IRShow: IRShow
8+
9+
using Accessors
10+
using CodeTracking: CodeTracking, definition, whereis, maybe_fix_path
11+
using InteractiveUtils
12+
using UUIDs
13+
using REPL: REPL, AbstractTerminal
14+
using JuliaSyntax
15+
using JuliaSyntax: SyntaxNode, AbstractSyntaxNode, child, children
16+
using TypedSyntax
17+
using WidthLimitedIO
18+
19+
using Core.IR
1120

12-
include("../src/CthulhuBase.jl")
21+
include("../src/CthulhuCompiler.jl")
22+
23+
function __init__()
24+
Cthulhu.CompilerExt = @__MODULE__
25+
end
26+
else
27+
function __init__()
28+
Cthulhu.CompilerExt = nothing
29+
end
30+
end
1331

1432
end

src/Cthulhu.jl

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,61 @@ module Cthulhu
22

33
export @descend, @descend_code_typed, @descend_code_warntype,
44
descend, descend_code_typed, descend_code_warntype, ascend,
5-
AbstractProvider
5+
AbstractProvider,
6+
is_compiler_loaded, is_compiler_extension_loaded, get_module_for_compiler_integration
67

78
const CC = Base.Compiler
89
const IRShow = Base.IRShow
910

10-
const CTHULHU_MODULE = Ref{Module}(@__MODULE__)
11+
using Accessors
12+
using CodeTracking: CodeTracking, definition, whereis, maybe_fix_path
13+
using InteractiveUtils
14+
using InteractiveUtils: is_expected_union
15+
using UUIDs
16+
using REPL: REPL, AbstractTerminal
17+
using JuliaSyntax: JuliaSyntax, children
18+
using TypedSyntax
19+
import WidthLimitedIO: TextWidthLimiter
20+
using Preferences
1121

22+
using Core.IR
23+
using Base: default_tt, unwrap_unionall, mapany
24+
25+
global CompilerExt::Union{Nothing, Module}
26+
27+
is_compiler_extension_loaded() = isdefined(@__MODULE__, :CompilerExt)
1228
function is_compiler_loaded()
1329
pkgid = Base.PkgId(Base.UUID("807dbc54-b67e-4c79-8afb-eafe4df6f2e1"), "Compiler")
1430
return haskey(Base.loaded_modules, pkgid)
1531
end
16-
is_compiler_extension_loaded() = CTHULHU_MODULE[] !== @__MODULE__
17-
18-
function resolve_module(Compiler::Module)
19-
Compiler === Base.Compiler && return @__MODULE__
20-
Compiler === CTHULHU_MODULE[].Compiler && return CTHULHU_MODULE[]
21-
return resolve_module()
22-
end
23-
resolve_module() = CTHULHU_MODULE[]
24-
function resolve_module(@nospecialize(::T)) where {T}
25-
mod = parentmodule(T)
26-
nameof(mod) === :Compiler && return resolve_module(mod)
27-
return resolve_module()
32+
function get_module_for_compiler_integration(; use_compiler_stdlib::Bool = true)
33+
!use_compiler_stdlib && return @__MODULE__
34+
is_compiler_extension_loaded() || error("The Cthulhu -> Compiler extension must be loaded first if `use_compiler_stdlib` is set to `true`")
35+
return something(CompilerExt, @__MODULE__)
2836
end
2937

38+
cached_exception_type(code::CodeInstance) = code.exctype
39+
get_mi(ci::CodeInstance) = CC.get_ci_mi(ci)
40+
get_mi(mi::MethodInstance) = mi
41+
42+
include("config.jl")
43+
include("preferences.jl")
3044
__init__() = read_config!()
3145

32-
include("CthulhuBase.jl")
46+
include("interface.jl")
47+
include("callsite.jl")
48+
include("state.jl")
49+
include("ui.jl")
50+
include("bookmark.jl")
51+
include("descend.jl")
52+
include("ascend.jl")
3353
include("backedges.jl")
3454
include("testing.jl")
3555

56+
function ir_to_src end
57+
58+
include("CthulhuCompiler.jl")
59+
3660
"""
3761
@descend
3862
@@ -146,19 +170,8 @@ julia> descend() do
146170
[...]
147171
```
148172
"""
149-
function descend(@nospecialize(args...); interp=nothing,
150-
provider=nothing,
151-
@nospecialize(kwargs...))
152-
if provider !== nothing
153-
mod = resolve_module(provider)
154-
mod.descend_with_error_handling(args...; provider, kwargs...)
155-
elseif interp !== nothing
156-
mod = resolve_module(interp)
157-
mod.descend_with_error_handling(args...; interp, kwargs...)
158-
else
159-
mod = resolve_module()
160-
mod.descend_with_error_handling(args...; kwargs...)
161-
end
173+
function descend(@nospecialize(args...); @nospecialize(kwargs...))
174+
descend_with_error_handling(args...; kwargs...)
162175
return nothing
163176
end
164177

@@ -236,9 +249,7 @@ with the option to `descend` into intermediate calls.
236249
Keyword arguments `pagesize, dynamic, maxsize` are passed to `Cthulhu.FoldingTrees.TreeMenu`.
237250
Any remaining `kwargs` are passed to [`descend`](@ref).
238251
"""
239-
function ascend(@nospecialize(args...); kwargs...)
240-
CTHULHU_MODULE[].ascend_impl(args...; kwargs...)
241-
end
252+
ascend(@nospecialize(args...); kwargs...) = ascend_impl(args...; kwargs...)
242253

243254
using PrecompileTools
244255
@setup_workload begin

src/CthulhuBase.jl renamed to src/CthulhuCompiler.jl

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,29 @@
11
Base.Experimental.@compiler_options compile=min optimize=1
22

3-
using Accessors
4-
using CodeTracking: CodeTracking, definition, whereis, maybe_fix_path
5-
using InteractiveUtils
6-
using UUIDs
7-
using REPL: REPL, AbstractTerminal
8-
using JuliaSyntax
9-
using JuliaSyntax: SyntaxNode, AbstractSyntaxNode, children, is_leaf
10-
using TypedSyntax
11-
using WidthLimitedIO
3+
import .Cthulhu: AbstractProvider, get_abstract_interpreter, get_inference_world, find_method_instance, generate_code_instance, get_override, lookup, find_caller_of, get_inlining_costs, show_parameters, get_ci, get_rt, get_pc_remarks, get_pc_effects, get_pc_excts, show_callsite, show_callinfo, print_callsite_info, cthulhu_source, cthulhu_typed, cthulhu_ast, cthulhu_llvm, cthulhu_native, find_callsites, ir_to_src
4+
using .Cthulhu: CthulhuState, CthulhuConfig, CallInfo, Callsite, cached_exception_type, get_mi
5+
6+
using Base: isvarargtype, unwrapva, unwrap_unionall, mapany, get_world_counter
7+
using JuliaSyntax: JuliaSyntax, children, is_leaf
128

13-
using Core: MethodInstance, MethodMatch
14-
using Core.IR
159
using .CC: AbstractInterpreter, CallMeta, ApplyCallInfo, CallInfo as CCCallInfo, ConstCallInfo,
1610
EFFECTS_TOTAL, Effects, IncrementalCompact, InferenceParams, InferenceResult,
1711
InferenceState, IRCode, LimitedAccuracy, MethodMatchInfo, MethodResultPure,
1812
NativeInterpreter, NoCallInfo, OptimizationParams, OptimizationState,
19-
UnionSplitApplyCallInfo, UnionSplitInfo, WorldRange, WorldView, get_inference_world,
13+
UnionSplitApplyCallInfo, UnionSplitInfo, WorldRange, WorldView,
2014
argextype, argtypes_to_type, compileable_specialization, ignorelimited, singleton_type,
21-
specialize_method, sptypes_from_meth_instance, widenconst, method_table, findsup
22-
using Base: @constprop, default_tt, isvarargtype, unwrapva, unwrap_unionall, rewrap_unionall
23-
const mapany = Base.mapany
15+
specialize_method, sptypes_from_meth_instance, widenconst, method_table, findsup,
16+
cached_return_type
2417

2518
const ArgTypes = Vector{Any}
2619

27-
using Base: get_world_counter
28-
29-
get_mi(ci::CodeInstance) = CC.get_ci_mi(ci)
30-
get_mi(mi::MethodInstance) = mi
31-
32-
using Preferences
33-
include("config.jl")
34-
include("preferences.jl")
35-
36-
include("interface.jl")
37-
include("callsite.jl")
38-
include("compiler.jl")
39-
include("state.jl")
40-
include("interpreter.jl")
41-
include("provider.jl")
42-
include("reflection.jl")
43-
include("ui.jl")
44-
include("codeview.jl")
45-
include("bookmark.jl")
46-
include("descend.jl")
47-
include("ascend.jl")
48-
49-
resolve_module(::AbstractProvider) = @__MODULE__
50-
51-
using .CC: cached_return_type
52-
53-
cached_exception_type(code::CodeInstance) = code.exctype
20+
include("compiler/callsite.jl")
21+
include("compiler/interface.jl")
22+
include("compiler/lookup.jl")
23+
include("compiler/interpreter.jl")
24+
include("compiler/provider.jl")
25+
include("compiler/reflection.jl")
26+
include("compiler/codeview.jl")
5427

5528
get_effects(codeinst::CodeInstance) = CC.decode_effects(codeinst.ipo_purity_bits)
5629
get_effects(codeinst::CodeInfo) = CC.decode_effects(codeinst.purity)

src/ascend.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
function ascend_impl(
22
term, mi;
3-
interp::AbstractInterpreter=NativeInterpreter(),
3+
interp=Base.Compiler.NativeInterpreter(),
44
provider::AbstractProvider=AbstractProvider(interp),
55
pagesize::Int=10, dynamic::Bool=false, maxsize::Int=pagesize,
66
menu_options=(; pagesize), kwargs...)

src/backedges.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ function show_tuple_as_call(@nospecialize(highlighter), io::IO, name::Symbol, @n
1818
sig = (sig::DataType).parameters
1919

2020
ft = sig[1]
21-
uw = Base.unwrap_unionall(ft)
21+
uw = unwrap_unionall(ft)
2222
if ft <: Function && isa(uw,DataType) && isempty(uw.parameters) &&
2323
isdefined(uw.name.module, get_fname(uw)) &&
2424
ft == typeof(getfield(uw.name.module, get_fname(uw)))
@@ -44,7 +44,7 @@ end
4444

4545
function stripType(@nospecialize(typ))
4646
if isa(typ, UnionAll)
47-
typ = Base.unwrap_unionall(typ)
47+
typ = unwrap_unionall(typ)
4848
elseif isa(typ, TypeVar) || isa(typ, Union)
4949
return typ
5050
end

src/bookmark.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const BOOKMARKS = Bookmark[]
3636
function Base.show(io::IO, ::MIME"text/plain", bookmark::Bookmark; kwargs...)
3737
(; provider, ci) = bookmark
3838
state = CthulhuState(bookmark; kwargs...)
39-
result = LookupResult(provider, ci, state.config.optimize)
39+
result = lookup(provider, ci, state.config.optimize)
4040
world = get_inference_world(provider)
4141
if get(io, :typeinfo, Any) === Bookmark # a hack to check if in Vector etc.
4242
info = EdgeCallInfo(ci, result.rt, Effects())
@@ -52,7 +52,7 @@ end
5252
function Base.code_typed(bookmark::Bookmark; kwargs...)
5353
(; provider, ci) = bookmark
5454
state = CthulhuState(bookmark; kwargs...)
55-
result = LookupResult(provider, ci, state.config.optimize)
55+
result = lookup(provider, ci, state.config.optimize)
5656
src = something(result.src, result.ir)::Union{CodeInfo, IRCode}
5757
return src => result.rt
5858
end
@@ -67,20 +67,20 @@ InteractiveUtils.code_native(bookmark::Bookmark; kwargs...) =
6767
function InteractiveUtils.code_warntype(io::IO, bookmark::Bookmark; kwargs...)
6868
(; provider, ci) = bookmark
6969
state = CthulhuState(bookmark; kwargs...)
70-
result = LookupResult(provider, ci, state.config.optimize)
70+
result = lookup(provider, ci, state.config.optimize)
7171
cthulhu_warntype(io, provider, state, result)
7272
end
7373

7474
function InteractiveUtils.code_llvm(io::IO, bookmark::Bookmark; dump_module = false, raw = false, kwargs...)
7575
(; provider, ci) = bookmark
7676
state = CthulhuState(bookmark; kwargs...)
77-
result = LookupResult(provider, ci, state.config.optimize)
77+
result = lookup(provider, ci, state.config.optimize)
7878
cthulhu_llvm(io, provider, state, result; dump_module, raw)
7979
end
8080

8181
function InteractiveUtils.code_native(io::IO, bookmark::Bookmark; dump_module = false, raw = false, kwargs...)
8282
(; provider, ci) = bookmark
8383
state = CthulhuState(bookmark; kwargs...)
84-
result = LookupResult(provider, ci, state.config.optimize)
84+
result = lookup(provider, ci, state.config.optimize)
8585
cthulhu_native(io, provider, state, result; dump_module, raw)
8686
end

0 commit comments

Comments
 (0)