Skip to content

Fix #41975 - Dropped typecheck in GotoIfNot #41994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,10 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
end
condval = maybe_extract_const_bool(condt)
l = stmt.dest::Int
if l == 0
# Bool typecheck only - not control flow effect
break
end
if !isempty(frame.pclimitations)
# we can't model the possible effect of control
# dependencies on the return value, so we propagate it
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ function statement_or_branch_cost(@nospecialize(stmt), line::Int, src::Union{Cod
# summing the cost of the not-taken branch
thiscost = dst(stmt.label) < line ? 40 : 0
elseif stmt isa GotoIfNot
thiscost = dst(stmt.dest) < line ? 40 : 0
thiscost = (stmt.dest != 0 && dst(stmt.dest) < line) ? 40 : 0
end
return thiscost
end
Expand Down Expand Up @@ -619,7 +619,7 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab
if isa(cond, SSAValue)
cond = SSAValue(cond.id + ssachangemap[cond.id])
end
body[i] = GotoIfNot(cond, el.dest + labelchangemap[el.dest])
body[i] = GotoIfNot(cond, el.dest == 0 ? 0 : el.dest + labelchangemap[el.dest])
elseif isa(el, ReturnNode)
if isdefined(el, :val)
val = el.val
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
elseif isa(stmt′, Expr) && stmt′.head === :enter
stmt′ = Expr(:enter, stmt′.args[1]::Int + bb_offset)
elseif isa(stmt′, GotoIfNot)
stmt′ = GotoIfNot(stmt′.cond, stmt′.dest + bb_offset)
stmt′ = GotoIfNot(stmt′.cond, stmt′.dest == 0 ? 0 : stmt′.dest + bb_offset)
elseif isa(stmt′, PhiNode)
stmt′ = PhiNode(Int32[edge+bb_offset for edge in stmt′.edges], stmt′.values)
end
Expand Down Expand Up @@ -588,7 +588,7 @@ function batch_inline!(todo::Vector{Pair{Int, Any}}, ir::IRCode, linetable::Vect
elseif isa(stmt, Expr) && stmt.head === :enter
compact[idx] = Expr(:enter, state.bb_rename[stmt.args[1]::Int])
elseif isa(stmt, GotoIfNot)
compact[idx] = GotoIfNot(stmt.cond, state.bb_rename[stmt.dest])
compact[idx] = GotoIfNot(stmt.cond, stmt.dest == 0 ? 0 : state.bb_rename[stmt.dest])
elseif isa(stmt, PhiNode)
compact[idx] = PhiNode(Int32[edge == length(state.bb_rename) ? length(state.new_cfg_blocks) : state.bb_rename[edge+1]-1 for edge in stmt.edges], stmt.values)
end
Expand Down
12 changes: 7 additions & 5 deletions base/compiler/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function basic_blocks_starts(stmts::Vector{Any})
for idx in 1:length(stmts)
stmt = stmts[idx]
# Terminators
if isa(stmt, GotoIfNot)
if isa(stmt, GotoIfNot) && stmt.dest != 0
push!(jump_dests, idx+1)
push!(jump_dests, stmt.dest)
elseif isa(stmt, ReturnNode)
Expand Down Expand Up @@ -106,7 +106,7 @@ function compute_basic_blocks(stmts::Vector{Any})
continue
end
# Conditional Branch
if isa(terminator, GotoIfNot)
if isa(terminator, GotoIfNot) && terminator.dest != 0
block′ = block_for_inst(basic_block_index, terminator.dest)
if block′ == num + 1
# This GotoIfNot acts like a noop - treat it as such.
Expand Down Expand Up @@ -1012,17 +1012,19 @@ function process_node!(compact::IncrementalCompact, result_idx::Int, inst::Instr
result[result_idx][:inst] = stmt
cond = stmt.cond
if isa(cond, Bool) && compact.fold_constant_branches
if cond
if cond || stmt.dest == 0
result[result_idx][:inst] = nothing
kill_edge!(compact, active_bb, active_bb, stmt.dest)
if stmt.dest != 0
kill_edge!(compact, active_bb, active_bb, stmt.dest)
end
# Don't increment result_idx => Drop this statement
else
result[result_idx][:inst] = GotoNode(compact.bb_rename_succ[stmt.dest])
kill_edge!(compact, active_bb, active_bb, active_bb+1)
result_idx += 1
end
else
result[result_idx][:inst] = GotoIfNot(cond, compact.bb_rename_succ[stmt.dest])
result[result_idx][:inst] = GotoIfNot(cond, stmt.dest == 0 ? 0 : compact.bb_rename_succ[stmt.dest])
result_idx += 1
end
elseif isa(stmt, Expr)
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/ssair/legacy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any})
if isa(stmt, GotoNode)
code[i] = GotoNode(block_for_inst(cfg, stmt.label))
elseif isa(stmt, GotoIfNot)
code[i] = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest))
code[i] = GotoIfNot(stmt.cond, stmt.dest == 0 ? 0 : block_for_inst(cfg, stmt.dest))
elseif isa(stmt, PhiNode)
code[i] = PhiNode(Int32[block_for_inst(cfg, Int(edge)) for edge in stmt.edges], stmt.values)
elseif isa(stmt, Expr) && stmt.head === :enter
Expand Down Expand Up @@ -57,7 +57,7 @@ function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int)
if isa(stmt, GotoNode)
stmt = GotoNode(first(ir.cfg.blocks[stmt.label].stmts))
elseif isa(stmt, GotoIfNot)
stmt = GotoIfNot(stmt.cond, first(ir.cfg.blocks[stmt.dest].stmts))
stmt = GotoIfNot(stmt.cond, stmt.dest == 0 ? 0 : first(ir.cfg.blocks[stmt.dest].stmts))
elseif isa(stmt, PhiNode)
stmt = PhiNode(Int32[last(ir.cfg.blocks[edge].stmts) for edge in stmt.edges], stmt.values)
elseif isa(stmt, Expr) && stmt.head === :enter
Expand Down
4 changes: 3 additions & 1 deletion base/compiler/ssair/queries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ function stmt_effect_free(@nospecialize(stmt), @nospecialize(rt), src, sptypes::
isa(stmt, PhiNode) && return true
isa(stmt, ReturnNode) && return false
isa(stmt, GotoNode) && return false
isa(stmt, GotoIfNot) && return false
if isa(stmt, GotoIfNot)
return stmt.dest == 0 && argextype(stmt.cond, src, sptypes) ⊑ Bool
end
isa(stmt, Slot) && return false # Slots shouldn't occur in the IR at this point, but let's be defensive here
isa(stmt, GlobalRef) && return isdefined(stmt.mod, stmt.name)
if isa(stmt, Expr)
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ end

show_unquoted(io::IO, stmt::GotoIfNot, indent::Int, ::Int) = show_unquoted_gotoifnot(io, stmt, indent, "%")
function show_unquoted_gotoifnot(io::IO, stmt::GotoIfNot, indent::Int, prefix::String)
print(io, "goto ", prefix, stmt.dest, " if not ")
print(io, "goto ", prefix, stmt.dest == 0 ? "fallthrough" : stmt.dest, " if not ")
show_unquoted(io, stmt.cond, indent)
end

Expand Down Expand Up @@ -536,7 +536,7 @@ function statement_indices_to_labels(stmt, cfg::CFG)
stmt = Expr(:enter, block_for_inst(cfg, stmt.args[1]::Int))
end
elseif isa(stmt, GotoIfNot)
stmt = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest))
stmt = GotoIfNot(stmt.cond, stmt.dest == 0 ? 0 : block_for_inst(cfg, stmt.dest))
elseif stmt isa GotoNode
stmt = GotoNode(block_for_inst(cfg, stmt.label))
elseif stmt isa PhiNode
Expand Down
11 changes: 7 additions & 4 deletions base/compiler/ssair/slot2ssa.jl
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree)
node = result[nidx]
node[:inst], node[:type], node[:line] = GotoNode(bb_rename[bb + 1]), Any, 0
end
result[inst_range[end]][:inst] = GotoIfNot(terminator.cond, bb_rename[terminator.dest])
result[inst_range[end]][:inst] = GotoIfNot(terminator.cond, terminator.dest == 0 ? 0 : bb_rename[terminator.dest])
elseif !isa(terminator, ReturnNode)
if isa(terminator, Expr)
if terminator.head == :enter
Expand Down Expand Up @@ -821,11 +821,14 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse,
# Convert GotoNode/GotoIfNot/PhiNode to BB addressing
if isa(stmt, GotoNode)
new_code[idx] = GotoNode(block_for_inst(cfg, stmt.label))
elseif isa(stmt, GotoIfNot)
elseif isa(stmt, GotoIfNot) && stmt.dest != 0
new_dest = block_for_inst(cfg, stmt.dest)
if new_dest == bb+1
# Drop this node - it's a noop
new_code[idx] = stmt.cond
# We need to keep this node for the bool typecheck
# (until we can prove that it doesn't throw at least),
# but we null the destination and don't record a separate
# edge to keep the unique edge invariant.
new_code[idx] = GotoIfNot(stmt.cond, 0)
else
new_code[idx] = GotoIfNot(stmt.cond, new_dest)
end
Expand Down
11 changes: 10 additions & 1 deletion base/compiler/ssair/verify.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,16 @@ function verify_ir(ir::IRCode, print::Bool=true)
@verify_error "Block $idx terminator forms a double edge to block $(idx+1)"
error("")
end
if length(block.succs) != 2 || (block.succs != [terminator.dest, idx+1] && block.succs != [idx+1, terminator.dest])
if length(block.succs) == 1
if block.dest != 0
@verify_error "Block $idx terminator has non-zero dest but only one successor"
error("")
end
if block.succs[1] != idx + 1
@verify_error "Block $idx successors ($(block.succs)), does not match GotoIfNot fallthrough"
error("")
end
elseif length(block.succs) != 2 || (block.succs != [terminator.dest, idx+1] && block.succs != [idx+1, terminator.dest])
@verify_error "Block $idx successors ($(block.succs)), does not match GotoIfNot terminator"
error("")
end
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -637,9 +637,9 @@ function type_annotate!(sv::InferenceState, run_optimizer::Bool)
# replace GotoIfNot with its condition if the branch target is unreachable
for i = 1:nexpr
expr = body[i]
if isa(expr, GotoIfNot)
if isa(expr, GotoIfNot) && expr.dest != 0
if !isa(states[expr.dest], VarTable)
body[i] = expr.cond
body[i] = GotoIfNot(expr.cond, 0)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,10 @@ function find_throw_blocks(code::Vector{Any}, ir = RefValue{IRCode}())
elseif isa(s, GotoIfNot)
if i+1 in stmts
tgt = s.dest::Int
if isassigned(ir)
if isassigned(ir) && tgt != 0
tgt = first(ir[].cfg.blocks[tgt].stmts)
end
if tgt in stmts
if tgt == 0 || tgt in stmts
push!(stmts, i)
end
end
Expand Down
2 changes: 1 addition & 1 deletion base/meta.jl
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any},
return Core.GotoIfNot(
_partially_inline!(x.cond, slot_replacements, type_signature, static_param_values,
slot_offset, statement_offset, boundscheck),
x.dest + statement_offset,
x.dest == 0 ? 0 : x.dest + statement_offset,
)
end
if isa(x, Expr)
Expand Down
19 changes: 12 additions & 7 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7002,7 +7002,8 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
jl_value_t *stmt = jl_array_ptr_ref(stmts, i);
if (jl_is_gotoifnot(stmt)) {
int dest = jl_gotoifnot_label(stmt);
branch_targets.insert(dest);
if (dest != 0)
branch_targets.insert(dest);
// The next 1-indexed statement
branch_targets.insert(i + 2);
} else if (jl_is_returnnode(stmt)) {
Expand Down Expand Up @@ -7190,13 +7191,17 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
Value *isfalse = emit_condition(ctx, cond, "if");
mallocVisitStmt(debuginfoloc, nullptr);
come_from_bb[cursor+1] = ctx.builder.GetInsertBlock();
workstack.push_back(lname - 1);
BasicBlock *ifnot = BB[lname];
BasicBlock *ifso = BB[cursor+2];
if (ifnot == ifso)
ctx.builder.CreateBr(ifnot);
else
ctx.builder.CreateCondBr(isfalse, ifnot, ifso);
if (lname == 0) {
ctx.builder.CreateBr(ifso);
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else {
}
else {

workstack.push_back(lname - 1);
BasicBlock *ifnot = BB[lname];
if (ifnot == ifso)
ctx.builder.CreateBr(ifnot);
else
ctx.builder.CreateCondBr(isfalse, ifnot, ifso);
}
find_next_stmt(cursor + 1);
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip,
}
else if (jl_is_gotoifnot(stmt)) {
jl_value_t *cond = eval_value(jl_gotoifnot_cond(stmt), s);
if (cond == jl_false) {
if (cond == jl_false && jl_gotoifnot_label(stmt) != 0) {
next_ip = jl_gotoifnot_label(stmt) - 1;
}
else if (cond != jl_true) {
Expand Down
2 changes: 1 addition & 1 deletion src/toplevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ static void body_attributes(jl_array_t *body, int *has_intrinsics, int *has_defs
*has_loops = 1;
}
else if (jl_is_gotoifnot(stmt)) {
if (jl_gotoifnot_label(stmt) <= i)
if (jl_gotoifnot_label(stmt) != 0 && jl_gotoifnot_label(stmt) <= i)
*has_loops = 1;
}
}
Expand Down
4 changes: 4 additions & 0 deletions test/compiler/ssair.jl
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,7 @@ let cfg = CFG(BasicBlock[
Compiler.domtree_insert_edge!(domtree, cfg.blocks, 1, 3)
@test domtree.idoms_bb == Compiler.naive_idoms(cfg.blocks) == [0, 1, 1, 3, 1, 4]
end

# Issue #41975 - SSA conversion drops type check
f_if_typecheck() = (if nothing; end; unsafe_load(Ptr{Int}(0)))
@test_throws TypeError f_if_typecheck()