Skip to content

Commit 13de082

Browse files
committed
Add a linearization pass in inference to make the AST easier to process
In particular, the features we'd like to take advantage of is mostly a limited variance of uses. This means we can easily 1. Keep track of the expression a value is used. 2. Replace the use. 3. Update the use info without extensive rescanning. This also limit the kind of sideeffect an expression arguments can have. The pass currently may change when `UndefVarError` is raised.
1 parent 2a82415 commit 13de082

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

base/inference.jl

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3568,6 +3568,7 @@ function optimize(me::InferenceState)
35683568
alloc_elim_pass!(opt)
35693569
getfield_elim_pass!(opt)
35703570
copy_duplicated_expr_pass!(opt)
3571+
linearize_pass!(opt)
35713572
# Clean up for `alloc_elim_pass!` and `getfield_elim_pass!`
35723573
void_use_elim_pass!(opt)
35733574
# Pop metadata before label reindexing
@@ -6167,6 +6168,120 @@ function replace_getfield!(e::Expr, tupname, vals, field_names, sv::Optimization
61676168
end
61686169
end
61696170

6171+
function is_ccall_static(e::Expr, sv::OptimizationState)
6172+
if e.head === :call
6173+
is_known_call(e, tuple, sv.src, sv.mod) || return false
6174+
length(e.args) == 3 || return false
6175+
for i in 2:3
6176+
a = e.args[i]
6177+
(isa(a, Expr) || isa(a, Slot) || isa(a, SSAValue)) && return false
6178+
end
6179+
return true
6180+
elseif e.head === :static_parameter
6181+
return true
6182+
end
6183+
return false
6184+
end
6185+
6186+
function linearize_arg!(args, i, stmts, sv::OptimizationState)
6187+
a = args[i]
6188+
if isa(a, Symbol)
6189+
a = a::Symbol
6190+
isdefined(sv.mod, a) && isconst(sv.mod, a) && return
6191+
typ = Any
6192+
elseif isa(a, GlobalRef)
6193+
a = a::GlobalRef
6194+
isdefined(a.mod, a.name) && isconst(a.mod, a.name) && return
6195+
typ = Any
6196+
elseif isa(a, Expr)
6197+
if a.head === :boundscheck
6198+
# Allow `Expr(:boundscheck)` to be nested, this affects DCE
6199+
# TODO: make the DCE/alloc_elim smarter to not need this hack
6200+
return
6201+
end
6202+
typ = (a::Expr).typ
6203+
else
6204+
return
6205+
end
6206+
ssa = newvar!(sv, typ)
6207+
push!(stmts, :($ssa = $a))
6208+
args[i] = ssa
6209+
return
6210+
end
6211+
6212+
# Temporary pass to linearize the IR before `alloc_elim_pass!` before we do so in lowering
6213+
function linearize_pass!(sv::OptimizationState)
6214+
body = sv.src.code
6215+
len = length(body)
6216+
next_i = 1
6217+
stmts = []
6218+
while next_i <= len
6219+
i = next_i
6220+
next_i += 1
6221+
ex = body[i]
6222+
isa(ex, Expr) || continue
6223+
ex = ex::Expr
6224+
head = ex.head
6225+
is_meta_expr_head(head) && continue
6226+
if head === :(=)
6227+
ex = ex.args[2]
6228+
isa(ex, Expr) || continue
6229+
ex = ex::Expr
6230+
head = ex.head
6231+
end
6232+
args = ex.args
6233+
if head === :foreigncall
6234+
if isa(args[1], Expr) && !is_ccall_static(args[1]::Expr, sv)
6235+
linearize_arg!(args, 1, stmts, sv)
6236+
end
6237+
for j in 2:length(args)
6238+
a = args[j]
6239+
isa(a, Expr) || continue
6240+
if a.head === :&
6241+
linearize_arg!(a.args, 1, stmts, sv)
6242+
else
6243+
linearize_arg!(args, j, stmts, sv)
6244+
end
6245+
end
6246+
elseif (head === :import || head === :using || head === :importall || head === :export ||
6247+
head === :isdefined || head === :const || is_meta_expr_head(head))
6248+
continue
6249+
elseif head === :call
6250+
if is_known_call(ex, Intrinsics.llvmcall, sv.src, sv.mod)
6251+
for j in 5:length(args)
6252+
linearize_arg!(args, j, stmts, sv)
6253+
end
6254+
elseif is_known_call(ex, Intrinsics.cglobal, sv.src, sv.mod)
6255+
if isa(args[2], Expr) && !is_ccall_static(args[2]::Expr, sv)
6256+
linearize_arg!(args, 2, stmts, sv)
6257+
end
6258+
for j in 3:length(args)
6259+
linearize_arg!(args, j, stmts, sv)
6260+
end
6261+
else
6262+
for j in 1:length(args)
6263+
linearize_arg!(args, j, stmts, sv)
6264+
end
6265+
end
6266+
else
6267+
for j in 1:length(args)
6268+
if j == 1 && head === :method
6269+
argj = args[j]
6270+
if isa(argj, Slot) || isa(argj, Symbol) || isa(argj, GlobalRef)
6271+
continue
6272+
end
6273+
end
6274+
linearize_arg!(args, j, stmts, sv)
6275+
end
6276+
end
6277+
isempty(stmts) && continue
6278+
next_i = i
6279+
splice!(body, i:(i - 1), stmts)
6280+
len += length(stmts)
6281+
empty!(stmts)
6282+
end
6283+
end
6284+
61706285
const meta_pop_loc = Expr(:meta, :pop_loc)
61716286

61726287
function copy_expr_in_array!(ary, seen)

0 commit comments

Comments
 (0)