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,17 +5460,28 @@ 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); + + /// Determines if a relative table should be generated. + /// Returns true if case results are pointers and they are dso_local. + /// Otherwise, returns false. + static bool ShouldBuildRelLookupTable( + const SmallVectorImpl> &Values); + + /// Generate an offset between the lookup table and given case result. + static Constant *GenerateRelOffset(GlobalVariable *Array, Constant *CaseRes, + IRBuilder<> &Builder); /// 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(Module &M, Value *Index, Type *ValueType, + IRBuilder<> &Builder); /// Return true if a table with TableSize elements of /// type ElementType would fit in a target-legal register. static bool WouldFitInRegister(const DataLayout &DL, uint64_t TableSize, Type *ElementType); - private: // Depending on the contents of the table, it can be represented in // different ways. @@ -5491,7 +5502,12 @@ // The table is stored as an array of values. Values are retrieved by load // instructions from the table. - ArrayKind + ArrayKind, + + // The table is stored as an array of relative offsets between + // the lookup table and case results. + // Values are retrieved by shift and load.relative intrinsic call. + RelOffsetArrayKind } Kind; // For SingleValueKind, this is the single value. @@ -5514,7 +5530,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) { assert(Values.size() && "Can't build lookup table without values!"); assert(TableSize >= Values.size() && "Can't fit values in table!"); @@ -5523,6 +5540,15 @@ Type *ValueType = Values.begin()->second->getType(); + /// Check if relative lookup table should be generated + if (ShouldBuildRelLookupTable(Values)) { + ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), TableSize); + Array = new GlobalVariable(M, ArrayTy, /*isConstant=*/true, + GlobalVariable::PrivateLinkage, nullptr, + "switch.reltable." + FuncName); + Kind = RelOffsetArrayKind; + } + // Build up the table contents. SmallVector TableContents(TableSize); for (size_t I = 0, E = Values.size(); I != E; ++I) { @@ -5531,7 +5557,12 @@ 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 (Kind == RelOffsetArrayKind) + TableContents[Idx] = GenerateRelOffset(Array, CaseRes, Builder); + else + TableContents[Idx] = CaseRes; if (CaseRes != SingleValue) SingleValue = nullptr; @@ -5542,9 +5573,18 @@ assert(DefaultValue && "Need a default value to fill the lookup table holes."); assert(DefaultValue->getType() == ValueType); + + Constant *DefaultValueOffset = nullptr; + if (Kind == RelOffsetArrayKind) + DefaultValueOffset = GenerateRelOffset(Array, DefaultValue, Builder); + for (uint64_t I = 0; I < TableSize; ++I) { - if (!TableContents[I]) - TableContents[I] = DefaultValue; + if (!TableContents[I]) { + if (Kind == RelOffsetArrayKind) + TableContents[I] = DefaultValueOffset; + else + TableContents[I] = DefaultValue; + } } if (DefaultValue != SingleValue) @@ -5614,21 +5654,59 @@ 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 (Kind == RelOffsetArrayKind) { + ArrayType *ArrayTy = ArrayType::get(Builder.getInt32Ty(), TableSize); + Constant *Initializer = ConstantArray::get(ArrayTy, TableContents); + Array->setInitializer(Initializer); + Array->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + Array->setAlignment(Align(DL.getPrefTypeAlignment(Builder.getInt32Ty()))); + } 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->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + Array->setAlignment(Align(DL.getPrefTypeAlignment(ValueType))); + Kind = ArrayKind; + } +} + +bool SwitchLookupTable::ShouldBuildRelLookupTable( + const SmallVectorImpl> &Values) { + Type *ValueType = Values.begin()->second->getType(); - 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. - Array->setAlignment(Align(DL.getPrefTypeAlignment(ValueType))); - Kind = ArrayKind; + if (ValueType->isPointerTy()) { + bool IsDSOLocal = true; + for (size_t I = 0, E = Values.size(); I != E; ++I) { + Constant *CaseRes = Values[I].second; + if (auto *GlobalVal = dyn_cast(CaseRes)) + if (!GlobalVal->isDSOLocal()) + IsDSOLocal = false; + + if (!IsDSOLocal) + return false; + } + return true; + } + + return false; } -Value *SwitchLookupTable::BuildLookup(Value *Index, IRBuilder<> &Builder) { +Constant *SwitchLookupTable::GenerateRelOffset(GlobalVariable *Array, + Constant *CaseRes, + IRBuilder<> &Builder) { + Constant *Base = llvm::ConstantExpr::getPtrToInt(Array, Builder.getInt64Ty()); + Constant *Target = + llvm::ConstantExpr::getPtrToInt(CaseRes, Builder.getInt64Ty()); + Constant *RelOffset = llvm::ConstantExpr::getSub(Target, Base); + return llvm::ConstantExpr::getTrunc(RelOffset, Builder.getInt32Ty()); +} + +Value *SwitchLookupTable::BuildLookup(Module &M, Value *Index, Type *ValueType, + IRBuilder<> &Builder) { switch (Kind) { case SingleValueKind: return SingleValue; @@ -5679,6 +5757,31 @@ cast(Array->getValueType())->getElementType(), GEP, "switch.load"); } + case RelOffsetArrayKind: { + // Make sure the table index will not overflow when treated as signed. + IntegerType *IT = cast(Index->getType()); + uint64_t TableSize = + Array->getInitializer()->getType()->getArrayNumElements(); + if (TableSize > (1ULL << (IT->getBitWidth() - 1))) + Index = Builder.CreateZExt( + Index, IntegerType::get(IT->getContext(), IT->getBitWidth() + 1), + "switch.tableidx.zext"); + + Constant *Base = + llvm::ConstantExpr::getBitCast(Array, Builder.getInt8PtrTy()); + // Shift index by 2 to compute the offset. + Value *Offset = + Builder.CreateLShr(Index, Builder.getInt32(2), "switch.reltable.shift"); + + Function *LoadRelIntrinsic = llvm::Intrinsic::getDeclaration( + &M, Intrinsic::load_relative, {Index->getType()}); + + // Call load relative intrinsic that computes the target address by adding + // base address (lookup table address) and relative offset. + Value *Call = Builder.CreateCall(LoadRelIntrinsic, {Base, Offset}, + "switch.reltable.intrinsic"); + return Builder.CreateBitCast(Call, ValueType); + } } llvm_unreachable("Unknown lookup table kind!"); } @@ -6030,9 +6133,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); - Value *Result = Table.BuildLookup(TableIndex, Builder); + Type *ValueType = ResultList.begin()->second->getType(); + Value *Result = Table.BuildLookup(Mod, TableIndex, ValueType, Builder); // If the result is used to return immediately from the function, we want to // do that right here. diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll --- a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll +++ b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll @@ -14,7 +14,7 @@ ; CHECK: @switch.table.h = private unnamed_addr constant [4 x float] [float 0x40091EB860000000, float 0x3FF3BE76C0000000, float 0x4012449BA0000000, float 0x4001AE1480000000], align 4 ; The table for @foostring -; CHECK: @switch.table.foostring = private unnamed_addr constant [4 x i8*] [i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str1, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str2, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str3, i64 0, i64 0)], align 8 +; CHECK: @switch.reltable.foostring = private unnamed_addr constant [4 x i32] [i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.foostring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str1 to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.foostring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str2 to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.foostring to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str3 to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.foostring to i64)) to i32)], align 4 ; The table for @earlyreturncrash ; CHECK: @switch.table.earlyreturncrash = private unnamed_addr constant [4 x i32] [i32 42, i32 9, i32 88, i32 5], align 4 @@ -170,9 +170,10 @@ ; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 4 ; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] ; CHECK: switch.lookup: -; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x i8*], [4 x i8*]* @switch.table.foostring, i32 0, i32 [[X]] -; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i8*, i8** [[SWITCH_GEP]], align 8 -; CHECK-NEXT: ret i8* [[SWITCH_LOAD]] +; CHECK-NEXT: [[SWITCH_RELTABLE_SHIFT:%.*]] = lshr i32 %x, 2 +; CHECK-NEXT: [[SWITCH_RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([4 x i32]* @switch.reltable.foostring to i8*), i32 [[SWITCH_RELTABLE_SHIFT]]) +; CHECK-NEXT: ret i8* [[SWITCH_RELTABLE_INTRINSIC]] + ; CHECK: return: ; CHECK-NEXT: ret i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str4, i64 0, i64 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,210 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -simplifycfg -switch-to-lookup=true -keep-loops=false -S -mtriple=x86_64-unknown-linux-gnu | FileCheck %s +; RUN: opt < %s -passes='simplify-cfg' -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.reltable.string_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.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.3 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str.4 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str.5 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.6 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.7 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.8 to i64), i64 ptrtoint ([9 x i32]* @switch.reltable.string_table to i64)) to i32)], align 4 + +; Relative string table lookup that holes are filled with relative offset to default values +; CHECK: @switch.reltable.string_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.reltable.string_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.reltable.string_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.reltable.string_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.reltable.string_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.reltable.string_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.reltable.string_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.reltable.string_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.reltable.string_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.reltable.string_table_holes to i64)) to i32)], align 4 + +; Relative string table lookup that holes are filled with relative offset to default values +; CHECK: @switch.table.no_dso_local = private unnamed_addr constant [7 x i32*] [i32* @a, i32* @b, i32* @c, i32* @d, i32* @e, i32* @f, i32* @g], align 8 + +; Switch used to return a string. +; Relative lookup table should be generated. + +define i8* @string_table(i32 %cond) { +; CHECK-LABEL: @string_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_RELTABLE_SHIFT:%.*]] = lshr i32 %cond, 2 +; CHECK-NEXT: [[SWITCH_RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([9 x i32]* @switch.reltable.string_table to i8*), i32 [[SWITCH_RELTABLE_SHIFT]]) +; CHECK-NEXT: ret i8* [[SWITCH_RELTABLE_INTRINSIC]] +; 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_table_holes(i32 %cond) { +; CHECK-LABEL: @string_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_RELTABLE_SHIFT:%.*]] = lshr i32 %cond, 2 +; CHECK-NEXT: [[SWITCH_RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([9 x i32]* @switch.reltable.string_table_holes to i8*), i32 [[SWITCH_RELTABLE_SHIFT]]) +; CHECK-NEXT: ret i8* [[SWITCH_RELTABLE_INTRINSIC]] +; 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 +} + +@a = external global i32, align 4 +@b = external global i32, align 4 +@c = external global i32, align 4 +@d = external global i32, align 4 +@e = external global i32, align 4 +@f = external global i32, align 4 +@g = external global i32, align 4 +@h = external global i32, align 4 + +; If pointers are not dso_local, relative lookup table should not be generated. +define i32* @no_dso_local(i32 %cond) { +; CHECK-LABEL: @no_dso_local( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 7 +; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] +; CHECK: switch.lookup: +; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [7 x i32*], [7 x i32*]* @switch.table.no_dso_local, i32 0, i32 %cond +; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32*, i32** [[SWITCH_GEP]], align 8 +; CHECK-NEXT: ret i32* [[SWITCH_LOAD]] +; CHECK: return: +; CHECK-NEXT: ret i32* @h +; +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 + ] + +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 i32* [ @h, %sw.default ], [ @g, %sw.bb6 ], [ @f, %sw.bb5 ], [ @e, %sw.bb4 ], [ @d, %sw.bb3 ], [ @c, %sw.bb2 ], [ @b, %sw.bb1 ], [ @a, %entry ] + ret i32* %retval.0 +} + +; define nonnull i32* @foo(i32 %cond) local_unnamed_addr #0 { +; entry: +; %0 = icmp ult i32 %cond, 7 +; br i1 %0, label %switch.lookup, label %return + +; switch.lookup: ; preds = %entry +; %1 = sext i32 %cond to i64 +; %switch.gep = getelementptr inbounds [7 x i32*], [7 x i32*]* @switch.table.main, i64 0, i64 %1 +; %switch.load = load i32*, i32** %switch.gep, align 8 +; ret i32* %switch.load + +; return: ; preds = %entry +; ret i32* @h +; }