Skip to content
This repository was archived by the owner on Mar 10, 2021. It is now read-only.

Commit ad609e9

Browse files
authored
Merge pull request #4 from JuliaDebug/vc/port
Port Cthulhu to TypedCodeUtils
2 parents 355184f + 3da6d1f commit ad609e9

File tree

6 files changed

+70
-40
lines changed

6 files changed

+70
-40
lines changed

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ os:
66
julia:
77
- 1.0
88
- 1.1
9+
- 1.2
10+
- 1.3
911
- nightly
1012
notifications:
1113
email: false

Project.toml

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ uuid = "687fb87b-adea-59d5-9be9-82253b54685d"
33
authors = ["Valentin Churavy <[email protected]>"]
44
version = "0.1.0"
55

6+
[compat]
7+
julia = "1"
8+
69
[extras]
710
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
811

src/TypedCodeUtils.jl

+5-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module TypedCodeUtils
33
using Base.Meta
44
import Core: Compiler
55
using .Compiler: widenconst, argextype
6+
using Core: MethodInstance
67

78
"""
89
Consumer
@@ -42,22 +43,16 @@ include("reflection.jl")
4243
# Callsite processing
4344
##
4445
abstract type CallInfo end
45-
46-
struct Callsite
47-
id
48-
callinfo::CallInfo
49-
end
50-
51-
canreflect(c::Callsite) = canreflect(c.callinfo)
52-
reflect(c::Callsite; optimize=true, params=current_params()) = reflect(c.callinfo, optimize=optimize, params=params)
53-
5446
include("process.jl")
47+
48+
##
49+
# Reflection preprocessing
50+
##
5551
include("preprocess.jl")
5652

5753
##
5854
# Utils
5955
##
60-
6156
filter(f, code) = ((id, c) for (id, c) in enumerate(code) if f(c))
6257

6358
"""

src/process.jl

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
canreflect(::CallInfo) = false
2+
isambiguous(::CallInfo) = false
23
reflect(::CallInfo; optimize=true, params=current_params()) = nothing
34

5+
# Call could be resolved to a singular MI
46
struct MICallInfo <: CallInfo
5-
mi
7+
mi::MethodInstance
68
rt
79
end
810
canreflect(::MICallInfo) = true
9-
reflect(mi::MICallInfo; optimize=true, params=current_params()) = reflect(mi.mi, optimize=optimize, params=params)
11+
reflect(mi::MICallInfo; optimize=true, params=current_params()) =
12+
reflect(mi.mi, optimize=optimize, params=params)
1013

1114
struct BuiltinCallInfo <: CallInfo
1215
types
@@ -18,6 +21,8 @@ struct MultiCallInfo <: CallInfo
1821
rt
1922
callinfos::Vector{CallInfo}
2023
end
24+
isambiguous(::MultiCallInfo) = true
25+
Base.collect(mci::MultiCallInfo) = mci.callinfos
2126

2227
struct GeneratedCallInfo <: CallInfo
2328
sig
@@ -63,23 +68,26 @@ function process_call(::Consumer, ref::Reflection, id, c)
6368

6469
# Filter out builtin functions and intrinsic function
6570
if sig[1] <: Core.Builtin || sig[1] <: Core.IntrinsicFunction
66-
return Callsite(id, BuiltinCallInfo(sig, rt))
71+
return id, BuiltinCallInfo(sig, rt)
6772
end
68-
return Callsite(id, callinfo(sig, rt, ref))
73+
return id, callinfo(sig, rt, ref)
6974
end
7075

7176
function callinfo(sig, rt, ref)
72-
methds = Base._methods_by_ftype(sig, 1, ref.world)
77+
methds = Base._methods_by_ftype(sig, -1, ref.world)
7378
(methds === false || length(methds) < 1) && return FailedCallInfo(sig, rt)
7479
callinfos = CallInfo[]
7580
for x in methds
76-
meth = x[3]
7781
atypes = x[1]
7882
sparams = x[2]
83+
meth = x[3]
7984
if isdefined(meth, :generator) && !Base.may_invoke_generator(meth, atypes, sparams)
8085
push!(callinfos, GeneratedCallInfo(sig, rt))
8186
else
82-
mi = code_for_method(meth, atypes, sparams, params.world)
87+
mi = code_for_method(meth, atypes, sparams, ref.world)
88+
if mi === nothing
89+
push!(callinfos, FailedCallInfo(sig, rt))
90+
end
8391
push!(callinfos, MICallInfo(mi, rt))
8492
end
8593
end

src/reflection.jl

+26-10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ else
88
sptypes_from_meth_instance(mi) = Core.Compiler.spvals_from_meth_instance(mi)
99
end
1010

11+
if VERSION >= v"1.2.0-DEV.320"
12+
const may_invoke_generator = Base.may_invoke_generator
13+
else
14+
may_invoke_generator(meth, @nospecialize(atypes), sparams) = isdispatchtuple(atypes)
15+
end
16+
1117
if VERSION < v"1.2.0-DEV.573"
1218
code_for_method(method, metharg, methsp, world, force=false) = Core.Compiler.code_for_method(method, metharg, methsp, world, force)
1319
else
@@ -35,21 +41,31 @@ else
3541
current_params() = Core.Compiler.Params(ccall(:jl_get_world_counter, UInt, ()))
3642
end
3743

