Skip to content

Commit a1a3558

Browse files
committed
internals: better representation for code
This'll help solve many of the problems with edges getting mis-represented and broken by adding an extra level of indirection between MethodInstance (now really just representing a particular specialization of a method) and the executable object, now called Lambda (representing some functional operator that converts some input arguments to some output values, with whatever metadata is convenient to contain there). This fixes many of the previous representation problems with back-edges, since a MethodInstance (like Method) no longer tries to also represent a computation. That task is now relegated strictly to Lambda.
1 parent d60503a commit a1a3558

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1650
-1591
lines changed

base/boot.jl

+5-2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565
#mutable struct MethodInstance
6666
#end
6767

68+
#mutable struct CodeInstance
69+
#end
70+
6871
#mutable struct CodeInfo
6972
#end
7073

@@ -442,11 +445,11 @@ Symbol(s::Symbol) = s
442445

443446
# module providing the IR object model
444447
module IR
445-
export CodeInfo, MethodInstance, GotoNode,
448+
export CodeInfo, MethodInstance, CodeInstance, GotoNode,
446449
NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot,
447450
PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode
448451

449-
import Core: CodeInfo, MethodInstance, GotoNode,
452+
import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode,
450453
NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot,
451454
PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode
452455

base/compiler/abstractinterpretation.jl

