Skip to content

Commit

Permalink
Add RTCallInfo for runtime calls (#354)
Browse files Browse the repository at this point in the history
The list of call sites serves two purposes:

- it gives you options to descend into
- it summarizes the calls made by your function

Given the second purpose, I've sometimes been confused by the absence
of an entry for the "worst" of all cases, runtime dispatch. This seems
particularly acute for #345, but may have value even when examining
the CodeInfo/IRCode.
  • Loading branch information
timholy authored Mar 8, 2023
1 parent 9361dc0 commit 3ec146d
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ FoldingTrees = "1"
JuliaSyntax = "0.3.2"
Preferences = "1"
SnoopPrecompile = "1"
TypedSyntax = "1.0.1"
TypedSyntax = "1.0.2"
julia = "1.7"

[extras]
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,17 @@ In the final section, you see:
![calls](images_readme/descend_calls.png)

This is a menu of calls that you can further descend into. Move the dot `` with the up and down
arrow keys, and hit Enter to descend into a particular call. Calls that start with `%nn = ...`
are in Julia's internal [Abstract Syntax Tree (AST)](https://docs.julialang.org/en/v1/devdocs/ast/) form;
arrow keys, and hit Enter to descend into a particular call. Any calls that are made at runtime ([dynamic dispatch](https://en.wikipedia.org/wiki/Dynamic_dispatch)) cannot be descended into;
if you select one, you'll see

```
[ Info: This is a runtime call. You cannot descend into it.
```

and the call menu will be printed again.

Calls that start with `%nn = ...` are in Julia's internal
[Abstract Syntax Tree (AST)](https://docs.julialang.org/en/v1/devdocs/ast/) form;
for these calls, Cthulhu and/or [TypedSyntax](TypedSyntax/README.md) (a sub-package living inside the Cthulhu repository) failed to "map" the call back to the original source code.

As a word of warning, **mapping type inference results back to the source is hard, and there may be errors or omissions in this mapping**. See the [TypedSyntax README](TypedSyntax/README.md) for further details about the challenges. When you think there are reasons to doubt what you're seeing, a reliable but harder-to-interpret strategy is to directly view the [`[T]yped code`](#viewing-the-internal-representation-of-julia-code) rather than the `[S]ource code`.
Expand Down
2 changes: 1 addition & 1 deletion TypedSyntax/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "TypedSyntax"
uuid = "d265eb64-f81a-44ad-a842-4247ee1503de"
authors = ["Tim Holy <[email protected]> and contributors"]
version = "1.0.1"
version = "1.0.2"

[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
Expand Down
8 changes: 6 additions & 2 deletions TypedSyntax/src/node.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ mutable struct TypedSyntaxData <: AbstractSyntaxData
raw::GreenNode{SyntaxHead}
position::Int
val::Any
typ::Any # can either be a Type or `nothing`
typ::Any # can either be a Type or `nothing`
runtime::Bool # true if this represents a call made by runtime dispatch (Cthulhu callsite annotation)
end
TypedSyntaxData(sd::SyntaxData, src::CodeInfo, typ=nothing) = TypedSyntaxData(sd.source, src, sd.raw, sd.position, sd.val, typ)
TypedSyntaxData(sd::SyntaxData, src::CodeInfo, typ=nothing) = TypedSyntaxData(sd.source, src, sd.raw, sd.position, sd.val, typ, false)

const TypedSyntaxNode = TreeNode{TypedSyntaxData}
const MaybeTypedSyntaxNode = Union{SyntaxNode,TypedSyntaxNode}
Expand Down Expand Up @@ -554,3 +555,6 @@ function is_tuple_stmt(@nospecialize(stmt))
f = stmt.args[1]
return isa(f, GlobalRef) && f.mod === Core && f.name == :tuple
end

is_runtime(node::TypedSyntaxNode) = node.runtime
is_runtime(::AbstractSyntaxNode) = false
5 changes: 5 additions & 0 deletions src/Cthulhu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,11 @@ function _descend(term::AbstractTerminal, interp::AbstractInterpreter, curs::Abs
"""
additional_descend(get_mi(info)::MethodInstance)
continue
elseif info isa RTCallInfo
@info """
This is a runtime call. You cannot descend into it.
"""
@goto show_menu
end

# recurse
Expand Down
17 changes: 17 additions & 0 deletions src/callsite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ struct LimitedCallInfo <: WrappedCallInfo
wrapped::CallInfo
end

# Runtime CallInfo
struct RTCallInfo <: CallInfo
f
argtyps
rt
end
get_mi(ci::RTCallInfo) = nothing
get_effects(ci::RTCallInfo) = Effects()

# uncached callsite, we can't recurse into this call
struct UncachedCallInfo <: WrappedCallInfo
wrapped::CallInfo
Expand Down Expand Up @@ -319,6 +328,8 @@ function show_callinfo(limiter, ci::Union{MultiCallInfo, FailedCallInfo, Generat
__show_limited(limiter, name::String, tt, get_rt(ci), get_effects(ci))
end

show_callinfo(limiter, ci::RTCallInfo) = __show_limited(limiter, "$(ci.f)", ci.argtyps, get_rt(ci), get_effects(ci))

function show_callinfo(limiter, pci::PureCallInfo)
ft, tt... = pci.argtypes
f = CC.singleton_type(ft)
Expand Down Expand Up @@ -376,6 +387,12 @@ function print_callsite_info(limiter::IO, info::Union{MultiCallInfo, FailedCallI
show_callinfo(limiter, info)
end

function print_callsite_info(limiter::IO, info::RTCallInfo)
print(limiter, "runtime < ")
show_callinfo(limiter, info)
print(limiter, " >")
end

function print_callsite_info(limiter::IO, info::TaskCallInfo)
print(limiter, "task < ")
show_callinfo(limiter, info.ci)
Expand Down
16 changes: 13 additions & 3 deletions src/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function find_callsites(interp::AbstractInterpreter, CI::Union{Core.CodeInfo, IR
callsite = nothing
if stmt_infos !== nothing && is_call_expr(stmt, optimize)
info = stmt_infos[id]
if info !== NoCallInfo()
if info !== nothing
rt = ignorelimited(argextype(SSAValue(id), CI, sptypes, slottypes))
# in unoptimized IR, there may be `slot = rhs` expressions, which `argextype` doesn't handle
# so extract rhs for such an case
Expand Down Expand Up @@ -97,7 +97,7 @@ function find_callsites(interp::AbstractInterpreter, CI::Union{Core.CodeInfo, IR
if annotate_source
if mappings !== nothing
mapped = mappings[id]
push!(sourcenodes, length(mapped) == 1 ? mapped[1] : callsite)
push!(sourcenodes, length(mapped) == 1 ? tag_runtime(mapped[1], callsite.info) : callsite)
else
push!(sourcenodes, callsite)
end
Expand Down Expand Up @@ -220,7 +220,11 @@ function process_info(interp::AbstractInterpreter, @nospecialize(info::CCCallInf
vmi = FailedCallInfo(sig, Union{})
end
return Any[ReturnTypeCallInfo(vmi)]
elseif info == NoCallInfo() || info === false
elseif info == NoCallInfo()
f = unwrapconst(argtypes[1])
isa(f, Core.Builtin) && return []
return [RTCallInfo(f, argtypes[2:end], rt)]
elseif info === false
return []
else
@eval Main begin
Expand Down Expand Up @@ -359,3 +363,9 @@ function get_typed_sourcetext(mi, src, rt; warn::Bool=true)
end
return tsn, mappings
end

function tag_runtime(node::TypedSyntaxNode, info)
node.runtime = isa(info, RTCallInfo)
return node
end
tag_runtime(node, info) = node
14 changes: 11 additions & 3 deletions src/ui.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ function build_options(callsites, with_effects::Bool, optimize::Bool, iswarn::Bo
nd = TypedSyntax.ndigits_linenumbers(node)
reduced_displaysize -= nd + 1
end
string(chomp(sprint(node; context=:color=>true) do io, node
printstyled(TextWidthLimiter(io, reduced_displaysize), node; iswarn, hide_type_stable)
end))
string(chomp(
sprint(node; context=:color=>true) do io, node
if TypedSyntax.is_runtime(node)
if iswarn
printstyled(io, "runtime "; color=:red)
else
print(io, "runtime ")
end
end
printstyled(TextWidthLimiter(io, reduced_displaysize), node; iswarn, hide_type_stable)
end))
end
end
push!(shown_callsites, "")
Expand Down
10 changes: 8 additions & 2 deletions test/test_terminal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,14 @@ end
Base.text_colors[Base.error_color()]
end
@test occursin("$(warncolor)%\e[39m2 = call → fmulti(::Any)::Union{Float32, Int64}", lines)
write(in, keydict[:enter])
lines = cread1(out)
if isdefined(Core.Compiler, :NoCallInfo)
write(in, keydict[:down])
write(in, keydict[:enter])
lines = cread(out)
else
write(in, keydict[:enter])
lines = cread1(out)
end
@test occursin("%2 = fmulti(::Int32)::Union{Float32, $Int}", lines)
@test occursin("%2 = fmulti(::Float32)::Union{Float32, $Int}", lines)
@test occursin("%2 = fmulti(::Char)::Union{Float32, $Int}", lines)
Expand Down

2 comments on commit 3ec146d

@timholy
Copy link
Member Author

@timholy timholy commented on 3ec146d Mar 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register subdir=TypedSyntax

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/79163

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a TypedSyntax-v1.0.2 -m "<description of version>" 3ec146d643a24bedd52509af22de60496cadaf33
git push origin TypedSyntax-v1.0.2

Please sign in to comment.