diff --git a/llvm/include/llvm/IR/Dominators.h b/llvm/include/llvm/IR/Dominators.h --- a/llvm/include/llvm/IR/Dominators.h +++ b/llvm/include/llvm/IR/Dominators.h @@ -191,7 +191,7 @@ /// * Non-instruction Defs dominate everything. /// * Def does not dominate a use in Def itself (outside of degenerate cases /// like unreachable code or trivial phi cycles). - /// * Invoke/callbr Defs only dominate uses in their default destination. + /// * Invoke Defs only dominate uses in their default destination. bool dominates(const Value *Def, const Use &U) const; /// Return true if value Def dominates all possible uses inside instruction /// User. Same comments as for the Use-based API apply. diff --git a/llvm/lib/IR/Dominators.cpp b/llvm/lib/IR/Dominators.cpp --- a/llvm/lib/IR/Dominators.cpp +++ b/llvm/lib/IR/Dominators.cpp @@ -194,11 +194,17 @@ return dominates(E, UseBB); } - // Callbr results are similarly only usable in the default destination. if (const auto *CBI = dyn_cast(Def)) { BasicBlock *NormalDest = CBI->getDefaultDest(); BasicBlockEdge E(DefBB, NormalDest); - return dominates(E, UseBB); + if (dominates(E, UseBB)) + return true; + for (BasicBlock *IndirectBB : CBI->getIndirectDests()) { + BasicBlockEdge E(DefBB, IndirectBB); + if (dominates(E, UseBB)) + return true; + } + return false; } return dominates(DefBB, UseBB); @@ -311,11 +317,17 @@ return dominates(E, U); } - // Callbr results are similarly only usable in the default destination. if (const auto *CBI = dyn_cast(Def)) { BasicBlock *NormalDest = CBI->getDefaultDest(); BasicBlockEdge E(DefBB, NormalDest); - return dominates(E, U); + if (dominates(E, U)) + return true; + for (BasicBlock *IndirectBB : CBI->getIndirectDests()) { + BasicBlockEdge E(DefBB, IndirectBB); + if (dominates(E, U)) + return true; + } + return false; } // If the def and use are in different blocks, do a simple CFG dominator diff --git a/llvm/test/Verifier/callbr.ll b/llvm/test/Verifier/callbr.ll --- a/llvm/test/Verifier/callbr.ll +++ b/llvm/test/Verifier/callbr.ll @@ -56,9 +56,7 @@ ret void } -;; Ensure you cannot use the return value of a callbr in indirect targets. -; CHECK: Instruction does not dominate all uses! -; CHECK-NEXT: #test4 +;; Ensure you can use the return value of a callbr in indirect targets. define i32 @test4(i1 %var) { entry: %ret = callbr i32 asm sideeffect "#test4", "=r,!i"() to label %normal [label %abnormal] diff --git a/llvm/test/Verifier/dominates.ll b/llvm/test/Verifier/dominates.ll --- a/llvm/test/Verifier/dominates.ll +++ b/llvm/test/Verifier/dominates.ll @@ -69,6 +69,7 @@ ; CHECK-NEXT: %x = phi i32 [ %y, %entry ] } +;; No issue! define i32 @f6(i32 %x) { bb0: %y1 = callbr i32 asm "", "=r,!i"() to label %bb1 [label %bb2] @@ -76,8 +77,4 @@ ret i32 0 bb2: ret i32 %y1 -; CHECK: Instruction does not dominate all uses! -; CHECK-NEXT: %y1 = callbr i32 asm "", "=r,!i"() -; CHECK-NEXT: to label %bb1 [label %bb2] -; CHECK-NEXT: ret i32 %y1 } diff --git a/llvm/unittests/IR/DominatorTreeTest.cpp b/llvm/unittests/IR/DominatorTreeTest.cpp --- a/llvm/unittests/IR/DominatorTreeTest.cpp +++ b/llvm/unittests/IR/DominatorTreeTest.cpp @@ -1100,3 +1100,47 @@ EXPECT_TRUE(DT->dominates(C, U)); }); } +TEST(DominatorTree, CallBrDomination) { + StringRef ModuleString = R"( +define void @x() { + %y = alloca i32 + %w = callbr i32 asm "", "=r,!i"() + to label %asm.fallthrough [label %z] + +asm.fallthrough: + br label %cleanup + +z: + store i32 %w, ptr %y + br label %cleanup + +cleanup: + ret void +})"; + + LLVMContext Context; + std::unique_ptr M = makeLLVMModule(Context, ModuleString); + + runWithDomTree(*M, "x", + [&](Function &F, DominatorTree *DT, PostDominatorTree *PDT) { + Function::iterator FI = F.begin(); + + BasicBlock *Entry = &*FI++; + BasicBlock *ASMFallthrough = &*FI++; + BasicBlock *Z = &*FI++; + + EXPECT_TRUE(DT->dominates(Entry, ASMFallthrough)); + EXPECT_TRUE(DT->dominates(Entry, Z)); + + BasicBlock::iterator BBI = Entry->begin(); + ++BBI; + Instruction &I = *BBI; + EXPECT_TRUE(isa(I)); + EXPECT_TRUE(isa(I)); + for (const User *U : I.users()) { + llvm::errs() << *U << "\n"; + EXPECT_TRUE(isa(U)); + EXPECT_TRUE(DT->dominates(cast(&I), cast(U))); + } + }); +}