diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index dc0cec6122cdf..5ffad71abaf35 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -74,6 +74,7 @@ Changes to the LLVM IR format string function implementations from statically-linked libc's based on the requirements of each call. Currently only `float` is supported; this can keep floating point support out of printf if it can be proven unused. +* Case values are no longer operands of `SwitchInst`. Changes to LLVM infrastructure ------------------------------ @@ -178,6 +179,7 @@ Changes to the C API * Add `LLVMGetOrInsertFunction` to get or insert a function, replacing the combination of `LLVMGetNamedFunction` and `LLVMAddFunction`. * Allow `LLVMGetVolatile` to work with any kind of Instruction. * Add `LLVMConstFPFromBits` to get a constant floating-point value from an array of 64 bit values. +* Add `LLVMGetSwitchCaseValue` and `LLVMSetSwitchCaseValue` to get and set switch case values; switch case values are no longer operands of the instruction. Changes to the CodeGen infrastructure ------------------------------------- diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index fc41b5835d6eb..0074f1aad5a3c 100644 --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -4213,6 +4213,30 @@ LLVM_C_ABI void LLVMSetCondition(LLVMValueRef Branch, LLVMValueRef Cond); */ LLVM_C_ABI LLVMBasicBlockRef LLVMGetSwitchDefaultDest(LLVMValueRef SwitchInstr); +/** + * Obtain the case value for a successor of a switch instruction. i corresponds + * to the successor index. The first successor is the default destination, so i + * must be greater than zero. + * + * This only works on llvm::SwitchInst instructions. + * + * @see llvm::SwitchInst::CaseHandle::getCaseValue() + */ +LLVM_C_ABI LLVMValueRef LLVMGetSwitchCaseValue(LLVMValueRef SwitchInstr, + unsigned i); + +/** + * Set the case value for a successor of a switch instruction. i corresponds to + * the successor index. The first successor is the default destination, so i + * must be greater than zero. + * + * This only works on llvm::SwitchInst instructions. + * + * @see llvm::SwitchInst::CaseHandle::setValue() + */ +LLVM_C_ABI void LLVMSetSwitchCaseValue(LLVMValueRef SwitchInstr, unsigned i, + LLVMValueRef CaseValue); + /** * @} */ diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 8bd060ae8f485..80e9cb5bd806b 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -2665,7 +2665,7 @@ class PHINode : public Instruction { // User::allocHungoffUses, because we have to allocate Uses for the incoming // values and pointers to the incoming blocks, all in one allocation. void allocHungoffUses(unsigned N) { - User::allocHungoffUses(N, /* IsPhi */ true); + User::allocHungoffUses(N, /*WithExtraValues=*/true); } public: @@ -3198,10 +3198,10 @@ class SwitchInst : public Instruction { unsigned ReservedSpace; - // Operand[0] = Value to switch on - // Operand[1] = Default basic block destination - // Operand[2n ] = Value to match - // Operand[2n+1] = BasicBlock to go to on match + // Operand[0] = Value to switch on + // Operand[1] = Default basic block destination + // Operand[n] = BasicBlock to go to on match + // Values are stored after the Uses similar to PHINode's basic blocks. SwitchInst(const SwitchInst &SI); /// Create a new switch instruction, specifying a value to switch on and a @@ -3223,6 +3223,17 @@ class SwitchInst : public Instruction { LLVM_ABI SwitchInst *cloneImpl() const; + void allocHungoffUses(unsigned N) { + User::allocHungoffUses(N, /*WithExtraValues=*/true); + } + + ConstantInt *const *case_values() const { + return reinterpret_cast(op_begin() + ReservedSpace); + } + ConstantInt **case_values() { + return reinterpret_cast(op_begin() + ReservedSpace); + } + public: void operator delete(void *Ptr) { User::operator delete(Ptr); } @@ -3257,7 +3268,7 @@ class SwitchInst : public Instruction { ConstantIntT *getCaseValue() const { assert((unsigned)Index < SI->getNumCases() && "Index out the number of cases."); - return reinterpret_cast(SI->getOperand(2 + Index * 2)); + return SI->case_values()[Index]; } /// Resolves successor for current case. @@ -3299,7 +3310,7 @@ class SwitchInst : public Instruction { void setValue(ConstantInt *V) const { assert((unsigned)Index < SI->getNumCases() && "Index out the number of cases."); - SI->setOperand(2 + Index*2, reinterpret_cast(V)); + SI->case_values()[Index] = V; } /// Sets the new successor for current case. @@ -3406,9 +3417,7 @@ class SwitchInst : public Instruction { /// Return the number of 'cases' in this switch instruction, excluding the /// default case. - unsigned getNumCases() const { - return getNumOperands()/2 - 1; - } + unsigned getNumCases() const { return getNumOperands() - 2; } /// Returns a read/write iterator that points to the first case in the /// SwitchInst. @@ -3510,14 +3519,14 @@ class SwitchInst : public Instruction { /// case. LLVM_ABI CaseIt removeCase(CaseIt I); - unsigned getNumSuccessors() const { return getNumOperands()/2; } + unsigned getNumSuccessors() const { return getNumOperands() - 1; } BasicBlock *getSuccessor(unsigned idx) const { assert(idx < getNumSuccessors() &&"Successor idx out of range for switch!"); - return cast(getOperand(idx*2+1)); + return cast(getOperand(idx + 1)); } void setSuccessor(unsigned idx, BasicBlock *NewSucc) { assert(idx < getNumSuccessors() && "Successor # out of range for switch!"); - setOperand(idx * 2 + 1, NewSucc); + setOperand(idx + 1, NewSucc); } // Methods for support type inquiry through isa, cast, and dyn_cast: diff --git a/llvm/include/llvm/IR/User.h b/llvm/include/llvm/IR/User.h index cbb4379b68c41..394ea70d6637e 100644 --- a/llvm/include/llvm/IR/User.h +++ b/llvm/include/llvm/IR/User.h @@ -132,13 +132,13 @@ class User : public Value { /// Allocate the array of Uses, followed by a pointer /// (with bottom bit set) to the User. - /// \param IsPhi identifies callers which are phi nodes and which need - /// N BasicBlock* allocated along with N - LLVM_ABI void allocHungoffUses(unsigned N, bool IsPhi = false); + /// \param WithExtraValues identifies callers which need N Value* allocated + /// along the N operands. + LLVM_ABI void allocHungoffUses(unsigned N, bool WithExtraValues = false); /// Grow the number of hung off uses. Note that allocHungoffUses /// should be called if there are no uses. - LLVM_ABI void growHungoffUses(unsigned N, bool IsPhi = false); + LLVM_ABI void growHungoffUses(unsigned N, bool WithExtraValues = false); protected: ~User() = default; // Use deleteValue() to delete a generic Instruction. diff --git a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp index 36d0d35d024cc..4d5188cf7a0ce 100644 --- a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp +++ b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp @@ -164,6 +164,10 @@ static OrderMap orderModule(const Module &M) { orderConstantValue(Op); if (auto *SVI = dyn_cast(&I)) orderValue(SVI->getShuffleMaskForBitcode(), OM); + if (auto *SI = dyn_cast(&I)) { + for (const auto &Case : SI->cases()) + orderValue(Case.getCaseValue(), OM); + } orderValue(&I, OM); } } @@ -1092,6 +1096,10 @@ void ValueEnumerator::incorporateFunction(const Function &F) { } if (auto *SVI = dyn_cast(&I)) EnumerateValue(SVI->getShuffleMaskForBitcode()); + if (auto *SI = dyn_cast(&I)) { + for (const auto &Case : SI->cases()) + EnumerateValue(Case.getCaseValue()); + } } BasicBlocks.push_back(&BB); ValueMap[&BB] = BasicBlocks.size(); diff --git a/llvm/lib/CodeGen/TypePromotion.cpp b/llvm/lib/CodeGen/TypePromotion.cpp index e9fa78eabff7c..0865597dadcd6 100644 --- a/llvm/lib/CodeGen/TypePromotion.cpp +++ b/llvm/lib/CodeGen/TypePromotion.cpp @@ -512,6 +512,14 @@ void IRPromoter::PromoteTree() { I->setOperand(i, ConstantInt::get(ExtTy, 0)); } + // For switch, also mutate case values, which are not operands. + if (auto *SI = dyn_cast(I)) { + for (auto Case : SI->cases()) { + APInt NewConst = Case.getCaseValue()->getValue().zext(PromotedWidth); + Case.setValue(ConstantInt::get(SI->getContext(), NewConst)); + } + } + // Mutate the result type, unless this is an icmp or switch. if (!isa(I) && !isa(I)) { I->mutateType(ExtTy); diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp index bea30649947c7..e0427e9c3d8b1 100644 --- a/llvm/lib/IR/Core.cpp +++ b/llvm/lib/IR/Core.cpp @@ -3257,6 +3257,19 @@ LLVMBasicBlockRef LLVMGetSwitchDefaultDest(LLVMValueRef Switch) { return wrap(unwrap(Switch)->getDefaultDest()); } +LLVMValueRef LLVMGetSwitchCaseValue(LLVMValueRef Switch, unsigned i) { + assert(i > 0 && i <= unwrap(Switch)->getNumCases()); + auto It = unwrap(Switch)->case_begin() + (i - 1); + return wrap(It->getCaseValue()); +} + +void LLVMSetSwitchCaseValue(LLVMValueRef Switch, unsigned i, + LLVMValueRef CaseValue) { + assert(i > 0 && i <= unwrap(Switch)->getNumCases()); + auto It = unwrap(Switch)->case_begin() + (i - 1); + It->setValue(unwrap(CaseValue)); +} + /*--.. Operations on alloca instructions (only) ............................--*/ LLVMTypeRef LLVMGetAllocatedType(LLVMValueRef Alloca) { diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index 33ca46ca1c2c6..b1fa86aff239e 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -975,6 +975,14 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I, return equal(Phi->blocks(), OtherPhi->blocks()); } + if (const SwitchInst *SI = dyn_cast(this)) { + const SwitchInst *OtherSI = cast(I); + for (auto [Case, OtherCase] : zip(SI->cases(), OtherSI->cases())) + if (Case.getCaseValue() != OtherCase.getCaseValue()) + return false; + return true; + } + return this->hasSameSpecialState(I, /*IgnoreAlignment=*/false, IntersectAttrs); } diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index 85d3690dd8306..db0d5af83655f 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -202,7 +202,7 @@ void PHINode::growOperands() { if (NumOps < 2) NumOps = 2; // 2 op PHI nodes are VERY common. ReservedSpace = NumOps; - growHungoffUses(ReservedSpace, /* IsPhi */ true); + growHungoffUses(ReservedSpace, /*WithExtraValues=*/true); } /// hasConstantValue - If the specified PHI node always merges together the same @@ -4076,7 +4076,7 @@ SwitchInst::SwitchInst(Value *Value, BasicBlock *Default, unsigned NumCases, InsertPosition InsertBefore) : Instruction(Type::getVoidTy(Value->getContext()), Instruction::Switch, AllocMarker, InsertBefore) { - init(Value, Default, 2+NumCases*2); + init(Value, Default, 2 + NumCases); } SwitchInst::SwitchInst(const SwitchInst &SI) @@ -4084,10 +4084,12 @@ SwitchInst::SwitchInst(const SwitchInst &SI) init(SI.getCondition(), SI.getDefaultDest(), SI.getNumOperands()); setNumHungOffUseOperands(SI.getNumOperands()); Use *OL = getOperandList(); + ConstantInt **VL = case_values(); const Use *InOL = SI.getOperandList(); - for (unsigned i = 2, E = SI.getNumOperands(); i != E; i += 2) { + ConstantInt *const *InVL = SI.case_values(); + for (unsigned i = 2, E = SI.getNumOperands(); i != E; ++i) { OL[i] = InOL[i]; - OL[i+1] = InOL[i+1]; + VL[i - 2] = InVL[i - 2]; } SubclassOptionalData = SI.SubclassOptionalData; } @@ -4097,11 +4099,11 @@ SwitchInst::SwitchInst(const SwitchInst &SI) void SwitchInst::addCase(ConstantInt *OnVal, BasicBlock *Dest) { unsigned NewCaseIdx = getNumCases(); unsigned OpNo = getNumOperands(); - if (OpNo+2 > ReservedSpace) + if (OpNo + 1 > ReservedSpace) growOperands(); // Get more space! // Initialize some new operands. - assert(OpNo+1 < ReservedSpace && "Growing didn't work!"); - setNumHungOffUseOperands(OpNo+2); + assert(OpNo < ReservedSpace && "Growing didn't work!"); + setNumHungOffUseOperands(OpNo + 1); CaseHandle Case(this, NewCaseIdx); Case.setValue(OnVal); Case.setSuccessor(Dest); @@ -4112,21 +4114,22 @@ void SwitchInst::addCase(ConstantInt *OnVal, BasicBlock *Dest) { SwitchInst::CaseIt SwitchInst::removeCase(CaseIt I) { unsigned idx = I->getCaseIndex(); - assert(2 + idx*2 < getNumOperands() && "Case index out of range!!!"); + assert(2 + idx < getNumOperands() && "Case index out of range!!!"); unsigned NumOps = getNumOperands(); Use *OL = getOperandList(); + ConstantInt **VL = case_values(); // Overwrite this case with the end of the list. - if (2 + (idx + 1) * 2 != NumOps) { - OL[2 + idx * 2] = OL[NumOps - 2]; - OL[2 + idx * 2 + 1] = OL[NumOps - 1]; + if (2 + idx + 1 != NumOps) { + OL[2 + idx] = OL[NumOps - 1]; + VL[idx] = VL[NumOps - 2 - 1]; } // Nuke the last value. - OL[NumOps-2].set(nullptr); - OL[NumOps-2+1].set(nullptr); - setNumHungOffUseOperands(NumOps-2); + OL[NumOps - 1].set(nullptr); + VL[NumOps - 2 - 1] = nullptr; + setNumHungOffUseOperands(NumOps - 1); return CaseIt(this, idx); } @@ -4139,7 +4142,7 @@ void SwitchInst::growOperands() { unsigned NumOps = e*3; ReservedSpace = NumOps; - growHungoffUses(ReservedSpace); + growHungoffUses(ReservedSpace, /*WithExtraValues=*/true); } void SwitchInstProfUpdateWrapper::init() { diff --git a/llvm/lib/IR/User.cpp b/llvm/lib/IR/User.cpp index 9bb7c1298593a..1847c29d9ea4f 100644 --- a/llvm/lib/IR/User.cpp +++ b/llvm/lib/IR/User.cpp @@ -50,16 +50,16 @@ bool User::replaceUsesOfWith(Value *From, Value *To) { // User allocHungoffUses Implementation //===----------------------------------------------------------------------===// -void User::allocHungoffUses(unsigned N, bool IsPhi) { +void User::allocHungoffUses(unsigned N, bool WithExtraValues) { assert(HasHungOffUses && "alloc must have hung off uses"); - static_assert(alignof(Use) >= alignof(BasicBlock *), + static_assert(alignof(Use) >= alignof(Value *), "Alignment is insufficient for 'hung-off-uses' pieces"); // Allocate the array of Uses size_t size = N * sizeof(Use); - if (IsPhi) - size += N * sizeof(BasicBlock *); + if (WithExtraValues) + size += N * sizeof(Value *); Use *Begin = static_cast(::operator new(size)); Use *End = Begin + N; setOperandList(Begin); @@ -67,7 +67,7 @@ void User::allocHungoffUses(unsigned N, bool IsPhi) { new (Begin) Use(this); } -void User::growHungoffUses(unsigned NewNumUses, bool IsPhi) { +void User::growHungoffUses(unsigned NewNumUses, bool WithExtraValues) { assert(HasHungOffUses && "realloc must have hung off uses"); unsigned OldNumUses = getNumOperands(); @@ -77,22 +77,22 @@ void User::growHungoffUses(unsigned NewNumUses, bool IsPhi) { assert(NewNumUses > OldNumUses && "realloc must grow num uses"); Use *OldOps = getOperandList(); - allocHungoffUses(NewNumUses, IsPhi); + allocHungoffUses(NewNumUses, WithExtraValues); Use *NewOps = getOperandList(); // Now copy from the old operands list to the new one. std::copy(OldOps, OldOps + OldNumUses, NewOps); - // If this is a Phi, then we need to copy the BB pointers too. - if (IsPhi) { + // If the User has extra values (phi basic blocks, switch case values), then + // we need to copy these, too. + if (WithExtraValues) { auto *OldPtr = reinterpret_cast(OldOps + OldNumUses); auto *NewPtr = reinterpret_cast(NewOps + NewNumUses); - std::copy(OldPtr, OldPtr + (OldNumUses * sizeof(BasicBlock *)), NewPtr); + std::copy(OldPtr, OldPtr + (OldNumUses * sizeof(Value *)), NewPtr); } Use::zap(OldOps, OldOps + OldNumUses, true); } - // This is a private struct used by `User` to track the co-allocated descriptor // section. struct DescriptorInfo { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 10e6c452680b0..6aea9b0e7ecba 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -3424,7 +3424,7 @@ void Verifier::visitSwitchInst(SwitchInst &SI) { Type *SwitchTy = SI.getCondition()->getType(); SmallPtrSet Constants; for (auto &Case : SI.cases()) { - Check(isa(SI.getOperand(Case.getCaseIndex() * 2 + 2)), + Check(isa(Case.getCaseValue()), "Case value is not a constant integer.", &SI); Check(Case.getCaseValue()->getType() == SwitchTy, "Switch constants must all be same type as switch value!", &SI); diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index eea49bfdaf04b..4081b6b526771 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -1539,19 +1539,18 @@ void SPIRVEmitIntrinsics::useRoundingMode(ConstrainedFPIntrinsic *FPI, Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) { BasicBlock *ParentBB = I.getParent(); + Function *F = ParentBB->getParent(); IRBuilder<> B(ParentBB); B.SetInsertPoint(&I); SmallVector Args; SmallVector BBCases; - for (auto &Op : I.operands()) { - if (Op.get()->getType()->isSized()) { - Args.push_back(Op); - } else if (BasicBlock *BB = dyn_cast(Op.get())) { - BBCases.push_back(BB); - Args.push_back(BlockAddress::get(BB->getParent(), BB)); - } else { - report_fatal_error("Unexpected switch operand"); - } + Args.push_back(I.getCondition()); + BBCases.push_back(I.getDefaultDest()); + Args.push_back(BlockAddress::get(F, I.getDefaultDest())); + for (auto &Case : I.cases()) { + Args.push_back(Case.getCaseValue()); + BBCases.push_back(Case.getCaseSuccessor()); + Args.push_back(BlockAddress::get(F, Case.getCaseSuccessor())); } CallInst *NewI = B.CreateIntrinsic(Intrinsic::spv_switch, {I.getOperand(0)->getType()}, {Args}); diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 66b8c6937d8be..0389a0444a63c 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -1905,12 +1905,20 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI, // Check if sizes and terminators of all successors match. bool AllSame = - none_of(UniqueSuccessors, [&UniqueSuccessors](BasicBlock *Succ) { + all_of(UniqueSuccessors, [&UniqueSuccessors](BasicBlock *Succ) { Instruction *Term0 = UniqueSuccessors[0]->getTerminator(); Instruction *Term = Succ->getTerminator(); - return !Term->isSameOperationAs(Term0) || - !equal(Term->operands(), Term0->operands()) || - UniqueSuccessors[0]->size() != Succ->size(); + if (!Term->isSameOperationAs(Term0) || + UniqueSuccessors[0]->size() != Succ->size()) + return false; + if (auto *SI = dyn_cast(Term)) { + // Switch case values also need to be equal. + auto *SI0 = cast(Term0); + for (auto [Case, Case0] : zip(SI->cases(), SI0->cases())) + if (Case.getCaseValue() != Case0.getCaseValue()) + return false; + } + return equal(Term->operands(), Term0->operands()); }); if (!AllSame) return false; diff --git a/llvm/test/Transforms/SimplifyCFG/switch-dedup.ll b/llvm/test/Transforms/SimplifyCFG/switch-dedup.ll new file mode 100644 index 0000000000000..1a8115e3ca386 --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/switch-dedup.ll @@ -0,0 +1,76 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt < %s -passes=simplifycfg -S | FileCheck %s + +define i32 @nodedup(i1 %c, i16 %v) { +; CHECK-LABEL: define i32 @nodedup( +; CHECK-SAME: i1 [[C:%.*]], i16 [[V:%.*]]) { +; CHECK-NEXT: br i1 [[C]], label %[[B1:.*]], label %[[B2:.*]] +; CHECK: [[B1]]: +; CHECK-NEXT: switch i16 [[V]], label %[[RET2:.*]] [ +; CHECK-NEXT: i16 1962, label %[[COMMON_RET:.*]] +; CHECK-NEXT: i16 2000, label %[[COMMON_RET]] +; CHECK-NEXT: i16 1963, label %[[COMMON_RET]] +; CHECK-NEXT: ] +; CHECK: [[B2]]: +; CHECK-NEXT: switch i16 [[V]], label %[[RET2]] [ +; CHECK-NEXT: i16 2766, label %[[COMMON_RET]] +; CHECK-NEXT: i16 2798, label %[[COMMON_RET]] +; CHECK-NEXT: i16 2767, label %[[COMMON_RET]] +; CHECK-NEXT: ] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i32 [ 3, %[[RET2]] ], [ 1, %[[B2]] ], [ 1, %[[B2]] ], [ 1, %[[B2]] ], [ 1, %[[B1]] ], [ 1, %[[B1]] ], [ 1, %[[B1]] ] +; CHECK-NEXT: ret i32 [[COMMON_RET_OP]] +; CHECK: [[RET2]]: +; CHECK-NEXT: br label %[[COMMON_RET]] +; + br i1 %c, label %b1, label %b2 +b1: + switch i16 %v, label %ret2 [ + i16 1962, label %ret1 + i16 2000, label %ret1 + i16 1963, label %ret1 + ] +b2: + switch i16 %v, label %ret2 [ + i16 2766, label %ret1 + i16 2798, label %ret1 + i16 2767, label %ret1 + ] +ret1: + ret i32 1 +ret2: + ret i32 3 +} + +define i32 @dedup(i1 %c, i16 %v) { +; CHECK-LABEL: define i32 @dedup( +; CHECK-SAME: i1 [[C:%.*]], i16 [[V:%.*]]) { +; CHECK-NEXT: switch i16 [[V]], label %[[RET2:.*]] [ +; CHECK-NEXT: i16 2766, label %[[COMMON_RET:.*]] +; CHECK-NEXT: i16 2798, label %[[COMMON_RET]] +; CHECK-NEXT: i16 2767, label %[[COMMON_RET]] +; CHECK-NEXT: ] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i32 [ 3, %[[RET2]] ], [ 1, [[TMP0:%.*]] ], [ 1, [[TMP0]] ], [ 1, [[TMP0]] ] +; CHECK-NEXT: ret i32 [[COMMON_RET_OP]] +; CHECK: [[RET2]]: +; CHECK-NEXT: br label %[[COMMON_RET]] +; + br i1 %c, label %b1, label %b2 +b1: + switch i16 %v, label %ret2 [ + i16 2766, label %ret1 + i16 2798, label %ret1 + i16 2767, label %ret1 + ] +b2: + switch i16 %v, label %ret2 [ + i16 2766, label %ret1 + i16 2798, label %ret1 + i16 2767, label %ret1 + ] +ret1: + ret i32 1 +ret2: + ret i32 3 +} diff --git a/llvm/unittests/IR/InstructionsTest.cpp b/llvm/unittests/IR/InstructionsTest.cpp index f4693bfb1a4d1..f7517a732e7f6 100644 --- a/llvm/unittests/IR/InstructionsTest.cpp +++ b/llvm/unittests/IR/InstructionsTest.cpp @@ -968,6 +968,29 @@ TEST(InstructionsTest, SwitchInst) { const auto &Handle = *CCI; EXPECT_EQ(1, Handle.getCaseValue()->getSExtValue()); EXPECT_EQ(BB1.get(), Handle.getCaseSuccessor()); + + // C API tests. + EXPECT_EQ(BB0.get(), unwrap(LLVMGetSwitchDefaultDest(wrap(SI)))); + EXPECT_EQ(BB0.get(), unwrap(LLVMGetSuccessor(wrap(SI), 0))); + EXPECT_EQ(BB1.get(), unwrap(LLVMGetSuccessor(wrap(SI), 1))); + EXPECT_EQ( + 1, + unwrap(LLVMGetSwitchCaseValue(wrap(SI), 1))->getSExtValue()); + EXPECT_EQ(BB2.get(), unwrap(LLVMGetSuccessor(wrap(SI), 2))); + EXPECT_EQ( + 2, + unwrap(LLVMGetSwitchCaseValue(wrap(SI), 2))->getSExtValue()); + EXPECT_EQ(BB3.get(), unwrap(LLVMGetSuccessor(wrap(SI), 3))); + EXPECT_EQ( + 3, + unwrap(LLVMGetSwitchCaseValue(wrap(SI), 3))->getSExtValue()); + // Test case value modification. The C API provides case value indices + // matching the successor indices. + LLVMSetSwitchCaseValue(wrap(SI), 2, wrap(ConstantInt::get(Int32Ty, 12))); + EXPECT_EQ(12, (SI->case_begin() + 1)->getCaseValue()->getSExtValue()); + EXPECT_EQ( + 12, + unwrap(LLVMGetSwitchCaseValue(wrap(SI), 2))->getSExtValue()); } TEST(InstructionsTest, SwitchInstProfUpdateWrapper) { diff --git a/llvm/unittests/IR/VerifierTest.cpp b/llvm/unittests/IR/VerifierTest.cpp index 440db1216edc9..49fad197d12d7 100644 --- a/llvm/unittests/IR/VerifierTest.cpp +++ b/llvm/unittests/IR/VerifierTest.cpp @@ -329,9 +329,6 @@ TEST(VerifierTest, SwitchInst) { Switch->addCase(ConstantInt::get(Int32Ty, 2), OnTwo); EXPECT_FALSE(verifyFunction(*F)); - // set one case value to function argument. - Switch->setOperand(2, F->getArg(1)); - EXPECT_TRUE(verifyFunction(*F)); } TEST(VerifierTest, CrossFunctionRef) {