Skip to content

Commit 39c278b

Browse files
authored
Canonicalize IR to disallow throwing GlobalRef in value position (#36450)
In anticipation of making the SSA IR more of an interface that packages can use to implement custom compiler transformation, I'd like to do some cleanup first. The is the first of the items on my list: Disallowing GlobalRef in value position if it could potentially throw (because the binding doesn't exist yet). This is done as part of SSA conversion, because we don't know whether a binding will exist during parsing/lowering and we don't modify the IR at all between lowering and the end of type inference, so doing it during SSA conversion is the first possible opportunity. The reason to do this is to simplify transformation passes that want to replace calls with sequences of other instructions. By moving those GlobalRef that could potentially throw into statement position, the order of argument evaluation does not matter (this isn't quite true yet due to static parameters, but I'd like to fix that in a separate commit). I think that's a desirable property to simplify the life os pass authors.
1 parent cd13404 commit 39c278b

File tree

3 files changed

+36
-12
lines changed

3 files changed

+36
-12
lines changed

base/compiler/ssair/slot2ssa.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ function fixemup!(cond, rename, ir::IRCode, ci::CodeInfo, idx::Int, @nospecializ
168168
return nothing
169169
end
170170
op[] = x
171+
elseif isa(val, GlobalRef) && !isdefined(val.mod, val.name)
172+
op[] = NewSSAValue(insert_node!(ir, idx, Any, val).id - length(ir.stmts))
171173
end
172174
end
173175
return urs[]

base/compiler/ssair/verify.jl

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
if !isdefined(@__MODULE__, Symbol("@verify_error"))
44
macro verify_error(arg)
5-
arg isa String && return esc(:(println(stderr, $arg)))
5+
arg isa String && return esc(:(print && println(stderr, $arg)))
66
(arg isa Expr && arg.head === :string) || error("verify_error macro expected a string expression")
77
pushfirst!(arg.args, GlobalRef(Core, :stderr))
88
pushfirst!(arg.args, :println)
@@ -11,7 +11,7 @@ if !isdefined(@__MODULE__, Symbol("@verify_error"))
1111
end
1212
end
1313

14-
function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int)
14+
function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int, print::Bool)
1515
if isa(op, SSAValue)
1616
if op.id > length(ir.stmts)
1717
def_bb = block_for_inst(ir.cfg, ir.new_nodes[op.id - length(ir.stmts)].pos)
@@ -35,6 +35,11 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
3535
error()
3636
end
3737
end
38+
elseif isa(op, GlobalRef)
39+
if !isdefined(op.mod, op.name)
40+
@verify_error "Unbound GlobalRef not allowed in value position"
41+
error()
42+
end
3843
elseif isa(op, Union{OldSSAValue, NewSSAValue})
3944
#@Base.show ir
4045
@verify_error "Left over SSA marker"
@@ -55,7 +60,7 @@ function count_int(val::Int, arr::Vector{Int})
5560
n
5661
end
5762

58-
function verify_ir(ir::IRCode)
63+
function verify_ir(ir::IRCode, print::Bool=true)
5964
# For now require compact IR
6065
# @assert isempty(ir.new_nodes)
6166
# Verify CFG
@@ -169,7 +174,7 @@ function verify_ir(ir::IRCode)
169174
@verify_error "GlobalRefs and Exprs are not allowed as PhiNode values"
170175
error()
171176
end
172-
check_op(ir, domtree, val, edge, last(ir.cfg.blocks[stmt.edges[i]].stmts)+1)
177+
check_op(ir, domtree, val, edge, last(ir.cfg.blocks[stmt.edges[i]].stmts)+1, print)
173178
end
174179
elseif isa(stmt, PhiCNode)
175180
for i = 1:length(stmt.values)
@@ -206,17 +211,18 @@ function verify_ir(ir::IRCode)
206211
end
207212
for op in userefs(stmt)
208213
op = op[]
209-
check_op(ir, domtree, op, bb, idx)
214+
check_op(ir, domtree, op, bb, idx, print)
210215
end
211216
end
212217
end
213218
end
214219

215-
function verify_linetable(linetable::Vector{LineInfoNode})
220+
function verify_linetable(linetable::Vector{LineInfoNode}, print::Bool=true)
216221
for i in 1:length(linetable)
217222
line = linetable[i]
218223
if i <= line.inlined_at
219224
@verify_error "Misordered linetable"
225+
error()
220226
end
221227
end
222228
end

test/compiler/ssair.jl

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,14 @@ let ci = (Meta.@lower 1 + 1).args[1]
147147
Core.PhiNode(),
148148
Core.Compiler.ReturnNode(),
149149
# block 4
150-
Expr(:call,
151-
GlobalRef(Main, :something),
152-
GlobalRef(Main, :somethingelse)),
153-
Core.Compiler.GotoIfNot(Core.SSAValue(6), 9),
150+
GlobalRef(Main, :something),
151+
GlobalRef(Main, :somethingelse),
152+
Expr(:call, Core.SSAValue(6), Core.SSAValue(7)),
153+
Core.Compiler.GotoIfNot(Core.SSAValue(8), 11),
154154
# block 5
155-
Core.Compiler.ReturnNode(Core.SSAValue(6)),
155+
Core.Compiler.ReturnNode(Core.SSAValue(8)),
156156
# block 6
157-
Core.Compiler.ReturnNode(Core.SSAValue(6))
157+
Core.Compiler.ReturnNode(Core.SSAValue(8))
158158
]
159159
nstmts = length(ci.code)
160160
ci.ssavaluetypes = nstmts
@@ -164,3 +164,19 @@ let ci = (Meta.@lower 1 + 1).args[1]
164164
ir = Core.Compiler.compact!(ir, true)
165165
@test Core.Compiler.verify_ir(ir) == nothing
166166
end
167+
168+
# Test that GlobalRef in value position is non-canonical
169+
using Base.Meta
170+
let ci = (Meta.@lower 1 + 1).args[1]
171+
ci.code = [
172+
Expr(:call, GlobalRef(Main, :something_not_defined_please))
173+
ReturnNode(SSAValue(1))
174+
]
175+
nstmts = length(ci.code)
176+
ci.ssavaluetypes = nstmts
177+
ci.codelocs = fill(Int32(1), nstmts)
178+
ci.ssaflags = fill(Int32(0), nstmts)
179+
ir = Core.Compiler.inflate_ir(ci)
180+
ir = Core.Compiler.compact!(ir, true)
181+
@test_throws ErrorException Core.Compiler.verify_ir(ir, false)
182+
end

0 commit comments

Comments
 (0)