gulfem updated this revision to Diff 320025. gulfem added a comment. 1. Simplified test cases and increased readibility of the tables 2. Added x86_64 and aarch64 check and tiny or small code modes check to ensure 32 offsets 3. Modified single value test case
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D94355/new/ https://reviews.llvm.org/D94355 Files: clang/test/CodeGen/switch-to-lookup-table.c llvm/lib/Transforms/Utils/SimplifyCFG.cpp llvm/test/Transforms/SimplifyCFG/X86/switch_to_relative_lookup_table.ll
Index: llvm/test/Transforms/SimplifyCFG/X86/switch_to_relative_lookup_table.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/SimplifyCFG/X86/switch_to_relative_lookup_table.ll @@ -0,0 +1,211 @@ +; 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<switch-to-lookup;no-keep-loops>' -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 [8 x i8] c"default\00", align 1 +@.str.4 = private unnamed_addr constant [6 x i8] c"three\00", align 1 +@.str.5 = private unnamed_addr constant [5 x i8] c"str1\00", align 1 +@.str.6 = private unnamed_addr constant [5 x i8] c"str2\00", align 1 +@.str.7 = private unnamed_addr constant [12 x i8] c"singlevalue\00", align 1 + +; Relative string table lookup +; CHECK: @switch.reltable.string_table = private unnamed_addr constant [3 x i32] +; CHECK-SAME: [ +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.string_table to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.string_table to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.string_table to i64)) to i32) +; CHECK-SAME: ], 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 [4 x i32] +; CHECK-SAME: [ +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.string_table_holes to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([8 x i8]* @.str.3 to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.string_table_holes to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.string_table_holes to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([6 x i8]* @.str.4 to i64), i64 ptrtoint ([4 x i32]* @switch.reltable.string_table_holes to i64)) to i32) +; CHECK-SAME: ], align 4 + +; Integer pointer table lookup +; 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 + +; Single value check +; CHECK: @switch.reltable.single_value = private unnamed_addr constant [3 x i32] +; CHECK-SAME: [ +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.single_value to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.single_value to i64)) to i32), +; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.single_value to i64)) to i32) +; CHECK-SAME: ], align 4 + +; 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 [[COND:%.*]], 3 + ; 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 ([3 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.3, 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 + ] + +sw.bb1: ; preds = %entry + br label %return + +sw.bb2: ; preds = %entry + br label %return + +sw.default: ; preds = %entry + br label %return + +return: ; preds = %entry, %sw.default, %sw.bb2, %sw.bb1 + %retval.0 = phi i8* [ getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0), %sw.default ], [ 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 [[COND:%.*]], 4 +; 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 ([4 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.3, 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 + ] + +sw.bb1: ; preds = %entry + br label %return + +sw.bb2: ; preds = %entry + br label %return + +sw.default: ; preds = %entry + br label %return + +return: ; preds = %entry, %sw.default, %sw.bb2, %sw.bb1 + %retval.0 = phi i8* [ getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0), %sw.default ], [ getelementptr inbounds ([6 x i8], [6 x i8]* @.str.4, 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 [[COND:%.*]], 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 +} + +; Single value check +; If there is a lookup table, where each element contains the same value, +; a relative lookup should not be generated +define void @single_value(i32 %cond) { +; CHECK-LABEL: @single_value( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3 +; 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 ([3 x i32]* @switch.reltable.single_value to i8*), i32 [[SWITCH_RELTABLE_SHIFT]]) +; CHECK: sw.epilog: +; CHECK-NEXT: [[STR1:%.*]] = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0), %entry ], [ getelementptr inbounds ([12 x i8], [12 x i8]* @.str.7, i64 0, i64 0), %switch.lookup ] +; CHECK-NEXT: [[STR2:%.*]] = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.6, i64 0, i64 0), %entry ], [ %switch.reltable.intrinsic, %switch.lookup ] +; CHECK-NEXT: ret void + +entry: + switch i32 %cond, label %sw.epilog [ + i32 0, label %sw.bb + i32 1, label %sw.bb1 + i32 2, label %sw.bb2 + ] + +sw.bb: ; preds = %entry + br label %sw.epilog + +sw.bb1: ; preds = %entry + br label %sw.epilog + +sw.bb2: ; preds = %entry + br label %sw.epilog + +sw.epilog: ; preds = %entry, %sw.bb2, %sw.bb1, %sw.bb + %str1.0 = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0), %entry ], [ getelementptr inbounds ([12 x i8], [12 x i8]* @.str.7, i64 0, i64 0), %sw.bb2 ], [ getelementptr inbounds ([12 x i8], [12 x i8]* @.str.7, i64 0, i64 0), %sw.bb1 ], [ getelementptr inbounds ([12 x i8], [12 x i8]* @.str.7, i64 0, i64 0), %sw.bb ] + %str2.0 = phi i8* [ getelementptr inbounds ([5 x i8], [5 x i8]* @.str.6, i64 0, i64 0), %entry ], [ 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), %sw.bb ] + ret void +} + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 7, !"PIC Level", i32 2} +!1 = !{i32 1, !"Code Model", i32 1} Index: llvm/lib/Transforms/Utils/SimplifyCFG.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -5297,13 +5297,12 @@ /// 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. 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. @@ -5324,7 +5323,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. @@ -5340,6 +5344,15 @@ // For ArrayKind, this is the array. GlobalVariable *Array = nullptr; + + /// Determines whether a relative table should be generated. + static bool ShouldBuildRelLookupTable( + Module &M, + const SmallVectorImpl<std::pair<ConstantInt *, Constant *>> &Values); + + /// Generate an offset between the lookup table and given case result. + static Constant *GenerateRelOffset(Module &M, GlobalVariable *Array, + Constant *CaseRes); }; } // end anonymous namespace @@ -5447,21 +5460,111 @@ return; } - // Store the table in an array. - ArrayType *ArrayTy = ArrayType::get(ValueType, TableSize); - Constant *Initializer = ConstantArray::get(ArrayTy, TableContents); + // Check if relative lookup table should be generated. + if (ShouldBuildRelLookupTable(M, Values)) { + ArrayType *ArrayTy = + ArrayType::get(Type::getInt32Ty(M.getContext()), TableSize); + + Array = new GlobalVariable(M, ArrayTy, /*isConstant=*/true, + GlobalVariable::PrivateLinkage, nullptr, + "switch.reltable." + FuncName); + + for (size_t I = 0, E = Values.size(); I != E; ++I) { + ConstantInt *CaseVal = Values[I].first; + Constant *CaseRes = Values[I].second; + uint64_t Idx = + (CaseVal->getValue() - Offset->getValue()).getLimitedValue(); + + // If relative lookup table is enabled, put relative offsets into table. + TableContents[Idx] = GenerateRelOffset(M, Array, CaseRes); + } + + if (Values.size() < TableSize) { + Constant *DefaultValueOffset = GenerateRelOffset(M, Array, DefaultValue); + for (uint64_t I = 0; I < TableSize; ++I) { + if (TableContents[I] == DefaultValue) + TableContents[I] = DefaultValueOffset; + } + } - 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; + Constant *Initializer = ConstantArray::get(ArrayTy, TableContents); + Array->setInitializer(Initializer); + Array->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + Array->setAlignment( + Align(DL.getPrefTypeAlignment(Type::getInt32Ty(M.getContext())))); + Kind = RelOffsetArrayKind; + } 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; + } } -Value *SwitchLookupTable::BuildLookup(Value *Index, IRBuilder<> &Builder) { +bool SwitchLookupTable::ShouldBuildRelLookupTable( + Module &M, + const SmallVectorImpl<std::pair<ConstantInt *, Constant *>> &Values) { + // If not in x86 or aarch64 mode, do not generate a relative lookup table. + Triple TargetTriple(M.getTargetTriple()); + if (!(TargetTriple.getArch() == Triple::x86_64 || + TargetTriple.getArch() == Triple::aarch64)) + return false; + + // If not tiny or small code model, do not generate a relative lookup table. + Optional<CodeModel::Model> CodeModel = M.getCodeModel(); + if (!(CodeModel == CodeModel::Tiny || CodeModel == CodeModel::Small)) + return false; + + // If not in PIC mode, do not generate a relative lookup table. + if (M.getPICLevel() == PICLevel::NotPIC) + return false; + + Type *ValueType = Values.begin()->second->getType(); + // If values are not pointers, do not generate a relative lookup table. + if (!ValueType->isPointerTy()) + return false; + + /// If any of the pointer values is not a global value and dso_local, + /// do not generate a relative lookup table. + for (size_t I = 0, E = Values.size(); I != E; ++I) { + Constant *CaseRes = Values[I].second; + GlobalValue *GlobalVal = dyn_cast<GlobalValue>(CaseRes); + + // Handle a constantexpr gep. + if (ConstantExpr *CE = dyn_cast<ConstantExpr>(CaseRes)) { + if (CE->getOpcode() == Instruction::GetElementPtr) + GlobalVal = dyn_cast<GlobalValue>(CE->getOperand(0)); + } + + /// If any of the pointer values is not a global value or is not dso_local, + /// do not generate a relative lookup table. + if (GlobalVal == nullptr) + return false; + else if (!(GlobalVal->isDSOLocal() || + GlobalValue::isLocalLinkage(GlobalVal->getLinkage()))) + return false; + } + + return true; +} + +Constant *SwitchLookupTable::GenerateRelOffset(Module &M, GlobalVariable *Array, + Constant *CaseRes) { + Type *IntPtrTy = M.getDataLayout().getIntPtrType(M.getContext()); + Constant *Base = llvm::ConstantExpr::getPtrToInt(Array, IntPtrTy); + Constant *Target = llvm::ConstantExpr::getPtrToInt(CaseRes, IntPtrTy); + Constant *RelOffset = llvm::ConstantExpr::getSub(Target, Base); + return llvm::ConstantExpr::getTrunc(RelOffset, + Type::getInt32Ty(M.getContext())); +} + +Value *SwitchLookupTable::BuildLookup(Value *Index, Type *ValueType, + IRBuilder<> &Builder) { switch (Kind) { case SingleValueKind: return SingleValue; @@ -5495,7 +5598,8 @@ // Mask off. return Builder.CreateTrunc(DownShifted, BitMapElementTy, "switch.masked"); } - case ArrayKind: { + case ArrayKind: + case RelOffsetArrayKind: { // Make sure the table index will not overflow when treated as signed. IntegerType *IT = cast<IntegerType>(Index->getType()); uint64_t TableSize = @@ -5505,12 +5609,31 @@ Index, IntegerType::get(IT->getContext(), IT->getBitWidth() + 1), "switch.tableidx.zext"); - Value *GEPIndices[] = {Builder.getInt32(0), Index}; - Value *GEP = Builder.CreateInBoundsGEP(Array->getValueType(), Array, - GEPIndices, "switch.gep"); - return Builder.CreateLoad( - cast<ArrayType>(Array->getValueType())->getElementType(), GEP, - "switch.load"); + if (Kind == ArrayKind) { + Value *GEPIndices[] = {Builder.getInt32(0), Index}; + Value *GEP = Builder.CreateInBoundsGEP(Array->getValueType(), Array, + GEPIndices, "switch.gep"); + return Builder.CreateLoad( + cast<ArrayType>(Array->getValueType())->getElementType(), GEP, + "switch.load"); + } else { + 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"); + + llvm::Module *Module = Builder.GetInsertBlock()->getModule(); + Function *LoadRelIntrinsic = llvm::Intrinsic::getDeclaration( + Module, 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!"); @@ -5864,7 +5987,8 @@ SwitchLookupTable Table(Mod, TableSize, MinCaseVal, ResultList, DV, DL, FuncName); - 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. Index: clang/test/CodeGen/switch-to-lookup-table.c =================================================================== --- /dev/null +++ clang/test/CodeGen/switch-to-lookup-table.c @@ -0,0 +1,55 @@ +// Check switch to lookup optimization in fPIC and fno-PIC mode +// RUN: %clang %s -target x86_64-linux -O2 -fno-PIC -fno-discard-value-names -S -emit-llvm -o - | FileCheck %s --check-prefix=FNOPIC +// RUN: %clang %s -target x86_64-linux -O2 -fPIC -fno-discard-value-names -mcmodel=small -S -emit-llvm -o - | FileCheck %s --check-prefix=FPIC + +// Switch lookup table +// FNOPIC: @switch.table.string_table = private unnamed_addr constant [3 x i8*] +// FNOPIC-SAME: [ +// FNOPIC-SAME: i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), +// FNOPIC-SAME: i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0), +// FNOPIC-SAME: i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0) +// FNOPIC-SAME: ], align 8 + +// Relative switch lookup table +// FPIC: @switch.reltable.string_table = private unnamed_addr constant [3 x i32] +// FPIC-SAME: [ +// FPIC-SAME: i32 trunc (i64 sub (i64 ptrtoint ([5 x i8]* @.str to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.string_table to i64)) to i32), +// FPIC-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.1 to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.string_table to i64)) to i32), +// FPIC-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8]* @.str.2 to i64), i64 ptrtoint ([3 x i32]* @switch.reltable.string_table to i64)) to i32) +// FPIC-SAME: ], align 4 +char* string_table(int cond) +{ + // FNOPIC-LABEL: @string_table( + // FNOPIC-NEXT: entry: + // FNOPIC-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3 + // FNOPIC-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] + // FNOPIC: switch.lookup: + // FNOPIC-NEXT: [[TMP1:%.*]] = sext i32 %cond to i64 + // FNOPIC-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [3 x i8*], [3 x i8*]* @switch.table.string_table, i64 0, i64 [[TMP1]] + // FNOPIC-NEXT: [[SWITCH_LOAD:%.*]] = load i8*, i8** [[SWITCH_GEP]], align 8 + // FNOPIC-NEXT: ret i8* [[SWITCH_LOAD]] + // FNOPIC: return: + // FNOPIC-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0) + + // FPIC-LABEL: @string_table( + // FPIC-NEXT: entry: + // FPIC-NEXT: [[TMP0:%.*]] = icmp ult i32 [[COND:%.*]], 3 + // FPIC-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]] + // FPIC: switch.lookup: + // FPIC-NEXT: [[SWITCH_RELTABLE_SHIFT:%.*]] = lshr i32 %cond, 2 + // FPIC-NEXT: [[SWITCH_RELTABLE_INTRINSIC:%.*]] = call i8* @llvm.load.relative.i32(i8* bitcast ([3 x i32]* @switch.reltable.string_table to i8*), i32 [[SWITCH_RELTABLE_SHIFT]]) + // FPIC-NEXT: ret i8* [[SWITCH_RELTABLE_INTRINSIC]] + // FPIC: return: + // FPIC-NEXT: ret i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i64 0, i64 0) + + switch (cond) { + case 0: + return "zero"; + case 1: + return "one"; + case 2: + return "two"; + default: + return "default"; + } +}
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits