Index: lib/Transforms/Scalar/GVN.cpp =================================================================== --- lib/Transforms/Scalar/GVN.cpp +++ lib/Transforms/Scalar/GVN.cpp @@ -1387,6 +1387,18 @@ "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: Come up with a check which is more fine-grained than + // mayHaveSideEffects(). + // FIXME: Potential O(N^2) performance issue? + for (; BBI != BBE; ++BBI) + if (!isa(BBI) && !isa(BBI) && BBI->mayHaveSideEffects()) + 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 @@ -1405,6 +1417,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. @@ -1419,6 +1437,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); @@ -1492,7 +1513,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; 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 +}