Index: lib/Transforms/Scalar/GVN.cpp =================================================================== --- lib/Transforms/Scalar/GVN.cpp +++ lib/Transforms/Scalar/GVN.cpp @@ -1391,6 +1391,16 @@ "post condition violation"); } +static bool canHoistAcross(BasicBlock::iterator BBI, BasicBlock::iterator BBE) { + // Don't hoist a load across a call which could throw an exception + // or call exit(). + // FIXME: Potential O(N^2) performance issue? + for (; BBI != BBE; ++BBI) + if (isGuaranteedToTransferExecutionToSuccessor(BBI)) + return false; + return true; +} + bool GVN::PerformLoadPRE(LoadInst *LI, AvailValInBlkVect &ValuesPerBlock, UnavailBlkVect &UnavailableBlocks) { // Okay, we have *some* definitions of the value. This means that the value @@ -1409,6 +1419,12 @@ BasicBlock *LoadBB = LI->getParent(); BasicBlock *TmpBB = LoadBB; + const DataLayout &DL = LI->getModule()->getDataLayout(); + Value* UnderlyingObject = GetUnderlyingObject(LI->getPointerOperand(), DL); + bool SafeToLoadUnconditionally = isa(UnderlyingObject); + if (!SafeToLoadUnconditionally && !canHoistAcross(LoadBB->begin(), LI->getIterator())) + return false; + while (TmpBB->getSinglePredecessor()) { TmpBB = TmpBB->getSinglePredecessor(); if (TmpBB == LoadBB) // Infinite (unreachable) loop. @@ -1423,6 +1439,9 @@ // which it was not previously executed. if (TmpBB->getTerminator()->getNumSuccessors() != 1) return false; + + if (!SafeToLoadUnconditionally && !canHoistAcross(TmpBB->begin(), TmpBB->end())) + return false; } assert(TmpBB); @@ -1496,7 +1515,6 @@ // Check if the load can safely be moved to all the unavailable predecessors. bool CanDoPRE = true; - const DataLayout &DL = LI->getModule()->getDataLayout(); SmallVector NewInsts; for (auto &PredLoad : PredLoads) { BasicBlock *UnavailablePred = PredLoad.first; @@ -2401,6 +2419,10 @@ if (isa(PREPred->getTerminator())) return false; + if (!isSafeToSpeculativelyExecute(CurInst) && + !canHoistAcross(CurrentBlock->begin(), CurInst->getIterator())) + return false; + // We can't do PRE safely on a critical edge, so instead we schedule // the edge to be split and perform the PRE the next time we iterate // on the function. Index: test/Transforms/GVN/local-pre.ll =================================================================== --- test/Transforms/GVN/local-pre.ll +++ test/Transforms/GVN/local-pre.ll @@ -1,6 +1,7 @@ -; RUN: opt < %s -gvn -enable-pre -S | grep "b.pre" +; RUN: opt < %s -gvn -S | FileCheck %s -define i32 @main(i32 %p, i32 %q) { +define i32 @test1(i32 %p, i32 %q) { +; CHECK-LABEL: @test1 block1: %cmp = icmp eq i32 %p, %q br i1 %cmp, label %block2, label %block3 @@ -9,10 +10,42 @@ %a = add i32 %p, 1 br label %block4 +; CHECK: block3: +; CHECK-NEXT: add i32 %p, 1 block3: br label %block4 block4: +; CHECK: block4: +; CHECK-NEXT: phi +; CHECK-NEXT: ret %b = add i32 %p, 1 ret i32 %b } + +define i32 @test2(i32 %p, i32 %q) { +; CHECK-LABEL: @test2 +; CHECK: block1: +block1: + %cmp = icmp eq i32 %p, %q + br i1 %cmp, label %block2, label %block3 + +block2: + %a = sdiv i32 %p, %q + br label %block4 + +block3: + br label %block4 + +; CHECK: block4 +; CHECK-NEXT: call +; CHECK-NEXT: %b = sdiv +; CHECK-NEXT: ret i32 %b + +block4: + call void @may_exit() nounwind + %b = sdiv i32 %p, %q + ret i32 %b +} + +declare void @may_exit() nounwind Index: test/Transforms/GVN/pre-load.ll =================================================================== --- test/Transforms/GVN/pre-load.ll +++ test/Transforms/GVN/pre-load.ll @@ -430,3 +430,55 @@ call void @g(i32 %NOTPRE) cleanupret from %c2 unwind to caller } + +; Don't PRE load across call which could throw or call exit(). +define i32 @test13(i32* noalias nocapture readonly %x, i32* noalias nocapture %r, i32 %a) { +; CHECK-LABEL: @test13( +; CHECK: entry: +; CHECK-NEXT: icmp eq +; CHECK-NEXT: br i1 +entry: + %tobool = icmp eq i32 %a, 0 + br i1 %tobool, label %if.end, label %if.then + +; CHECK: if.then: +; CHECK-NEXT: load i32 +; CHECK-NEXT: store i32 +if.then: + %uu = load i32, i32* %x, align 4 + store i32 %uu, i32* %r, align 4 + br label %if.end + +; CHECK: if.end: +; CHECK-NEXT: call void @f() +; CHECK-NEXT: load i32 +if.end: + call void @f() + %vv = load i32, i32* %x, align 4 + ret i32 %vv +} + +; Okay to PRE load from alloca across call. +declare void @h(i32* nocapture) +define i32 @test14(i32* noalias nocapture %r, i32 %a) { +; CHECK-LABEL: @test14( +entry: + %x = alloca i32 + call void @h(i32* %x) + %tobool = icmp eq i32 %a, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: + %uu = load i32, i32* %x, align 4 + store i32 %uu, i32* %r, align 4 + br label %if.end + +; CHECK: if.end: +; CHECK-NEXT: %vv = phi i32 +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret i32 %vv +if.end: + call void @f() + %vv = load i32, i32* %x, align 4 + ret i32 %vv +}