@@ -3903,8 +3903,27 @@ function get_spec_lambda(atypes::ANY, sv, invoke_data::ANY)
3903
3903
end
3904
3904
end
3905
3905
3906
- function invoke_NF (argexprs, etype:: ANY , atypes, sv, atype_unlimited:: ANY ,
3907
- invoke_data:: ANY )
3906
+ function linearize_args! (args:: Vector{Any} , atypes:: Vector{Any} , stmts:: Vector{Any} , sv:: InferenceState )
3907
+ # linearize the IR by moving the arguments to SSA position
3908
+ na = length (args)
3909
+ @assert length (atypes) == na
3910
+ newargs = Vector {Any} (na)
3911
+ for i = na: - 1 : 1
3912
+ aei = args[i]
3913
+ ti = atypes[i]
3914
+ if isa (aei, Expr) || isa (aei, GlobalRef)
3915
+ newvar = newvar! (sv, ti)
3916
+ unshift! (stmts, Expr (:(= ), newvar, aei))
3917
+ else
3918
+ newvar = aei
3919
+ end
3920
+ newargs[i] = newvar
3921
+ end
3922
+ return newargs
3923
+ end
3924
+
3925
+ function invoke_NF (argexprs, etype:: ANY , atypes:: Vector{Any} , sv:: InferenceState ,
3926
+ atype_unlimited:: ANY , invoke_data:: ANY )
3908
3927
# converts a :call to :invoke
3909
3928
nu = countunionsplit (atypes)
3910
3929
nu > sv. params. MAX_UNION_SPLITTING && return NF
@@ -3918,12 +3937,16 @@ function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY,
3918
3937
end
3919
3938
3920
3939
if nu > 1
3940
+ # linearize the IR by moving the arguments to SSA position
3941
+ stmts = []
3942
+
3921
3943
spec_miss = nothing
3922
3944
error_label = nothing
3923
3945
ex = Expr (:call )
3924
- ex. args = copy (argexprs)
3925
3946
ex. typ = etype
3926
- stmts = []
3947
+ ex. args = linearize_args! (argexprs, atypes, stmts, sv)
3948
+ invoke_texpr === nothing || insert! (stmts, 2 , invoke_texpr)
3949
+ invoke_fexpr === nothing || unshift! (stmts, invoke_fexpr)
3927
3950
3928
3951
local ret_var, merge, invoke_ex, spec_hit
3929
3952
ret_var = add_slot! (sv. src, widenconst (etype), false )
@@ -3933,26 +3956,6 @@ function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY,
3933
3956
unshift! (invoke_ex. args, nothing )
3934
3957
spec_hit = false
3935
3958
3936
- # linearize the IR by moving the arguments to SSA position
3937
- for i = length (atypes): - 1 : 1
3938
- if i == 1 && ! (invoke_texpr === nothing )
3939
- unshift! (stmts, invoke_texpr)
3940
- end
3941
- ti = atypes[i]
3942
- aei = ex. args[i]
3943
- if ! isa (aei, Slot) &&
3944
- ! isa (aei, SSAValue) &&
3945
- ! isa (aei, Symbol) &&
3946
- ! isa (aei, QuoteNode) &&
3947
- ! isa (aei, Int) &&
3948
- ! isa (aei, Type) &&
3949
- ! isa (aei, GlobalRef)
3950
- newvar = newvar! (sv, ti)
3951
- unshift! (stmts, :($ newvar = $ aei))
3952
- ex. args[i] = newvar
3953
- end
3954
- end
3955
- invoke_fexpr === nothing || unshift! (stmts, invoke_fexpr)
3956
3959
function splitunion (atypes:: Vector{Any} , i:: Int )
3957
3960
if i == 0
3958
3961
local sig = argtypes_to_type (atypes)
@@ -4020,6 +4023,7 @@ function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY,
4020
4023
local cache_linfo = get_spec_lambda (atype_unlimited, sv, invoke_data)
4021
4024
cache_linfo === nothing && return NF
4022
4025
add_backedge! (cache_linfo, sv)
4026
+ argexprs = copy (argexprs)
4023
4027
unshift! (argexprs, cache_linfo)
4024
4028
ex = Expr (:invoke )
4025
4029
ex. args = argexprs
@@ -4206,6 +4210,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4206
4210
end
4207
4211
4208
4212
argexprs0 = argexprs
4213
+ atypes0 = atypes
4209
4214
na = Int (method. nargs)
4210
4215
# check for vararg function
4211
4216
isva = false
@@ -4214,6 +4219,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4214
4219
# construct tuple-forming expression for argument tail
4215
4220
vararg = mk_tuplecall (argexprs[na: end ], sv)
4216
4221
argexprs = Any[argexprs[1 : (na - 1 )]. .. , vararg]
4222
+ atypes = Any[atypes[1 : (na - 1 )]. .. , vararg. typ]
4217
4223
isva = true
4218
4224
elseif na != length (argexprs)
4219
4225
# we have a method match only because an earlier
@@ -4253,7 +4259,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4253
4259
4254
4260
# see if the method has been previously inferred (and cached)
4255
4261
linfo = code_for_method (method, metharg, methsp, sv. params. world, ! force_infer) # Union{Void, MethodInstance}
4256
- isa (linfo, MethodInstance) || return invoke_NF (argexprs0, e. typ, atypes , sv,
4262
+ isa (linfo, MethodInstance) || return invoke_NF (argexprs0, e. typ, atypes0 , sv,
4257
4263
atype_unlimited, invoke_data)
4258
4264
linfo = linfo:: MethodInstance
4259
4265
if linfo. jlcall_api == 2
@@ -4323,7 +4329,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4323
4329
src_inlineable = ccall (:jl_ast_flag_inlineable , Bool, (Any,), inferred)
4324
4330
end
4325
4331
if ! src_inferred || ! src_inlineable
4326
- return invoke_NF (argexprs0, e. typ, atypes , sv, atype_unlimited,
4332
+ return invoke_NF (argexprs0, e. typ, atypes0 , sv, atype_unlimited,
4327
4333
invoke_data)
4328
4334
end
4329
4335
@@ -4350,7 +4356,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4350
4356
if inline_worthy_stmts (current_stmts)
4351
4357
append! (current_stmts, ast)
4352
4358
if ! inline_worthy_stmts (current_stmts)
4353
- return invoke_NF (argexprs0, e. typ, atypes , sv, atype_unlimited,
4359
+ return invoke_NF (argexprs0, e. typ, atypes0 , sv, atype_unlimited,
4354
4360
invoke_data)
4355
4361
end
4356
4362
end
@@ -4388,46 +4394,9 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4388
4394
prelude_stmts = []
4389
4395
stmts_free = true # true = all entries of stmts are effect_free
4390
4396
4391
- for i = na: - 1 : 1 # stmts_free needs to be calculated in reverse-argument order
4392
- # args_i = args[i]
4393
- aei = argexprs[i]
4394
- aeitype = argtype = widenconst (exprtype (aei, sv. src, sv. mod))
4395
- if i == 1 && ! (invoke_texpr === nothing )
4396
- unshift! (prelude_stmts, invoke_texpr)
4397
- end
4398
-
4399
- # ok for argument to occur more than once if the actual argument
4400
- # is a symbol or constant, or is not affected by previous statements
4401
- # that will exist after the inlining pass finishes
4402
- affect_free = stmts_free # false = previous statements might affect the result of evaluating argument
4403
- occ = 0
4404
- for j = length (body. args): - 1 : 1
4405
- b = body. args[j]
4406
- if occ < 6
4407
- occ += occurs_more (b, x-> (isa (x, Slot) && slot_id (x) == i), 6 )
4408
- end
4409
- if occ > 0 && affect_free && ! effect_free (b, src, method. module, true )
4410
- # TODO : we might be able to short-circuit this test better by memoizing effect_free(b) in the for loop over i
4411
- affect_free = false
4412
- end
4413
- if occ > 5 && ! affect_free
4414
- break
4415
- end
4416
- end
4417
- free = effect_free (aei, sv. src, sv. mod, true )
4418
- if ((occ== 0 && aeitype=== Bottom) || (occ > 1 && ! inline_worthy (aei, occ* 2000 )) ||
4419
- (affect_free && ! free) || (! affect_free && ! effect_free (aei, sv. src, sv. mod, false )))
4420
- if occ != 0
4421
- vnew = newvar! (sv, aeitype)
4422
- argexprs[i] = vnew
4423
- unshift! (prelude_stmts, Expr (:(= ), vnew, aei))
4424
- stmts_free &= free
4425
- elseif ! free && ! isType (aeitype)
4426
- unshift! (prelude_stmts, aei)
4427
- stmts_free = false
4428
- end
4429
- end
4430
- end
4397
+ # linearize the IR by moving the arguments to SSA position
4398
+ argexprs = linearize_args! (argexprs, atypes, prelude_stmts, sv)
4399
+ invoke_texpr === nothing || insert! (prelude_stmts, 2 , invoke_texpr)
4431
4400
invoke_fexpr === nothing || unshift! (prelude_stmts, invoke_fexpr)
4432
4401
4433
4402
# re-number the SSAValues and copy their type-info to the new ast
@@ -4478,19 +4447,18 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4478
4447
local retval
4479
4448
multiret = false
4480
4449
lastexpr = pop! (body. args)
4481
- if isa (lastexpr,LabelNode)
4450
+ if isa (lastexpr, LabelNode)
4482
4451
push! (body. args, lastexpr)
4483
4452
push! (body. args, Expr (:call , GlobalRef (topmod, :error ), " fatal error in type inference (lowering)" ))
4484
4453
lastexpr = nothing
4485
- elseif ! (isa (lastexpr,Expr) && lastexpr. head === :return )
4454
+ elseif ! (isa (lastexpr, Expr) && lastexpr. head === :return )
4486
4455
# code sometimes ends with a meta node, e.g. inbounds pop
4487
4456
push! (body. args, lastexpr)
4488
4457
lastexpr = nothing
4489
4458
end
4490
4459
for a in body. args
4491
4460
push! (stmts, a)
4492
- if isa (a,Expr)
4493
- a = a:: Expr
4461
+ if isa (a, Expr)
4494
4462
if a. head === :return
4495
4463
if ! multiret
4496
4464
# create slot first time
@@ -4580,7 +4548,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
4580
4548
end
4581
4549
end
4582
4550
4583
- if isa (expr,Expr)
4551
+ if isa (expr, Expr)
4584
4552
old_t = e. typ
4585
4553
if old_t ⊑ expr. typ
4586
4554
# if we had better type information than the content being inlined,
@@ -4618,10 +4586,10 @@ function inline_worthy(body::Expr, cost::Integer=1000) # precondition: 0 < cost;
4618
4586
nstmt += 1
4619
4587
end
4620
4588
end
4621
- if nstmt < (symlim + 500 ) ÷ 1000
4589
+ if nstmt < (symlim + 500 ) ÷ 100
4622
4590
symlim *= 16
4623
4591
symlim ÷= 1000
4624
- if occurs_more (body, e-> ! inline_ignore (e), symlim) < symlim
4592
+ if occurs_more (body, e -> ! inline_ignore (e), symlim) < symlim
4625
4593
return true
4626
4594
end
4627
4595
end
@@ -5455,6 +5423,8 @@ end
5455
5423
# TODO can probably be removed when we switch to a linear IR
5456
5424
function getfield_elim_pass! (sv:: InferenceState )
5457
5425
body = sv. src. code
5426
+ nssavalues = length (sv. src. ssavaluetypes)
5427
+ sv. ssavalue_defs = find_ssavalue_defs (body, nssavalues)
5458
5428
for i = 1 : length (body)
5459
5429
body[i] = _getfield_elim_pass! (body[i], sv)
5460
5430
end
@@ -5468,11 +5438,18 @@ function _getfield_elim_pass!(e::Expr, sv::InferenceState)
5468
5438
(isa (e. args[3 ],Int) || isa (e. args[3 ],QuoteNode))
5469
5439
e1 = e. args[2 ]
5470
5440
j = e. args[3 ]
5471
- if isa (e1,Expr)
5472
- alloc = is_allocation (e1, sv)
5441
+ single_use = true
5442
+ while isa (e1, SSAValue)
5443
+ single_use = false
5444
+ def = sv. ssavalue_defs[e1. id + 1 ]
5445
+ stmt = sv. src. code[def]:: Expr
5446
+ e1 = stmt. args[2 ]
5447
+ end
5448
+ if isa (e1, Expr)
5449
+ alloc = single_use && is_allocation (e1, sv)
5473
5450
if alloc != = false
5474
5451
flen, fnames = alloc
5475
- if isa (j,QuoteNode)
5452
+ if isa (j, QuoteNode)
5476
5453
j = findfirst (fnames, j. value)
5477
5454
end
5478
5455
if 1 <= j <= flen
@@ -5488,17 +5465,17 @@ function _getfield_elim_pass!(e::Expr, sv::InferenceState)
5488
5465
end
5489
5466
end
5490
5467
end
5491
- elseif isa (e1, GlobalRef) || isa (e1, Symbol) || isa (e1, Slot) || isa (e1, SSAValue)
5468
+ elseif isa (e1, GlobalRef) || isa (e1, Symbol) || isa (e1, Slot)
5492
5469
# non-self-quoting value
5493
5470
else
5494
5471
if isa (e1, QuoteNode)
5495
5472
e1 = e1. value
5496
5473
end
5497
- if isimmutable (e1) || isa (e1,SimpleVector)
5474
+ if isimmutable (e1) || isa (e1, SimpleVector)
5498
5475
# SimpleVector length field is immutable
5499
5476
if isa (j, QuoteNode)
5500
5477
j = j. value
5501
- if ! (isa (j,Int) || isa (j,Symbol))
5478
+ if ! (isa (j, Int) || isa (j, Symbol))
5502
5479
return e
5503
5480
end
5504
5481
end
0 commit comments