Index: include/llvm/Transforms/Utils/Local.h =================================================================== --- include/llvm/Transforms/Utils/Local.h +++ include/llvm/Transforms/Utils/Local.h @@ -355,6 +355,9 @@ unsigned replaceDominatedUsesWith(Value *From, Value *To, DominatorTree &DT, const BasicBlock *BB); +/// Replace each use of 'From' with 'To' if that use is outside the defining +/// block of 'From'. Returns the number of replacements made. +unsigned replaceNonLocalUsesWith(Instruction *From, Value *To); /// Return true if the CallSite CS calls a gc leaf function. /// Index: lib/Transforms/Scalar/JumpThreading.cpp =================================================================== --- lib/Transforms/Scalar/JumpThreading.cpp +++ lib/Transforms/Scalar/JumpThreading.cpp @@ -822,17 +822,24 @@ CondBr->getSuccessor(ToRemove)->removePredecessor(BB, true); BranchInst::Create(CondBr->getSuccessor(ToKeep), CondBr); CondBr->eraseFromParent(); - if (CondCmp->use_empty()) - CondCmp->eraseFromParent(); - else if (CondCmp->getParent() == BB) { - // If the fact we just learned is true for all uses of the - // condition, replace it with a constant value + + // Now, see if we can eagerly update any other uses. WARNING: we used a + // context instruction at the end of the current basic block, so the + // facts LVI returned may not be true before that point. (i.e. we may + // have a guard or assume part way through the block.) Thus, we can + // not reason about any use which is not dominated by the context + // instruction. Given we don't have dom-tree here, we're restricted to + // only handling cases where dominance can be trivially established + // from the definition of SSA. + if (!CondCmp->use_empty() && + CondCmp->getParent() == BB) { auto *CI = Ret == LazyValueInfo::True ? ConstantInt::getTrue(CondCmp->getType()) : ConstantInt::getFalse(CondCmp->getType()); - CondCmp->replaceAllUsesWith(CI); - CondCmp->eraseFromParent(); + replaceNonLocalUsesWith(CondCmp, CI); } + if (CondCmp->use_empty()) + CondCmp->eraseFromParent(); return true; } } Index: lib/Transforms/Utils/Local.cpp =================================================================== --- lib/Transforms/Utils/Local.cpp +++ lib/Transforms/Utils/Local.cpp @@ -1755,6 +1755,29 @@ combineMetadata(K, J, KnownIDs); } +unsigned llvm::replaceNonLocalUsesWith(Instruction *From, Value *To) { + assert(From->getType() == To->getType()); + + BasicBlock *DefBB = From->getParent(); + + unsigned Count = 0; + for (Value::use_iterator UI = From->use_begin(), UE = From->use_end(); + UI != UE; ) { + Use &U = *UI++; + auto *I = cast(U.getUser()); + if (DefBB != I->getParent()) { + U.set(To); + DEBUG(dbgs() << "Replace non-local use of '" + << From->getName() << "' as " + << *To << " in " << *U << "\n"); + ++Count; + } + } + return Count; + +} + + unsigned llvm::replaceDominatedUsesWith(Value *From, Value *To, DominatorTree &DT, const BasicBlockEdge &Root) { @@ -1780,6 +1803,12 @@ const BasicBlock *BB) { assert(From->getType() == To->getType()); + // This "micro optimization" is here mostly to ensure this code path gets + // test coverage. + if (auto *FromI = dyn_cast(From)) + if (FromI->getParent() == BB) + return replaceNonLocalUsesWith(FromI, To); + unsigned Count = 0; for (Value::use_iterator UI = From->use_begin(), UE = From->use_end(); UI != UE;) { Index: test/Transforms/JumpThreading/assume.ll =================================================================== --- test/Transforms/JumpThreading/assume.ll +++ test/Transforms/JumpThreading/assume.ll @@ -56,8 +56,50 @@ ret i32 %retval.0 } +@g = external global i32 + +; Check that we do prove a fact using an assume within the block and +; that we *don't* use circular logic to remove the assume. +; CHECK-LABEL: @local_assume +; CHECK: %notnull = icmp ne i32* %array, null +; CHECK-NEXT: call void @llvm.assume(i1 %notnull) +; CHECK-NEXT: ret void +define void @local_assume(i32* %array) { + %notnull = icmp ne i32* %array, null + call void @llvm.assume(i1 %notnull) + br i1 %notnull, label %normal, label %error + +normal: + ret void + +error: + store atomic i32 0, i32* @g unordered, align 4 + ret void +} + +; Check that we do prove a fact using an assume within the block and +; that we *don't* use circular logic to remove the guard. +; CHECK-LABEL: @local_guard +; CHECK: %notnull = icmp ne i32* %array, null +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %notnull) [ "deopt"(i32 0) ] +; CHECK-NEXT: ret void + +define void @local_guard(i32* %array) { + %notnull = icmp ne i32* %array, null + call void (i1, ...) @llvm.experimental.guard(i1 %notnull) ["deopt" (i32 0)] + br i1 %notnull, label %normal, label %error + +normal: + ret void + +error: + store atomic i32 0, i32* @g unordered, align 4 + ret void +} + ; Function Attrs: nounwind declare void @llvm.assume(i1) #1 +declare void @llvm.experimental.guard(i1 %cnd, ...) declare void @bar(...)