Index: llvm/lib/IR/ReplaceConstant.cpp =================================================================== --- llvm/lib/IR/ReplaceConstant.cpp +++ llvm/lib/IR/ReplaceConstant.cpp @@ -134,36 +134,58 @@ } } +static bool isExpandableUser(User *U) { + return isa(U) || isa(U); +} + +static Instruction *expandUser(Instruction *InsertPt, Constant *C) { + if (auto *CE = dyn_cast(C)) { + return CE->getAsInstruction(InsertPt); + } else if (isa(C) || isa(C)) { + Value *V = PoisonValue::get(C->getType()); + for (auto [Idx, Op] : enumerate(C->operands())) + V = InsertValueInst::Create(V, Op, Idx, "", InsertPt); + return cast(V); + } else if (isa(C)) { + Type *IdxTy = Type::getInt32Ty(C->getContext()); + Value *V = PoisonValue::get(C->getType()); + for (auto [Idx, Op] : enumerate(C->operands())) + V = InsertElementInst::Create(V, Op, ConstantInt::get(IdxTy, Idx), "", + InsertPt); + return cast(V); + } else { + llvm_unreachable("Not an expandable user"); + } +} + bool convertUsersOfConstantsToInstructions(ArrayRef Consts) { - // Find all ConstantExpr that are direct users Consts. - SmallVector Stack; + // Find all expandable direct users of Consts. + SmallVector Stack; for (Constant *C : Consts) for (User *U : C->users()) - if (ConstantExpr *CE = dyn_cast(U)) - Stack.push_back(CE); + if (isExpandableUser(U)) + Stack.push_back(cast(U)); - // Expand to include constexpr users of direct users - SetVector ConstExprUsers; + // Include transitive users. + SetVector ExpandableUsers; while (!Stack.empty()) { - ConstantExpr *V = Stack.pop_back_val(); - if (ConstExprUsers.contains(V)) + Constant *C = Stack.pop_back_val(); + if (!ExpandableUsers.insert(C)) continue; - ConstExprUsers.insert(V); - - for (auto *Nested : V->users()) - if (ConstantExpr *CE = dyn_cast(Nested)) - Stack.push_back(CE); + for (auto *Nested : C->users()) + if (isExpandableUser(Nested)) + Stack.push_back(cast(Nested)); } - // Find all instructions that use any of the ConstExpr users + // Find all instructions that use any of the expandable users SetVector InstructionWorklist; - for (ConstantExpr *CE : ConstExprUsers) - for (User *U : CE->users()) + for (Constant *C : ExpandableUsers) + for (User *U : C->users()) if (auto *I = dyn_cast(U)) InstructionWorklist.insert(I); - // Replace those ConstExpr operands with instructions + // Replace those expandable operands with instructions bool Changed = false; while (!InstructionWorklist.empty()) { Instruction *I = InstructionWorklist.pop_back_val(); @@ -176,18 +198,20 @@ BI = &*It; } - if (ConstantExpr *C = dyn_cast(U.get())) { - if (ConstExprUsers.contains(C)) { + if (auto *C = dyn_cast(U.get())) { + if (ExpandableUsers.contains(C)) { Changed = true; - Instruction *NI = C->getAsInstruction(BI); + Instruction *NI = expandUser(BI, C); InstructionWorklist.insert(NI); U.set(NI); - C->removeDeadConstantUsers(); } } } } + for (Constant *C : Consts) + C->removeDeadConstantUsers(); + return Changed; } Index: llvm/lib/Transforms/IPO/LowerTypeTests.cpp =================================================================== --- llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -51,6 +51,7 @@ #include "llvm/IR/ModuleSummaryIndexYAML.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/ReplaceConstant.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/User.h" @@ -1368,11 +1369,17 @@ F->getAddressSpace(), "", &M); replaceCfiUses(F, PlaceholderFn, IsJumpTableCanonical); - Constant *Target = ConstantExpr::getSelect( - ConstantExpr::getICmp(CmpInst::ICMP_NE, F, - Constant::getNullValue(F->getType())), - JT, Constant::getNullValue(F->getType())); - PlaceholderFn->replaceAllUsesWith(Target); + convertUsersOfConstantsToInstructions(PlaceholderFn); + for (Use &U : make_early_inc_range(PlaceholderFn->uses())) { + auto *I = dyn_cast(U.getUser()); + assert(I && "Non-instruction users should have been eliminated"); + IRBuilder Builder(I); + Value *ICmp = Builder.CreateICmp(CmpInst::ICMP_NE, F, + Constant::getNullValue(F->getType())); + Value *Select = Builder.CreateSelect(ICmp, JT, + Constant::getNullValue(F->getType())); + U.set(Select); + } PlaceholderFn->eraseFromParent(); } Index: llvm/test/Transforms/LowerTypeTests/cfi-direct-call1.ll =================================================================== --- llvm/test/Transforms/LowerTypeTests/cfi-direct-call1.ll +++ llvm/test/Transforms/LowerTypeTests/cfi-direct-call1.ll @@ -56,7 +56,8 @@ ; FULL: %fptr1 = select i1 %cmp.i, ptr @local_func1, ptr @local_func2 ; Indirect references to extern_weak and extern_decl must go through jump table -; FULL: %fptr2 = select i1 %cmp.i, ptr select (i1 icmp ne (ptr @extern_weak, ptr null), ptr getelementptr inbounds ([4 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 2), ptr null), ptr getelementptr inbounds ([4 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 3) +; FULL: %0 = select i1 icmp ne (ptr @extern_weak, ptr null), ptr getelementptr inbounds ([4 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 2), ptr null +; FULL: %fptr2 = select i1 %cmp.i, ptr %0, ptr getelementptr inbounds ([4 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 3) ; Direct calls to extern_weak and extern_decl should go to original names ; FULL: %call5 = tail call i32 @extern_decl() @@ -82,7 +83,8 @@ ; THIN: %fptr1 = select i1 %cmp.i, ptr @local_func1, ptr @local_func2 ; Indirect references to extern_weak and extern_decl must go through jump table -; THIN: %fptr2 = select i1 %cmp.i, ptr select (i1 icmp ne (ptr @extern_weak, ptr null), ptr @extern_weak.cfi_jt, ptr null), ptr @extern_decl.cfi_jt +; THIN: %0 = select i1 icmp ne (ptr @extern_weak, ptr null), ptr @extern_weak.cfi_jt, ptr null +; THIN: %fptr2 = select i1 %cmp.i, ptr %0, ptr @extern_decl.cfi_jt ; Direct calls to extern_weak and extern_decl should go to original names ; THIN: %call5 = tail call i32 @extern_decl() Index: llvm/test/Transforms/LowerTypeTests/function-weak.ll =================================================================== --- llvm/test/Transforms/LowerTypeTests/function-weak.ll +++ llvm/test/Transforms/LowerTypeTests/function-weak.ll @@ -33,7 +33,9 @@ ; CHECK: define zeroext i1 @check_f() define zeroext i1 @check_f() { entry: -; CHECK: ret i1 icmp ne (ptr select (i1 icmp ne (ptr @f, ptr null), ptr @[[JT:.*]], ptr null), ptr null) +; CHECK: %0 = select i1 icmp ne (ptr @f, ptr null), ptr @[[JT:.*]], ptr null +; CHECK: %1 = icmp ne ptr %0, null +; ret i1 %1 ret i1 icmp ne (ptr @f, ptr null) } @@ -58,11 +60,21 @@ ; CHECK: define internal void @__cfi_global_var_init() section ".text.startup" { ; CHECK-NEXT: entry: -; CHECK-NEXT: store ptr select (i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null), ptr @x, align 8 -; CHECK-NEXT: store ptr select (i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null), ptr @x2, align 8 -; CHECK-NEXT: store ptr select (i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null), ptr @x3, align 8 -; CHECK-NEXT: store ptr getelementptr (i8, ptr select (i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null), i64 42), ptr @x4, align 8 -; CHECK-NEXT: store { ptr, ptr, i32 } { ptr select (i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null), ptr select (i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null), i32 42 }, ptr @s, align 8 +; CHECK-NEXT: %0 = select i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null +; CHECK-NEXT: store ptr %0, ptr @x, align 8 +; CHECK-NEXT: %1 = select i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null +; CHECK-NEXT: store ptr %1, ptr @x2, align 8 +; CHECK-NEXT: %2 = select i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null +; CHECK-NEXT: store ptr %2, ptr @x3, align 8 +; CHECK-NEXT: %3 = select i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null +; CHECK-NEXT: %4 = getelementptr i8, ptr %3, i64 42 +; CHECK-NEXT: store ptr %4, ptr @x4, align 8 +; CHECK-NEXT: %5 = select i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null +; CHECK-NEXT: %6 = insertvalue { ptr, ptr, i32 } poison, ptr %5, 0 +; CHECK-NEXT: %7 = select i1 icmp ne (ptr @f, ptr null), ptr @[[JT]], ptr null +; CHECK-NEXT: %8 = insertvalue { ptr, ptr, i32 } %6, ptr %7, 1 +; CHECK-NEXT: %9 = insertvalue { ptr, ptr, i32 } %8, i32 42, 2 +; CHECK-NEXT: store { ptr, ptr, i32 } %9, ptr @s, align 8 ; CHECK-NEXT: ret void ; CHECK-NEXT: }