diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h b/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h --- a/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h +++ b/llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h @@ -24,6 +24,7 @@ int BonusInstThreshold = 1; bool ForwardSwitchCondToPhi = false; bool ConvertSwitchToLookupTable = false; + bool RelativeSwitchLookupTable = false; bool NeedCanonicalLoop = true; bool HoistCommonInsts = false; bool SinkCommonInsts = false; @@ -45,6 +46,10 @@ ConvertSwitchToLookupTable = B; return *this; } + SimplifyCFGOptions &relativeSwitchLookupTable(bool B) { + RelativeSwitchLookupTable = B; + return *this; + } SimplifyCFGOptions &needCanonicalLoops(bool B) { NeedCanonicalLoop = B; return *this; diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -2046,6 +2046,8 @@ Result.forwardSwitchCondToPhi(Enable); } else if (ParamName == "switch-to-lookup") { Result.convertSwitchToLookupTable(Enable); + } else if (ParamName == "relative-switch-lookup-table") { + Result.relativeSwitchLookupTable(Enable); } else if (ParamName == "keep-loops") { Result.needCanonicalLoops(Enable); } else if (ParamName == "hoist-common-insts") { diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -823,6 +823,7 @@ MPM.add(createCFGSimplificationPass(SimplifyCFGOptions() .forwardSwitchCondToPhi(true) .convertSwitchToLookupTable(true) + .relativeSwitchLookupTable(true) .needCanonicalLoops(false) .hoistCommonInsts(true) .sinkCommonInsts(true))); diff --git a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp --- a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp +++ b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp @@ -60,6 +60,10 @@ "switch-to-lookup", cl::Hidden, cl::init(false), cl::desc("Convert switches to lookup tables (default = false)")); +static cl::opt UserRelativeSwitchLookupTable( + "relative-switch-lookup-table", cl::Hidden, cl::init(false), + cl::desc("Genereate relative switch lookup tables (default = false)")); + static cl::opt UserForwardSwitchCond( "forward-switch-cond", cl::Hidden, cl::init(false), cl::desc("Forward switch condition to phi ops (default = false)")); @@ -270,6 +274,8 @@ Options.ForwardSwitchCondToPhi = UserForwardSwitchCond; if (UserSwitchToLookup.getNumOccurrences()) Options.ConvertSwitchToLookupTable = UserSwitchToLookup; + if (UserRelativeSwitchLookupTable.getNumOccurrences()) + Options.RelativeSwitchLookupTable = UserRelativeSwitchLookupTable; if (UserKeepLoops.getNumOccurrences()) Options.NeedCanonicalLoop = UserKeepLoops; if (UserHoistCommonInsts.getNumOccurrences()) diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -5460,11 +5460,12 @@ SwitchLookupTable( Module &M, uint64_t TableSize, ConstantInt *Offset, const SmallVectorImpl> &Values, - Constant *DefaultValue, const DataLayout &DL, const StringRef &FuncName); + Constant *DefaultValue, const DataLayout &DL, const StringRef &FuncName, + IRBuilder<> &Builder, bool EnableRelativeTable); /// Build instructions with Builder to retrieve the value at /// the position given by Index in the lookup table. - Value *BuildLookup(Value *Index, IRBuilder<> &Builder); + Value *BuildLookup(Value *Index, Type *ValueType, IRBuilder<> &Builder); /// Return true if a table with TableSize elements of /// type ElementType would fit in a target-legal register. @@ -5507,6 +5508,9 @@ // For ArrayKind, this is the array. GlobalVariable *Array = nullptr; + + // Determines if a relative lookup table generated. + bool IsRelative = false; }; } // end anonymous namespace @@ -5514,7 +5518,8 @@ SwitchLookupTable::SwitchLookupTable( Module &M, uint64_t TableSize, ConstantInt *Offset, const SmallVectorImpl> &Values, - Constant *DefaultValue, const DataLayout &DL, const StringRef &FuncName) { + Constant *DefaultValue, const DataLayout &DL, const StringRef &FuncName, + IRBuilder<> &Builder, bool EnableRelativeTable) { assert(Values.size() && "Can't build lookup table without values!"); assert(TableSize >= Values.size() && "Can't fit values in table!"); @@ -5523,6 +5528,17 @@ Type *ValueType = Values.begin()->second->getType(); + /// If relative lookup table is enabled, generate a relative lookup table + /// Relative lookup table consists of offsets between the lookup table and + /// elements of the of the lookup table + if (EnableRelativeTable && ValueType->isPointerTy()) { + IsRelative = true; + ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), TableSize); + Array = new GlobalVariable(M, ArrayTy, /*isConstant=*/true, + GlobalVariable::PrivateLinkage, nullptr, + "switch.table." + FuncName); + } + // Build up the table contents. SmallVector TableContents(TableSize); for (size_t I = 0, E = Values.size(); I != E; ++I) { @@ -5531,7 +5547,19 @@ assert(CaseRes->getType() == ValueType); uint64_t Idx = (CaseVal->getValue() - Offset->getValue()).getLimitedValue(); - TableContents[Idx] = CaseRes; + // If relative lookup table is enabled, put relative offsets into the table + if (IsRelative) { + Constant *Base = + llvm::ConstantExpr::getPtrToInt(Array, Builder.getInt64Ty()); + Constant *Target = + llvm::ConstantExpr::getPtrToInt(CaseRes, Builder.getInt64Ty()); + llvm::Constant *RelOffset = llvm::ConstantExpr::getSub(Target, Base); + ; + RelOffset = llvm::ConstantExpr::getTrunc(RelOffset, Builder.getInt32Ty()); + TableContents[Idx] = RelOffset; + } else { + TableContents[Idx] = CaseRes; + } if (CaseRes != SingleValue) SingleValue = nullptr; @@ -5543,8 +5571,19 @@ "Need a default value to fill the lookup table holes."); assert(DefaultValue->getType() == ValueType); for (uint64_t I = 0; I < TableSize; ++I) { - if (!TableContents[I]) - TableContents[I] = DefaultValue; + if (!TableContents[I]) { + if (IsRelative) { + Constant *Base = + llvm::ConstantExpr::getPtrToInt(Array, Builder.getInt64Ty()); + Constant *Target = llvm::ConstantExpr::getPtrToInt( + DefaultValue, Builder.getInt64Ty()); + llvm::Constant *RelOffset = llvm::ConstantExpr::getSub(Target, Base); + RelOffset = + llvm::ConstantExpr::getTrunc(RelOffset, Builder.getInt32Ty()); + TableContents[I] = RelOffset; + } else + TableContents[I] = DefaultValue; + } } if (DefaultValue != SingleValue) @@ -5614,13 +5653,20 @@ return; } - // Store the table in an array. - ArrayType *ArrayTy = ArrayType::get(ValueType, TableSize); - Constant *Initializer = ConstantArray::get(ArrayTy, TableContents); + // If relative table is generated, initialize the array with the table + if (EnableRelativeTable && ValueType->isPointerTy()) { + ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), TableSize); + Constant *Initializer = ConstantArray::get(ArrayTy, TableContents); + Array->setInitializer(Initializer); + } else { + // Store the table in an array. + ArrayType *ArrayTy = ArrayType::get(ValueType, TableSize); + Constant *Initializer = ConstantArray::get(ArrayTy, TableContents); - Array = new GlobalVariable(M, ArrayTy, /*isConstant=*/true, - GlobalVariable::PrivateLinkage, Initializer, - "switch.table." + FuncName); + Array = new GlobalVariable(M, ArrayTy, /*isConstant=*/true, + GlobalVariable::PrivateLinkage, Initializer, + "switch.table." + FuncName); + } Array->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); // Set the alignment to that of an array items. We will be only loading one // value out of it. @@ -5628,7 +5674,8 @@ Kind = ArrayKind; } -Value *SwitchLookupTable::BuildLookup(Value *Index, IRBuilder<> &Builder) { +Value *SwitchLookupTable::BuildLookup(Value *Index, Type *ValueType, + IRBuilder<> &Builder) { switch (Kind) { case SingleValueKind: return SingleValue; @@ -5675,9 +5722,24 @@ Value *GEPIndices[] = {Builder.getInt32(0), Index}; Value *GEP = Builder.CreateInBoundsGEP(Array->getValueType(), Array, GEPIndices, "switch.gep"); - return Builder.CreateLoad( + + Value *Load = Builder.CreateLoad( cast(Array->getValueType())->getElementType(), GEP, "switch.load"); + + /// If relative lookup table is generated, create instructions to add + /// lookup table address and offset and compute the target address + if (IsRelative) { + Constant *Base = + llvm::ConstantExpr::getPtrToInt(Array, Builder.getInt64Ty()); + Value *Offset = Builder.CreateSExt(Load, Builder.getInt64Ty(), + "switch.reltable.offset.sext"); + Value *Add = Builder.CreateAdd(Base, Offset, "switch.reltable.add"); + assert(ValueType->isPointerTy() && "Values should have pointer types!"); + return Builder.CreateIntToPtr(Add, ValueType); + } + + return Load; } } llvm_unreachable("Unknown lookup table kind!"); @@ -5829,7 +5891,8 @@ /// lookup tables. static bool SwitchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder, DomTreeUpdater *DTU, const DataLayout &DL, - const TargetTransformInfo &TTI) { + const TargetTransformInfo &TTI, + bool EnableRelativeTable) { assert(SI->getNumCases() > 1 && "Degenerate switch?"); BasicBlock *BB = SI->getParent(); @@ -6030,9 +6093,10 @@ Constant *DV = NeedMask ? ResultLists[PHI][0].second : DefaultResults[PHI]; StringRef FuncName = Fn->getName(); SwitchLookupTable Table(Mod, TableSize, MinCaseVal, ResultList, DV, DL, - FuncName); + FuncName, Builder, EnableRelativeTable); - Value *Result = Table.BuildLookup(TableIndex, Builder); + Type *ValueType = ResultList.begin()->second->getType(); + Value *Result = Table.BuildLookup(TableIndex, ValueType, Builder); // If the result is used to return immediately from the function, we want to // do that right here. @@ -6229,7 +6293,8 @@ // CVP. Therefore, only apply this transformation during late stages of the // optimisation pipeline. if (Options.ConvertSwitchToLookupTable && - SwitchToLookupTable(SI, Builder, DTU, DL, TTI)) + SwitchToLookupTable(SI, Builder, DTU, DL, TTI, + Options.RelativeSwitchLookupTable)) return requestResimplify(); if (ReduceSwitchRange(SI, Builder, DL, TTI)) diff --git a/llvm/test/Transforms/SimplifyCFG/ARM/switch-to-relative-lookup-table.ll b/llvm/test/Transforms/SimplifyCFG/ARM/switch-to-relative-lookup-table.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/ARM/switch-to-relative-lookup-table.ll @@ -0,0 +1,38 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -simplifycfg -simplifycfg-require-and-preserve-domtree=1 -switch-to-lookup -relative-switch-lookup-table -mtriple=arm -relocation-model=pic < %s | FileCheck %s +; RUN: opt -S -passes='simplify-cfg' -mtriple=arm -relocation-model=pic < %s | FileCheck %s + +; CHECK: @{{.*}} = private unnamed_addr constant [3 x i32] [i32 trunc (i64 sub (i64 ptrtoint (i32* @c1 to i64), i64 ptrtoint ([3 x i32]* @switch.table.test to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32* @c2 to i64), i64 ptrtoint ([3 x i32]* @switch.table.test to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (i32* @c3 to i64), i64 ptrtoint ([3 x i32]* @switch.table.test to i64)) to i32)], align 4 + +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv7a--none-eabi" + +@c1 = external constant i32, align 4 +@c2 = external constant i32, align 4 +@c3 = external constant i32, align 4 +@c4 = external constant i32, align 4 + +define i32* @test(i32 %n) { +entry: + switch i32 %n, label %sw.default [ + i32 0, label %sw.bb + i32 1, label %sw.bb1 + i32 2, label %sw.bb2 + ] + +sw.bb: + br label %return + +sw.bb1: + br label %return + +sw.bb2: + br label %return + +sw.default: + br label %return + +return: + %retval.0 = phi i32* [ @c4, %sw.default ], [ @c3, %sw.bb2 ], [ @c2, %sw.bb1 ], [ @c1, %sw.bb ] + ret i32* %retval.0 +} diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_relative_lookup_table.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_relative_lookup_table.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_relative_lookup_table.ll @@ -0,0 +1,138 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -simplifycfg -switch-to-lookup=true -relative-switch-lookup-table=true -keep-loops=false -S -mtriple=x86_64-unknown-linux-gnu | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@.str = private unnamed_addr constant [5 x i8] c"zero\00", align 1 +@.str.1 = private unnamed_addr constant [4 x i8] c"one\00", align 1 +@.str.2 = private unnamed_addr constant [4 x i8] c"two\00", align 1 +@.str.3 = private unnamed_addr constant [6 x i8] c"three\00", align 1 +@.str.4 = private unnamed_addr constant [5 x i8] c"four\00", align 1 +@.str.5 = private unnamed_addr constant [5 x i8] c"five\00", align 1 +@.str.6 = private unnamed_addr constant [4 x i8] c"six\00", align 1 +@.str.7 = private unnamed_addr constant [6 x i8] c"seven\00", align 1 +@.str.8 = private unnamed_addr constant [6 x i8] c"eight\00", align 1 +@.str.9 = private unnamed_addr constant [8 x i8] c"default\00", align 1 + +; Relative string table lookup +; CHECK: @switch.table.string_relative_table = private unnamed_addr constant [9 x i32] [i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.3 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str.4 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str.5 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.6 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.7 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.8 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64)) to i32)], align 8 + +; Relative string table lookup that holes are filled with relative offset to default values +; CHECK: @switch.table.string_relative_table_holes = private unnamed_addr constant [9 x i32] [i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([8 x i8]* @.str.9 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.3 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str.4 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str.5 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([8 x i8]* @.str.9 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.7 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.8 to i64), i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64)) to i32)], align 8 + +; Switch used to return a string. +; Relative lookup table should be generated. + +define i8* @string_relative_table(i32 %cond) { +; CHECK-LABEL: @string_relative_table( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 9 +; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] +; CHECK: switch.lookup: +; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [9 x i32], [9 x i32]* @switch.table.string_relative_table, i32 0, i32 [[X]] +; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, i32* [[SWITCH_GEP]], align 4 +; CHECK-NEXT: [[SWITCH_RELTABLE_OFFSET_SEXT:%.*]] = sext i32 [[SWITCH_LOAD]] to i64 +; CHECK-NEXT: [[SWITCH_RELTABLE_ADD:%.*]] = add i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table to i64), [[SWITCH_RELTABLE_OFFSET_SEXT]] +; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[SWITCH_RELTABLE_ADD]] to i8* +; CHECK-NEXT: ret i8* [[TMP1]] +; CHECK: return: +; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.9, i64 0, i64 0) +; +entry: + switch i32 %cond, label %sw.default [ + i32 0, label %return + i32 1, label %sw.bb1 + i32 2, label %sw.bb2 + i32 3, label %sw.bb3 + i32 4, label %sw.bb4 + i32 5, label %sw.bb5 + i32 6, label %sw.bb6 + i32 7, label %sw.bb7 + i32 8, label %sw.bb8 + ] + +sw.bb1: ; preds = %entry + br label %return + +sw.bb2: ; preds = %entry + br label %return + +sw.bb3: ; preds = %entry + br label %return + +sw.bb4: ; preds = %entry + br label %return + +sw.bb5: ; preds = %entry + br label %return + +sw.bb6: ; preds = %entry + br label %return + +sw.bb7: ; preds = %entry + br label %return + +sw.bb8: ; preds = %entry + br label %return + +sw.default: ; preds = %entry + br label %return + +return: ; preds = %entry, %sw.default, %sw.bb8, %sw.bb7, %sw.bb6, %sw.bb5, %sw.bb4, %sw.bb3, %sw.bb2, %sw.bb1 + %retval.0 = phi i8* [ getelementptr inbounds ([8 x i8], [8 x i8]* @.str.9, i64 0, i64 0), %sw.default ], [ getelementptr inbounds ([6 x i8], [6 x i8]* @.str.8, i64 0, i64 0), %sw.bb8 ], [ getelementptr inbounds ([6 x i8], [6 x i8]* @.str.7, i64 0, i64 0), %sw.bb7 ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @.str.6, i64 0, i64 0), %sw.bb6 ], [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0), %sw.bb5 ], [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.4, i64 0, i64 0), %sw.bb4 ], [ getelementptr inbounds ([6 x i8], [6 x i8]* @.str.3, i64 0, i64 0), %sw.bb3 ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), %sw.bb2 ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0), %sw.bb1 ], [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), %entry ] + ret i8* %retval.0 +} + +; Fill the holes with offset of the default value in the relative lookup table + +define i8* @string_relative_table_holes(i32 %cond) { +; CHECK-LABEL: @string_relative_table_holes( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 9 +; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] +; CHECK: switch.lookup: +; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [9 x i32], [9 x i32]* @switch.table.string_relative_table_holes, i32 0, i32 [[X]] +; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, i32* [[SWITCH_GEP]], align 4 +; CHECK-NEXT: [[SWITCH_RELTABLE_OFFSET_SEXT:%.*]] = sext i32 [[SWITCH_LOAD]] to i64 +; CHECK-NEXT: [[SWITCH_RELTABLE_ADD:%.*]] = add i64 ptrtoint ([9 x i32]* @switch.table.string_relative_table_holes to i64), [[SWITCH_RELTABLE_OFFSET_SEXT]] +; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[SWITCH_RELTABLE_ADD]] to i8* +; CHECK-NEXT: ret i8* [[TMP1]] +; CHECK: return: +; CHECK-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.9, i64 0, i64 0) +; +entry: + switch i32 %cond, label %sw.default [ + i32 0, label %return + i32 2, label %sw.bb1 + i32 3, label %sw.bb2 + i32 4, label %sw.bb3 + i32 5, label %sw.bb4 + i32 7, label %sw.bb5 + i32 8, label %sw.bb6 + ] + +sw.bb1: ; preds = %entry + br label %return + +sw.bb2: ; preds = %entry + br label %return + +sw.bb3: ; preds = %entry + br label %return + +sw.bb4: ; preds = %entry + br label %return + +sw.bb5: ; preds = %entry + br label %return + +sw.bb6: ; preds = %entry + br label %return + +sw.default: ; preds = %entry + br label %return + +return: ; preds = %entry, %sw.default, %sw.bb6, %sw.bb5, %sw.bb4, %sw.bb3, %sw.bb2, %sw.bb1 + %retval.0 = phi i8* [ getelementptr inbounds ([8 x i8], [8 x i8]* @.str.9, i64 0, i64 0), %sw.default ], [ getelementptr inbounds ([6 x i8], [6 x i8]* @.str.8, i64 0, i64 0), %sw.bb6 ], [ getelementptr inbounds ([6 x i8], [6 x i8]* @.str.7, i64 0, i64 0), %sw.bb5 ], [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0), %sw.bb4 ], [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.4, i64 0, i64 0), %sw.bb3 ], [ getelementptr inbounds ([6 x i8], [6 x i8]* @.str.3, i64 0, i64 0), %sw.bb2 ], [ getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), %sw.bb1 ], [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), %entry ] + ret i8* %retval.0 +}