mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-03 09:21:13 +00:00
[PlaceSafepoints] Stop special casing some intrinsics
We were special casing a handful of intrinsics as not needing a safepoint before them. After running into another valid case - memset - I took a closer look and realized that almost no intrinsics need to have a safepoint poll before them. Restructure the code to make that apparent so that we stop hitting these bugs. The only intrinsics which need a safepoint poll before them are ones which can run arbitrary code. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@237744 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
4bc9d4ea26
commit
a5703bc52e
@ -381,6 +381,32 @@ bool PlaceBackedgeSafepointsImpl::runOnLoop(Loop *L) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if an entry safepoint is not required before this callsite in
|
||||
/// the caller function.
|
||||
static bool doesNotRequireEntrySafepointBefore(const CallSite &CS) {
|
||||
Instruction *Inst = CS.getInstruction();
|
||||
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst)) {
|
||||
switch (II->getIntrinsicID()) {
|
||||
case Intrinsic::experimental_gc_statepoint:
|
||||
case Intrinsic::experimental_patchpoint_void:
|
||||
case Intrinsic::experimental_patchpoint_i64:
|
||||
// The can wrap an actual call which may grow the stack by an unbounded
|
||||
// amount or run forever.
|
||||
return false;
|
||||
default:
|
||||
// Most LLVM intrinsics are things which do not expand to actual calls, or
|
||||
// at least if they do, are leaf functions that cause only finite stack
|
||||
// growth. In particular, the optimizer likes to form things like memsets
|
||||
// out of stores in the original IR. Another important example is
|
||||
// llvm.frameescape which must occur in the entry block. Inserting a
|
||||
// safepoint before it is not legal since it could push the frameescape
|
||||
// out of the entry block.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Instruction *findLocationForEntrySafepoint(Function &F,
|
||||
DominatorTree &DT) {
|
||||
|
||||
@ -421,23 +447,16 @@ static Instruction *findLocationForEntrySafepoint(Function &F,
|
||||
for (cursor = F.getEntryBlock().begin(); hasNextInstruction(cursor);
|
||||
cursor = nextInstruction(cursor)) {
|
||||
|
||||
// We need to stop going forward as soon as we see a call that can
|
||||
// grow the stack (i.e. the call target has a non-zero frame
|
||||
// size).
|
||||
if (CallSite(cursor)) {
|
||||
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(cursor)) {
|
||||
// llvm.assume(...) are not really calls.
|
||||
if (II->getIntrinsicID() == Intrinsic::assume) {
|
||||
continue;
|
||||
}
|
||||
// llvm.frameescape() intrinsic is not a real call. The intrinsic can
|
||||
// exist only in the entry block.
|
||||
// Inserting a statepoint before llvm.frameescape() may split the
|
||||
// entry block, and push the intrinsic out of the entry block.
|
||||
if (II->getIntrinsicID() == Intrinsic::frameescape) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// We need to ensure a safepoint poll occurs before any 'real' call. The
|
||||
// easiest way to ensure finite execution between safepoints in the face of
|
||||
// recursive and mutually recursive functions is to enforce that each take
|
||||
// a safepoint. Additionally, we need to ensure a poll before any call
|
||||
// which can grow the stack by an unbounded amount. This isn't required
|
||||
// for GC semantics per se, but is a common requirement for languages
|
||||
// which detect stack overflow via guard pages and then throw exceptions.
|
||||
if (auto CS = CallSite(cursor)) {
|
||||
if (doesNotRequireEntrySafepointBefore(CS))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
20
test/Transforms/PlaceSafepoints/memset.ll
Normal file
20
test/Transforms/PlaceSafepoints/memset.ll
Normal file
@ -0,0 +1,20 @@
|
||||
; RUN: opt -S -place-safepoints %s | FileCheck %s
|
||||
|
||||
define void @test(i32, i8 addrspace(1)* %ptr) gc "statepoint-example" {
|
||||
; CHECK-LABEL: @test
|
||||
; CHECK-NEXT: llvm.memset
|
||||
; CHECK: do_safepoint
|
||||
; CHECK: @foo
|
||||
call void @llvm.memset.p1i8.i64(i8 addrspace(1)* %ptr, i8 0, i64 24, i32 8, i1 false)
|
||||
call void @foo()
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @foo()
|
||||
declare void @llvm.memset.p1i8.i64(i8 addrspace(1)*, i8, i64, i32, i1)
|
||||
|
||||
declare void @do_safepoint()
|
||||
define void @gc.safepoint_poll() {
|
||||
call void @do_safepoint()
|
||||
ret void
|
||||
}
|
Loading…
Reference in New Issue
Block a user