@@ -1436,7 +1436,7 @@ function typeinf(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector, def, cop
1436
1436
end
1437
1437
end
1438
1438
# TODO : typeinf currently gets stuck without this
1439
- if linfo. name === :abstract_interpret || linfo. name === :tuple_elim_pass || linfo. name === :abstract_call_gf
1439
+ if linfo. name === :abstract_interpret || linfo. name === :alloc_elim_pass || linfo. name === :abstract_call_gf
1440
1440
return (linfo. ast, Any)
1441
1441
end
1442
1442
@@ -1861,7 +1861,7 @@ function typeinf_uncached(linfo::LambdaInfo, atypes::ANY, sparams::SimpleVector,
1861
1861
sv. vars = append_any (f_argnames (fulltree), map (vi-> vi[1 ], fulltree. args[2 ][1 ]))
1862
1862
inbounds_meta_elim_pass (fulltree. args[3 ])
1863
1863
end
1864
- tuple_elim_pass (fulltree, sv)
1864
+ alloc_elim_pass (fulltree, sv)
1865
1865
getfield_elim_pass (fulltree. args[3 ], sv)
1866
1866
end
1867
1867
linfo. inferred = true
@@ -2429,15 +2429,15 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atype::ANY, sv::VarInfo, enclosing
2429
2429
vaname = args[na]
2430
2430
len_argexprs = length (argexprs)
2431
2431
valen = len_argexprs- na+ 1
2432
- if valen> 0 && ! occurs_outside_getfield (body, vaname, sv, valen)
2432
+ if valen> 0 && ! occurs_outside_getfield (body, vaname, sv, valen, () )
2433
2433
# argument tuple is not used as a whole, so convert function body
2434
2434
# to one accepting the exact number of arguments we have.
2435
2435
newnames = unique_names (ast,valen)
2436
2436
if needcopy
2437
2437
body = astcopy (body)
2438
2438
needcopy = false
2439
2439
end
2440
- replace_getfield! (ast, body, vaname, newnames, sv, 1 )
2440
+ replace_getfield! (ast, body, vaname, newnames, (), sv, 1 )
2441
2441
args = vcat (args[1 : na- 1 ], newnames)
2442
2442
na = length (args)
2443
2443
@@ -3190,28 +3190,27 @@ symequal(x::Symbol , y::SymbolNode) = is(x,y.name)
3190
3190
symequal (x:: GenSym , y:: GenSym ) = is (x. id,y. id)
3191
3191
symequal (x:: ANY , y:: ANY ) = is (x,y)
3192
3192
3193
- function occurs_outside_getfield (e:: ANY , sym:: ANY , sv:: VarInfo , tuplen :: Int )
3193
+ function occurs_outside_getfield (e:: ANY , sym:: ANY , sv:: VarInfo , field_count, field_names )
3194
3194
if is (e, sym) || (isa (e, SymbolNode) && is (e. name, sym))
3195
3195
return true
3196
3196
end
3197
3197
if isa (e,Expr)
3198
3198
e = e:: Expr
3199
3199
if is_known_call (e, getfield, sv) && symequal (e. args[2 ],sym)
3200
- targ = e. args[2 ]
3201
- if ! (exprtype (targ,sv) <: Tuple )
3202
- return true
3203
- end
3204
3200
idx = e. args[3 ]
3205
- if ! isa (idx,Int) || ! ( 1 <= idx <= tuplen )
3206
- return true
3201
+ if isa (idx,QuoteNode) && ( idx. value in field_names )
3202
+ return false
3207
3203
end
3208
- return false
3204
+ if isa (idx,Int) && (1 <= idx <= field_count)
3205
+ return false
3206
+ end
3207
+ return true
3209
3208
end
3210
3209
if is (e. head,:(= ))
3211
- return occurs_outside_getfield (e. args[2 ], sym, sv, tuplen )
3210
+ return occurs_outside_getfield (e. args[2 ], sym, sv, field_count, field_names )
3212
3211
else
3213
3212
for a in e. args
3214
- if occurs_outside_getfield (a, sym, sv, tuplen )
3213
+ if occurs_outside_getfield (a, sym, sv, field_count, field_names )
3215
3214
return true
3216
3215
end
3217
3216
end
@@ -3230,46 +3229,80 @@ function inbounds_meta_elim_pass(e::Expr)
3230
3229
end
3231
3230
end
3232
3231
3233
- # replace getfield(tuple(exprs...), i) with exprs[i]
3232
+ # does the same job as alloc_elim_pass for allocations inline in getfields
3233
+ # TODO can probably be removed when we switch to a linear IR
3234
3234
function getfield_elim_pass (e:: Expr , sv)
3235
3235
for i = 1 : length (e. args)
3236
3236
ei = e. args[i]
3237
3237
if isa (ei,Expr)
3238
3238
getfield_elim_pass (ei, sv)
3239
3239
if is_known_call (ei, getfield, sv) && length (ei. args)== 3 &&
3240
- isa (ei. args[3 ],Int)
3240
+ ( isa (ei. args[3 ],Int) || isa (ei . args[ 3 ],QuoteNode) )
3241
3241
e1 = ei. args[2 ]
3242
3242
j = ei. args[3 ]
3243
3243
if isa (e1,Expr)
3244
- if is_known_call (e1, tuple, sv) && (1 <= j < length (e1. args))
3245
- ok = true
3246
- for k = 2 : length (e1. args)
3247
- k == j+ 1 && continue
3248
- if ! effect_free (e1. args[k], sv, true )
3249
- ok = false ; break
3250
- end
3244
+ alloc = is_immutable_allocation (e1, sv)
3245
+ if ! is (alloc, false )
3246
+ flen, fnames = alloc
3247
+ if isa (j,QuoteNode)
3248
+ j = findfirst (fnames, j. value)
3251
3249
end
3252
- if ok
3253
- e. args[i] = e1. args[j+ 1 ]
3250
+ if 1 <= j <= flen
3251
+ ok = true
3252
+ for k = 2 : length (e1. args)
3253
+ k == j+ 1 && continue
3254
+ if ! effect_free (e1. args[k], sv, true )
3255
+ ok = false ; break
3256
+ end
3257
+ end
3258
+ if ok
3259
+ e. args[i] = e1. args[j+ 1 ]
3260
+ end
3254
3261
end
3255
3262
end
3256
- elseif isa (e1,Tuple) && (1 <= j <= length (e1))
3263
+ elseif isa (e1,Tuple) && isa (j,Int) && (1 <= j <= length (e1))
3257
3264
e1j = e1[j]
3258
3265
if ! (isa (e1j,Number) || isa (e1j,AbstractString) || isa (e1j,Tuple) ||
3259
3266
isa (e1j,Type))
3260
3267
e1j = QuoteNode (e1j)
3261
3268
end
3262
3269
e. args[i] = e1j
3263
- elseif isa (e1,QuoteNode) && isa (e1. value,Tuple) && (1 <= j <= length (e1. value))
3270
+ elseif isa (e1,QuoteNode) && isa (e1. value,Tuple) && isa (j,Int) && (1 <= j <= length (e1. value))
3264
3271
e. args[i] = QuoteNode (e1. value[j])
3265
3272
end
3266
3273
end
3267
3274
end
3268
3275
end
3269
3276
end
3270
3277
3271
- # eliminate allocation of unnecessary tuples
3272
- function tuple_elim_pass (ast:: Expr , sv:: VarInfo )
3278
+ # check if e is a successful allocation of an immutable struct
3279
+ # if it is, returns (n,f) such that it is always valid to call
3280
+ # getfield(..., 1 <= x <= n) or getfield(..., x in f) on the result
3281
+ function is_immutable_allocation (e :: ANY , sv:: VarInfo )
3282
+ isa (e, Expr) || return false
3283
+ if is_known_call (e, tuple, sv)
3284
+ return (length (e. args)- 1 ,())
3285
+ elseif e. head === :new
3286
+ typ = exprtype (e, sv)
3287
+ if isleaftype (typ) && ! typ. mutable
3288
+ @assert (isa (typ,DataType))
3289
+ nf = length (e. args)- 1
3290
+ names = fieldnames (typ)
3291
+ @assert (nf <= nfields (typ))
3292
+ if nf < nfields (typ)
3293
+ # some fields were left undef
3294
+ # we could potentially propagate Bottom
3295
+ # for pointer fields
3296
+ names = names[1 : nf]
3297
+ end
3298
+ return (nf, names)
3299
+ end
3300
+ end
3301
+ false
3302
+ end
3303
+ # eliminate allocation of unnecessary immutables
3304
+ # that are only used as arguments to safe getfield calls
3305
+ function alloc_elim_pass (ast:: Expr , sv:: VarInfo )
3273
3306
bexpr = ast. args[3 ]:: Expr
3274
3307
body = (ast. args[3 ]. args):: Array{Any,1}
3275
3308
vs = find_sa_vars (ast)
@@ -3283,10 +3316,11 @@ function tuple_elim_pass(ast::Expr, sv::VarInfo)
3283
3316
end
3284
3317
var = e. args[1 ]
3285
3318
rhs = e. args[2 ]
3286
- if isa (rhs,Expr) && is_known_call (rhs, tuple, sv)
3319
+ alloc = is_immutable_allocation (rhs, sv)
3320
+ if ! is (alloc,false )
3321
+ nv, field_names = alloc
3287
3322
tup = rhs. args
3288
- nv = length (tup)- 1
3289
- if occurs_outside_getfield (bexpr, var, sv, nv) || ! is_local (sv, var)
3323
+ if occurs_outside_getfield (bexpr, var, sv, nv, field_names) || ! is_local (sv, var)
3290
3324
i += 1
3291
3325
continue
3292
3326
end
@@ -3309,22 +3343,29 @@ function tuple_elim_pass(ast::Expr, sv::VarInfo)
3309
3343
end
3310
3344
end
3311
3345
i += n_ins
3312
- replace_getfield! (ast, bexpr, var, vals, sv, i)
3346
+ replace_getfield! (ast, bexpr, var, vals, field_names, sv, i)
3313
3347
else
3314
3348
i += 1
3315
3349
end
3316
3350
end
3317
3351
end
3318
3352
3319
- function replace_getfield! (ast, e:: ANY , tupname, vals, sv, i0)
3353
+ function replace_getfield! (ast, e:: ANY , tupname, vals, field_names, sv, i0)
3320
3354
if ! isa (e,Expr)
3321
3355
return
3322
3356
end
3323
3357
for i = i0: length (e. args)
3324
3358
a = e. args[i]
3325
3359
if isa (a,Expr) && is_known_call (a, getfield, sv) &&
3326
3360
symequal (a. args[2 ],tupname)
3327
- val = vals[a. args[3 ]]
3361
+ idx = if isa (a. args[3 ], Int)
3362
+ a. args[3 ]
3363
+ else
3364
+ @assert isa (a. args[3 ], QuoteNode)
3365
+ findfirst (field_names, a. args[3 ]. value)
3366
+ end
3367
+ @assert (idx > 0 ) # clients should check that all getfields are valid
3368
+ val = vals[idx]
3328
3369
# original expression might have better type info than
3329
3370
# the tuple element expression that's replacing it.
3330
3371
if isa (val,SymbolNode)
@@ -3347,7 +3388,7 @@ function replace_getfield!(ast, e::ANY, tupname, vals, sv, i0)
3347
3388
end
3348
3389
e. args[i] = val
3349
3390
else
3350
- replace_getfield! (ast, a, tupname, vals, sv, 1 )
3391
+ replace_getfield! (ast, a, tupname, vals, field_names, sv, 1 )
3351
3392
end
3352
3393
end
3353
3394
end
0 commit comments