+36-30
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nosp
8989
this_rt === Any && break
9090
end
9191
else
92-
this_rt, edgecycle, edge = abstract_call_method(method, sig, match[2]::SimpleVector, sv)
92+
this_rt, edgecycle1, edge = abstract_call_method(method, sig, match[2]::SimpleVector, sv)
93+
edgecycle |= edgecycle1::Bool
9394
if edge !== nothing
9495
push!(edges, edge)
9596
end
@@ -170,53 +171,58 @@ function abstract_call_method_with_const_args(@nospecialize(rettype), @nospecial
170171
method.isva && (nargs -= 1)
171172
length(argtypes) >= nargs || return Any
172173
haveconst = false
174+
allconst = true
175+
# see if any or all of the arguments are constant and propagating constants may be worthwhile
173176
for a in argtypes
174177
a = widenconditional(a)
175-
if has_nontrivial_const_info(a)
176-
haveconst = const_prop_profitable(a)
177-
haveconst && break
178+
if allconst && !isa(a, Const) && !isconstType(a) && !isa(a, PartialStruct)
179+
allconst = false
180+
end
181+
if !haveconst && has_nontrivial_const_info(a) && const_prop_profitable(a)
182+
haveconst = true
183+
end
184+
if haveconst && !allconst
185+
break
178186
end
179187
end
180188
haveconst || improvable_via_constant_propagation(rettype) || return Any
181189
sig = match[1]
182190
sparams = match[2]::SimpleVector
183-
code = code_for_method(method, sig, sparams, sv.params.world)
184-
code === nothing && return Any
185-
code = code::MethodInstance
186-
# decide if it's likely to be worthwhile
187-
declared_inline = isdefined(method, :source) && ccall(:jl_ast_flag_inlineable, Bool, (Any,), method.source)
188-
cache_inlineable = declared_inline
189-
if isdefined(code, :inferred) && !cache_inlineable
190-
cache_inf = code.inferred
191-
if !(cache_inf === nothing)
192-
cache_src_inferred = ccall(:jl_ast_flag_inferred, Bool, (Any,), cache_inf)
193-
cache_src_inlineable = ccall(:jl_ast_flag_inlineable, Bool, (Any,), cache_inf)
194-
cache_inlineable = cache_src_inferred && cache_src_inlineable
195-
end
191+
force_inference = allconst || sv.params.aggressive_constant_propagation
192+
if istopfunction(f, :getproperty) || istopfunction(f, :setproperty!)
193+
force_inference = true
196194
end
197-
if !cache_inlineable && !sv.params.aggressive_constant_propagation
198-
tm = _topmod(sv)
199-
if !istopfunction(f, :getproperty) && !istopfunction(f, :setproperty!)
200-
# in this case, see if all of the arguments are constants
201-
for a in argtypes
202-
a = widenconditional(a)
203-
if !isa(a, Const) && !isconstType(a) && !isa(a, PartialStruct)
204-
return Any
205-
end
195+
mi = specialize_method(method, sig, sparams, !force_inference)
196+
mi === nothing && return Any
197+
mi = mi::MethodInstance
198+
# decide if it's likely to be worthwhile
199+
if !force_inference
200+
code = inf_for_methodinstance(mi, sv.params.world)
201+
declared_inline = isdefined(method, :source) && ccall(:jl_ast_flag_inlineable, Bool, (Any,), method.source)
202+
cache_inlineable = declared_inline
203+
if isdefined(code, :inferred) && !cache_inlineable
204+
cache_inf = code.inferred
205+
if !(cache_inf === nothing)
206+
cache_src_inferred = ccall(:jl_ast_flag_inferred, Bool, (Any,), cache_inf)
207+
cache_src_inlineable = ccall(:jl_ast_flag_inlineable, Bool, (Any,), cache_inf)
208+
cache_inlineable = cache_src_inferred && cache_src_inlineable
206209
end
207210
end
211+
if !cache_inlineable
212+
return Any
213+
end
208214
end
209-
inf_result = cache_lookup(code, argtypes, sv.params.cache)
215+
inf_result = cache_lookup(mi, argtypes, sv.params.cache)
210216
if inf_result === nothing
211-
inf_result = InferenceResult(code, argtypes)
217+
inf_result = InferenceResult(mi, argtypes)
212218
frame = InferenceState(inf_result, #=cache=#false, sv.params)
213219
frame.limited = true
214220
frame.parent = sv
215221
push!(sv.params.cache, inf_result)
216222
typeinf(frame) || return Any
217223
end
218224
result = inf_result.result
219-
isa(result, InferenceState) && return Any # TODO: is this recursive constant inference?
225+
isa(result, InferenceState) && return Any # TODO: unexpected, is this recursive constant inference?
220226
add_backedge!(inf_result.linfo, sv)
221227
return result
222228
end
@@ -237,7 +243,7 @@ function abstract_call_method(method::Method, @nospecialize(sig), sparams::Simpl
237243
# necessary in order to retrieve this field from the generated `CodeInfo`, if it exists.
238244
# The other `CodeInfo`s we inspect will already have this field inflated, so we just
239245
# access it directly instead (to avoid regeneration).
240-
method2 = method_for_inference_heuristics(method, sig, sparams, sv.params.world) # Union{Method, Nothing}
246+
method2 = method_for_inference_heuristics(method, sig, sparams) # Union{Method, Nothing}
241247
sv_method2 = sv.src.method_for_inference_limit_heuristics # limit only if user token match
242248
sv_method2 isa Method || (sv_method2 = nothing) # Union{Method, Nothing}
243249
while !(infstate === nothing)

base/compiler/bootstrap.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# since we won't be able to specialize & infer them at runtime
77

88
let fs = Any[typeinf_ext, typeinf, typeinf_edge, pure_eval_call, run_passes],
9-
world = ccall(:jl_get_world_counter, UInt, ())
9+
world = get_world_counter()
1010
for x in T_FFUNC_VAL
1111
push!(fs, x[3])
1212
end

base/compiler/compiler.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ getfield(getfield(Main, :Core), :eval)(getfield(Main, :Core), :(baremodule Compi
55
using Core.Intrinsics, Core.IR
66

77
import Core: print, println, show, write, unsafe_write, stdout, stderr,
8-
_apply, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance
8+
_apply, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance, CodeInstance
99

1010
const getproperty = getfield
1111
const setproperty! = setfield!

base/compiler/inferenceresult.jl

+1-6
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,8 @@ mutable struct InferenceResult
99
result # ::Type, or InferenceState if WIP
1010
src #::Union{CodeInfo, OptimizationState, Nothing} # if inferred copy is available
1111
function InferenceResult(linfo::MethodInstance, given_argtypes = nothing)
12-
if isdefined(linfo, :inferred_const)
13-
result = Const(linfo.inferred_const)
14-
else
15-
result = linfo.rettype
16-
end
1712
argtypes, overridden_by_const = matching_cache_argtypes(linfo, given_argtypes)
18-
return new(linfo, argtypes, overridden_by_const, result, nothing)
13+
return new(linfo, argtypes, overridden_by_const, Any, nothing)
1914
end
2015
end
2116

base/compiler/inferencestate.jl

+5-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const LineNum = Int
55
mutable struct InferenceState
66
params::Params # describes how to compute the result
77
result::InferenceResult # remember where to put the result
8-
linfo::MethodInstance # used here for the tuple (specTypes, env, Method) and world-age validity
8+
linfo::MethodInstance
99
sptypes::Vector{Any} # types of static parameter
1010
slottypes::Vector{Any}
1111
mod::Module
@@ -87,13 +87,8 @@ mutable struct InferenceState
8787
inmodule = linfo.def::Module
8888
end
8989

90-
if cached && !toplevel
91-
min_valid = min_world(linfo.def)
92-
max_valid = max_world(linfo.def)
93-
else
94-
min_valid = typemax(UInt)
95-
max_valid = typemin(UInt)
96-
end
90+
min_valid = UInt(1)
91+
max_valid = get_world_counter()
9792
frame = new(
9893
params, result, linfo,
9994
sp, slottypes, inmodule, 0,
@@ -194,15 +189,12 @@ _topmod(sv::InferenceState) = _topmod(sv.mod)
194189
function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::InferenceState)
195190
sv.min_valid = max(sv.min_valid, min_valid)
196191
sv.max_valid = min(sv.max_valid, max_valid)
197-
@assert(!isa(sv.linfo.def, Method) ||
198-
!sv.cached ||
199-
sv.min_valid <= sv.params.world <= sv.max_valid,
192+
@assert(sv.min_valid <= sv.params.world <= sv.max_valid,
200193
"invalid age range update")
201194
nothing
202195
end
203196

204197
update_valid_age!(edge::InferenceState, sv::InferenceState) = update_valid_age!(edge.min_valid, edge.max_valid, sv)
205-
update_valid_age!(li::MethodInstance, sv::InferenceState) = update_valid_age!(min_world(li), max_world(li), sv)
206198

207199
function record_ssa_assign(ssa_id::Int, @nospecialize(new), frame::InferenceState)
208200
old = frame.src.ssavaluetypes[ssa_id]
@@ -226,6 +218,7 @@ function add_cycle_backedge!(frame::InferenceState, caller::InferenceState, curr
226218
update_valid_age!(frame, caller)
227219
backedge = (caller, currpc)
228220
contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge)
221+
add_backedge!(frame.linfo, caller)
229222
return frame
230223
end
231224

@@ -236,7 +229,6 @@ function add_backedge!(li::MethodInstance, caller::InferenceState)
236229
caller.stmt_edges[caller.currpc] = []
237230
end
238231
push!(caller.stmt_edges[caller.currpc], li)
239-
update_valid_age!(li, caller)
240232
nothing
241233
end
242234

base/compiler/optimize.jl

+9-7
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ mutable struct OptimizationState
5656
return new(linfo,
5757
s_edges::Vector{Any},
5858
src, inmodule, nargs,
59-
min_world(linfo), max_world(linfo),
59+
UInt(1), get_world_counter(),
6060
params, sptypes_from_meth_instance(linfo), slottypes, false)
6161
end
6262
end
@@ -104,19 +104,21 @@ _topmod(sv::OptimizationState) = _topmod(sv.mod)
104104
function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::OptimizationState)
105105
sv.min_valid = max(sv.min_valid, min_valid)
106106
sv.max_valid = min(sv.max_valid, max_valid)
107-
@assert(!isa(sv.linfo.def, Method) ||
108-
(sv.min_valid == typemax(UInt) && sv.max_valid == typemin(UInt)) ||
109-
sv.min_valid <= sv.params.world <= sv.max_valid,
107+
@assert(sv.min_valid <= sv.params.world <= sv.max_valid,
110108
"invalid age range update")
111109
nothing
112110
end
113111

114-
update_valid_age!(li::MethodInstance, sv::OptimizationState) = update_valid_age!(min_world(li), max_world(li), sv)
115-
116112
function add_backedge!(li::MethodInstance, caller::OptimizationState)
113+
#TODO: deprecate this?
117114
isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs
118115
push!(caller.calledges, li)
119-
update_valid_age!(li, caller)
116+
nothing
117+
end
118+
119+
function add_backedge!(li::CodeInstance, caller::OptimizationState)
120+
update_valid_age!(min_world(li), max_world(li), caller)
121+
add_backedge!(li.def, caller)
120122
nothing
121123
end
122124

base/compiler/params.jl

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ struct Params
5656
tuple_splat)
5757
end
5858
function Params(world::UInt)
59+
world == typemax(UInt) && (world = get_world_counter()) # workaround for bad callers
60+
@assert world <= get_world_counter()
5961
inlining = inlining_enabled()
6062
return new(Vector{InferenceResult}(),
6163
world, true,

0 commit comments

Comments
 (0)