Skip to content

Commit 29d87ec

Browse files
committed
Allow splitting a basic block before a PHI node
`split_block!` and the conditional splitting functions rejected a PHI node as the split point, while LLVM accepts it and leaves the PHI nodes that move to the tail for the caller to fix. Julia's LowerPTLS pass relies on that: it creates an empty PHI node, splits the block before it, and adds the incoming values from the new blocks afterwards. Accept PHI nodes, and document what the caller has to fix; exception-handling pads remain rejected, as no fix can make splitting before them valid.
1 parent 5556c32 commit 29d87ec

2 files changed

Lines changed: 75 additions & 12 deletions

File tree

‎src/analysis.jl‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,11 @@ name: ; the returned block, with the rest of the orig
148148
149149
Returns the new block. The PHI nodes in the successors of the original block are updated
150150
to refer to the new block. The block must have a terminator, and the split point must not be
151-
at the end of the block, before a PHI node or before an exception-handling pad. Debug
152-
records at the split point move along when the insertion point is at their head (e.g.,
153-
`LLVM.after(prev)`), and stay in the original block otherwise (e.g., `LLVM.before(inst)`).
151+
at the end of the block or before an exception-handling pad. It can be before a PHI node, as
152+
in LLVM, but the PHI nodes that move to the new block are not updated: their only
153+
predecessor is the original block now, so the caller has to fix them. Debug records at the
154+
split point move along when the insertion point is at their head (e.g., `LLVM.after(prev)`),
155+
and stay in the original block otherwise (e.g., `LLVM.before(inst)`).
154156
155157
Analyses, like the dominator tree, are not updated.
156158
@@ -162,9 +164,9 @@ function split_block!(pos::InsertionPoint{Instruction}, name::AbstractString="")
162164
throw(ArgumentError("Cannot split a basic block without a terminator"))
163165
pos.anchor == C_NULL &&
164166
throw(ArgumentError("Cannot split a basic block at its end"))
165-
Instruction(pos.anchor) isa Union{PHIInst,LandingPadInst,CatchPadInst,CleanupPadInst,
167+
Instruction(pos.anchor) isa Union{LandingPadInst,CatchPadInst,CleanupPadInst,
166168
CatchSwitchInst} &&
167-
throw(ArgumentError("Cannot split a basic block before a PHI node or an exception-handling pad"))
169+
throw(ArgumentError("Cannot split a basic block before an exception-handling pad"))
168170
BasicBlock(API.LLVMExtraSplitBasicBlock(bb, pos.anchor, pos.head, name))
169171
end
170172
split_block!(inst::Instruction, name::AbstractString="") = split_block!(before(inst), name)
@@ -187,8 +189,10 @@ tail: ; `at` and the rest of the original block
187189
```
188190
189191
Returns the terminator of the new block, before which instructions can be inserted. The
190-
instruction `at` must not be a PHI node or an exception-handling pad. The new branches get
191-
the debug location of `at`.
192+
instruction `at` must not be an exception-handling pad. It can be a PHI node, as in LLVM,
193+
but the PHI nodes in the tail are not updated for its new predecessors, so the caller has
194+
to fix them (e.g., by creating a PHI node first, to split before it and add its incoming
195+
values afterwards). The new branches get the debug location of `at`.
192196
193197
The `weights` of the conditional branch, a pair of integers for when `cond` is true and
194198
when it is false (e.g., `(1, 9)` when the new block is rarely executed), are attached to it
@@ -264,8 +268,8 @@ function check_conditional_split(cond::Value, at::Instruction)
264268
bb === nothing && throw(ArgumentError("Instruction is not part of a basic block"))
265269
terminator(bb) === nothing &&
266270
throw(ArgumentError("Cannot split a basic block without a terminator"))
267-
at isa Union{PHIInst,LandingPadInst,CatchPadInst,CleanupPadInst,CatchSwitchInst} &&
268-
throw(ArgumentError("Cannot split a basic block before a PHI node or an exception-handling pad"))
271+
at isa Union{LandingPadInst,CatchPadInst,CleanupPadInst,CatchSwitchInst} &&
272+
throw(ArgumentError("Cannot split a basic block before an exception-handling pad"))
269273
return
270274
end
271275

‎test/analysis.jl‎

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,6 @@ end
599599
end
600600

601601
@test_throws ArgumentError split_block_and_insert_if_then!(f.parameters[1], d)
602-
@test_throws ArgumentError split_block_and_insert_if_then!(
603-
ConstantInt(true), first(exit.instructions))
604602
end
605603

606604
# the dominator tree must be one of the function that is changed
@@ -733,6 +731,60 @@ end
733731
@test_throws ArgumentError split_block_and_insert_if_then_else!(c, d; weights=(1,))
734732
end
735733

734+
# before a PHI node, created first to be given its incoming values afterwards (like
735+
# Julia's LowerPTLS pass does), or before an exception-handling pad, which is invalid
736+
@dispose ctx=Context() builder=IRBuilder() begin
737+
pers = supports_typed_pointers(ctx) ? "i8* bitcast (i32 (...)* @pers to i8*)" :
738+
"ptr @pers"
739+
lp = supports_typed_pointers(ctx) ? "{ i8*, i32 }" : "{ ptr, i32 }"
740+
mod = parse(LLVM.Module, """
741+
declare void @g()
742+
declare i32 @pers(...)
743+
744+
define i32 @f(i32 %x, i1 %c) personality $pers {
745+
entry:
746+
%a = add i32 %x, 1
747+
invoke void @g() to label %exit unwind label %pad
748+
exit:
749+
ret i32 %a
750+
pad:
751+
%lp = landingpad $lp cleanup
752+
ret i32 0
753+
}""")
754+
f = mod.functions["f"]
755+
entry, exit, pad = f.blocks
756+
a = first(entry.instructions)
757+
ret = exit.terminator
758+
c = f.parameters[2]
759+
position!(builder, LLVM.before(ret))
760+
p = phi!(builder, LLVM.Int32Type())
761+
replace_uses!(a, p)
762+
then_term, else_term = split_block_and_insert_if_then_else!(c, p)
763+
@test p.parent == only(then_term.successors)
764+
push!(p.incoming, (a, then_term.parent))
765+
push!(p.incoming, (ConstantInt(Int32(0)), else_term.parent))
766+
@test ret.operands[1] == p
767+
verify(f)
768+
769+
# the same with a single new block
770+
position!(builder, LLVM.before(ret))
771+
q = phi!(builder, LLVM.Int32Type())
772+
replace_uses!(p, q)
773+
head = p.parent
774+
term = split_block_and_insert_if_then!(c, q)
775+
@test q.parent == only(term.successors)
776+
push!(q.incoming, (ConstantInt(Int32(1)), term.parent))
777+
push!(q.incoming, (p, head))
778+
@test ret.operands[1] == q
779+
verify(f)
780+
781+
@test_throws ArgumentError split_block_and_insert_if_then_else!(
782+
c, first(pad.instructions))
783+
@test_throws ArgumentError split_block_and_insert_if_then!(c, first(pad.instructions))
784+
@test_throws ArgumentError split_block!(first(pad.instructions))
785+
dispose(mod)
786+
end
787+
736788
# the dominator tree must be one of the function that is changed
737789
@dispose ctx=Context() mod=parse(LLVM.Module, """
738790
define void @f(i1 %c) {
@@ -1015,8 +1067,15 @@ end
10151067
@test only(tail2.instructions) == br
10161068
verify(f)
10171069

1070+
# before a PHI node, which moves to the new block without being updated
1071+
tail3 = split_block!(r)
1072+
@test collect(tail3.instructions)[1] == r
1073+
@test only(exit.terminator.successors) == tail3
1074+
@test verification_error(f) !== nothing
1075+
move!(r, LLVM.before(exit.terminator))
1076+
verify(f)
1077+
10181078
@test_throws ArgumentError split_block!(LLVM.at_end(tail2))
1019-
@test_throws ArgumentError split_block!(r)
10201079
detached = BasicBlock("detached")
10211080
@test_throws ArgumentError split_block!(LLVM.at_begin(detached))
10221081
erase!(detached)

0 commit comments

Comments
 (0)