38-
function reflect(F, TT; optimize=true, params=current_params())
39-
sig = Tuple{typeof(F), TT.parameters...}
44+
function reflect(@nospecialize(F), @nospecialize(TT); optimize=true, params=current_params(), kwargs...)
45+
sig = Base.signature_type(F, TT)
4046
reflect(sig; optimize=true, params=params)
4147
end
4248

43-
function reflect(sig; optimize=true, params=current_params())
44-
methds = Base._methods_by_ftype(sig, 1, params.world)
49+
# TODO: deduplicate with callinfo(sig, rt, ref)
50+
function reflect(@nospecialize(sig); optimize=true, params=current_params())
51+
methds = Base._methods_by_ftype(sig, -1, params.world)
4552
(methds === false || length(methds) < 1) && return nothing
46-
x = methds[1]
47-
meth = x[3]
48-
if isdefined(meth, :generator) && !isdispatchtuple(Tuple{sig.parameters[2:end]...})
49-
return nothing
53+
reflections = Reflection[]
54+
for x in methds
55+
atypes = x[1]
56+
sparams = x[2]
57+
meth = x[3]
58+
if isdefined(meth, :generator) && !may_invoke_generator(meth, atypes, sparams)
59+
continue
60+
end
61+
mi = code_for_method(meth, sig, x[2], params.world)
62+
if mi === nothing
63+
continue
64+
end
65+
ref = reflect(mi, optimize=optimize, params=params)
66+
push!(reflections, ref)
5067
end
51-
mi = code_for_method(meth, sig, x[2], params.world)
52-
reflect(mi, optimize=optimize, params=params)
68+
return reflections
5369
end
5470

5571
function reflect(mi::Core.Compiler.MethodInstance; optimize=true, params=current_params())

test/runtests.jl

+19-13
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@ using TypedCodeUtils
22
using Test
33

44
import TypedCodeUtils: reflect, filter, lookthrough,
5-
DefaultConsumer, Reflection, Callsite,
5+
DefaultConsumer, Reflection,
66
identify_invoke, identify_call,
77
process_invoke, process_call
88

99
# Test simple reflection
1010
f(x, y) = x + y
1111

12-
@test reflect(f, Tuple{Int, Int}) !== nothing
13-
@test reflect(f, Tuple{Int, Number}) !== nothing # this probably doesn't do the right thing
14-
# it will give us **a** method instance.
12+
@test !isempty(reflect(f, Tuple{Int, Int}))
13+
@test !isempty(reflect(f, Tuple{Int, Number}))
1514
@generated g(x, y) = :(x + y)
16-
@test reflect(g, Tuple{Int, Int}) !== nothing
17-
@test reflect(g, Tuple{Int, Number}) === nothing
15+
@test !isempty(reflect(g, Tuple{Int, Int}))
16+
if VERSION >= v"1.2.0-rc1"
17+
@test !isempty(reflect(g, Tuple{Int, Number}))
18+
else
19+
@test isempty(reflect(g, Tuple{Int, Number}))
20+
end
1821

1922
# Cthulhu's inner loop
2023
function cthulhu(ref::Reflection)
@@ -24,17 +27,17 @@ function cthulhu(ref::Reflection)
2427

2528
invokes = map((arg) -> process_invoke(DefaultConsumer(), ref, arg...), invokes)
2629
calls = map((arg) -> process_call( DefaultConsumer(), ref, arg...), calls)
27-
30+
2831
callsites = append!(invokes, calls)
29-
@show callsites
30-
sort!(callsites, by=(c)->c.id)
32+
sort!(callsites, by=(c)->first(c))
3133
return callsites
3234
end
3335

3436
params = TypedCodeUtils.current_params()
3537
ref = reflect(f, Tuple{Int, Int}, params=params)
36-
calls = cthulhu(ref)
37-
nextrefs = collect(reflect(c) for c in calls if TypedCodeUtils.canreflect(c))
38+
@test length(ref) == 1
39+
calls = cthulhu(first(ref))
40+
nextrefs = collect(first(reflect(c)) for c in calls if TypedCodeUtils.canreflect(c[2]))
3841

3942
function h(x)
4043
if x >= 2
@@ -46,8 +49,9 @@ end
4649

4750
params = TypedCodeUtils.current_params()
4851
ref = reflect(h, Tuple{Int}, params=params)
49-
calls = cthulhu(ref)
50-
nextrefs = collect(reflect(c) for c in calls if TypedCodeUtils.canreflect(c))
52+
@test length(ref) == 1
53+
calls = cthulhu(first(ref))
54+
nextrefs = collect(first(reflect(c)) for c in calls if TypedCodeUtils.canreflect(c[2]))
5155

5256
if VERSION >= v"1.1.0-DEV.215" && Base.JLOptions().check_bounds == 0
5357
Base.@propagate_inbounds function f(x)
@@ -57,6 +61,8 @@ g(x) = @inbounds f(x)
5761

5862
params = TypedCodeUtils.current_params()
5963
ref = reflect(g, Tuple{Vector{Float64}}, params=params)
64+
@test length(ref) == 1
65+
ref = first(ref)
6066
@show ref.CI.code
6167
calls = cthulhu(ref)
6268
@test !isempty(calls)

0 commit comments

Comments
 (0)