From 27d4c9b71b426742197174b7415e166f3272975a Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Thu, 22 Dec 2016 22:22:35 +0000 Subject: [PATCH] [cfi] Emit jump tables as a function-level inline asm. Use a dummy private function with inline asm calls instead of module level asm blocks for CFI jumptables. The main advantage is that now jumptable codegen can be affected by the function attributes (like target_cpu on ARM). Module level asm gets the default subtarget based on the target triple, which is often not good enough. This change also uses asm constraints/arguments to reference jumptable targets and aliases directly. We no longer do asm name mangling in an IR pass. Differential Revision: https://reviews.llvm.org/D28012 llvm-svn: 290384 --- llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 222 +++++++++------------ .../Transforms/LowerTypeTests/function-disjoint.ll | 32 +-- .../test/Transforms/LowerTypeTests/function-ext.ll | 10 +- .../Transforms/LowerTypeTests/function-weak.ll | 29 ++- llvm/test/Transforms/LowerTypeTests/function.ll | 97 ++++----- llvm/test/Transforms/LowerTypeTests/section.ll | 7 +- 6 files changed, 159 insertions(+), 238 deletions(-) diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp index 753eb4c..2948878 100644 --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -23,9 +23,9 @@ #include "llvm/IR/GlobalObject.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Mangler.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/Pass.h" @@ -243,6 +243,7 @@ class LowerTypeTestsModule { bool LinkerSubsectionsViaSymbols; Triple::ArchType Arch; + Triple::OSType OS; Triple::ObjectFormatType ObjectFormat; IntegerType *Int1Ty = Type::getInt1Ty(M.getContext()); @@ -260,7 +261,6 @@ class LowerTypeTestsModule { std::vector ByteArrayInfos; - Mangler Mang; Function *WeakInitializerFn = nullptr; BitSetInfo @@ -281,9 +281,8 @@ class LowerTypeTestsModule { ArrayRef Globals); unsigned getJumpTableEntrySize(); Type *getJumpTableEntryType(); - void createJumpTableEntry(raw_ostream &OS, Function *Dest, unsigned Distance); - void createJumpTableAlias(raw_ostream &OS, Function *Dest, - GlobalVariable *JumpTable, unsigned Distance); + void createJumpTableEntry(raw_ostream &AsmOS, raw_ostream &ConstraintOS, + SmallVectorImpl &AsmArgs, Function *Dest); void verifyTypeMDNode(GlobalObject *GO, MDNode *Type); void buildBitSetsFromFunctions(ArrayRef TypeIds, ArrayRef Functions); @@ -299,6 +298,8 @@ class LowerTypeTestsModule { void findGlobalVariableUsersOf(Constant *C, SmallSetVector &Out); + void createJumpTable(Function *F, ArrayRef Functions); + public: LowerTypeTestsModule(Module &M); bool lower(); @@ -691,80 +692,27 @@ unsigned LowerTypeTestsModule::getJumpTableEntrySize() { } } -static bool isValidAsmUnquotedName(StringRef Name) { - if (Name.empty()) - return false; - - for (char C : Name) { - if (!((C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z') || - (C >= '0' && C <= '9') || C == '_' || C == '$' || C == '.' || - C == '@')) - return false; - } - - return true; -} - -// Create a constant representing a jump table entry for the target. This -// consists of an instruction sequence containing a relative branch to Dest. The -// constant will be laid out at address Src+(Len*Distance) where Len is the -// target-specific jump table entry size. -void LowerTypeTestsModule::createJumpTableEntry(raw_ostream &OS, Function *Dest, - unsigned Distance) { - // FIXME: replace IR Mangler with TargetLoweringObjectFile interface. - // A private instance of Mangler we use here can not deal with unnamed - // symbols, as it may create colliding labels. Thankfully(?), the use of - // inline asm requires us to give names to all affected functions anyway. - assert(Dest->hasName() && "jumptable targets can not be anonymous"); - SmallString<16> Name; - Mang.getNameWithPrefix(Name, Dest, /* CannotUsePrivateLabel */ false); - - if (!isValidAsmUnquotedName(Name)) { - // We are going to emit a function call as textual asm. Escaped strings - // in such expressions are not well supported. - report_fatal_error( - "CFI-ICall does not allow special characters in a function name."); - } +// Create a jump table entry for the target. This consists of an instruction +// sequence containing a relative branch to Dest. Appends inline asm text, +// constraints and arguments to AsmOS, ConstraintOS and AsmArgs. +void LowerTypeTestsModule::createJumpTableEntry( + raw_ostream &AsmOS, raw_ostream &ConstraintOS, + SmallVectorImpl &AsmArgs, Function *Dest) { + unsigned ArgIndex = AsmArgs.size(); if (Arch == Triple::x86 || Arch == Triple::x86_64) { - OS << "jmp " << Name << "@plt\n"; - OS << "int3\nint3\nint3\n"; + AsmOS << "jmp ${" << ArgIndex << ":c}@plt\n"; + AsmOS << "int3\nint3\nint3\n"; } else if (Arch == Triple::arm || Arch == Triple::aarch64) { - OS << "b " << Name << "\n"; + AsmOS << "b $" << ArgIndex << "\n"; } else if (Arch == Triple::thumb) { - OS << "b.w " << Name << "\n"; + AsmOS << "b.w $" << ArgIndex << "\n"; } else { report_fatal_error("Unsupported architecture for jump tables"); } -} - -void LowerTypeTestsModule::createJumpTableAlias(raw_ostream &OS, Function *Dest, - GlobalVariable *JumpTable, - unsigned Distance) { - assert(Dest->hasName() && "jumptable targets can not be anonymous"); - SmallString<16> Name; - Mang.getNameWithPrefix(Name, Dest, /* CannotUsePrivateLabel */ false); - - if (!isValidAsmUnquotedName(Name)) { - // We are going to emit a function alias as textual asm. Escaped strings - // in such expressions are not well supported. - report_fatal_error( - "CFI-ICall does not allow special characters in a function name."); - } - if (Dest->isWeakForLinker()) - OS << ".weak " << Name << "\n"; - else if (!Dest->hasLocalLinkage()) - OS << ".globl " << Name << "\n"; - OS << ".type " << Name << ", function\n"; - if (Arch == Triple::thumb) { - OS << ".thumb_set " << Name << ", " << JumpTable->getName() << " + " - << (getJumpTableEntrySize() * Distance) << "\n"; - } else { - OS << Name << " = " << JumpTable->getName() << " + " - << (getJumpTableEntrySize() * Distance) << "\n"; - } - OS << ".size " << Name << ", " << getJumpTableEntrySize() << "\n"; + ConstraintOS << (ArgIndex > 0 ? ",s" : "s"); + AsmArgs.push_back(Dest); } Type *LowerTypeTestsModule::getJumpTableEntryType() { @@ -844,6 +792,52 @@ void LowerTypeTestsModule::replaceWeakDeclarationWithJumpTablePtr( PlaceholderFn->eraseFromParent(); } +void LowerTypeTestsModule::createJumpTable( + Function *F, ArrayRef Functions) { + std::string AsmStr, ConstraintStr; + raw_string_ostream AsmOS(AsmStr), ConstraintOS(ConstraintStr); + SmallVector AsmArgs; + AsmArgs.reserve(Functions.size() * 2); + + for (unsigned I = 0; I != Functions.size(); ++I) + createJumpTableEntry(AsmOS, ConstraintOS, AsmArgs, + cast(Functions[I]->getGlobal())); + + // Try to emit the jump table at the end of the text segment. + // Jump table must come after __cfi_check in the cross-dso mode. + // FIXME: this magic section name seems to do the trick. + F->setSection(ObjectFormat == Triple::MachO + ? "__TEXT,__text,regular,pure_instructions" + : ".text.cfi"); + // Align the whole table by entry size. + F->setAlignment(getJumpTableEntrySize()); + // Skip prologue. + // Disabled on win32 due to https://llvm.org/bugs/show_bug.cgi?id=28641#c3. + // Luckily, this function does not get any prologue even without the + // attribute. + if (OS != Triple::Win32) + F->addFnAttr(llvm::Attribute::Naked); + // Thumb jump table assembly needs Thumb2. The following attribute is added by + // Clang for -march=armv7. + if (Arch == Triple::thumb) + F->addFnAttr("target-cpu", "cortex-a8"); + + BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F); + IRBuilder<> IRB(BB); + + SmallVector ArgTypes; + ArgTypes.reserve(AsmArgs.size()); + for (const auto &Arg : AsmArgs) + ArgTypes.push_back(Arg->getType()); + InlineAsm *JumpTableAsm = + InlineAsm::get(FunctionType::get(IRB.getVoidTy(), ArgTypes, false), + AsmOS.str(), ConstraintOS.str(), + /*hasSideEffects=*/true); + + IRB.CreateCall(JumpTableAsm, AsmArgs); + IRB.CreateUnreachable(); +} + /// Given a disjoint set of type identifiers and functions, build a jump table /// for the functions, build the bit sets and lower the llvm.type.test calls. void LowerTypeTestsModule::buildBitSetsFromFunctionsNative( @@ -933,88 +927,51 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsNative( for (unsigned I = 0; I != Functions.size(); ++I) GlobalLayout[Functions[I]] = I * EntrySize; - // Create a constant to hold the jump table. + Function *JumpTableFn = + Function::Create(FunctionType::get(Type::getVoidTy(M.getContext()), + /* IsVarArg */ false), + GlobalValue::PrivateLinkage, ".cfi.jumptable", &M); ArrayType *JumpTableType = ArrayType::get(getJumpTableEntryType(), Functions.size()); auto JumpTable = - new GlobalVariable(M, JumpTableType, - /*isConstant=*/true, GlobalValue::ExternalLinkage, - nullptr, ".cfi.jumptable"); - JumpTable->setVisibility(GlobalValue::HiddenVisibility); - lowerTypeTestCalls(TypeIds, JumpTable, GlobalLayout); + ConstantExpr::getPointerCast(JumpTableFn, JumpTableType->getPointerTo(0)); - std::string AsmStr; - raw_string_ostream AsmOS(AsmStr); + lowerTypeTestCalls(TypeIds, JumpTable, GlobalLayout); // Build aliases pointing to offsets into the jump table, and replace // references to the original functions with references to the aliases. for (unsigned I = 0; I != Functions.size(); ++I) { Function *F = cast(Functions[I]->getGlobal()); - // Need a name for the asm label. Normally, unnamed functions get temporary - // asm labels in TargetLoweringObjectFile but we don't have access to that - // here. - if (!F->hasName()) - F->setName("unnamed"); + Constant *CombinedGlobalElemPtr = ConstantExpr::getBitCast( + ConstantExpr::getInBoundsGetElementPtr( + JumpTableType, JumpTable, + ArrayRef{ConstantInt::get(IntPtrTy, 0), + ConstantInt::get(IntPtrTy, I)}), + F->getType()); if (LinkerSubsectionsViaSymbols || F->isDeclarationForLinker()) { - Constant *CombinedGlobalElemPtr = ConstantExpr::getBitCast( - ConstantExpr::getGetElementPtr( - JumpTableType, JumpTable, - ArrayRef{ConstantInt::get(IntPtrTy, 0), - ConstantInt::get(IntPtrTy, I)}), - F->getType()); - - if (F->isWeakForLinker()) { - AsmOS << ".weak " << F->getName() << "\n"; + + if (F->isWeakForLinker()) replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr); - } else { + else F->replaceAllUsesWith(CombinedGlobalElemPtr); - } } else { assert(F->getType()->getAddressSpace() == 0); - createJumpTableAlias(AsmOS, F, JumpTable, I); - - Function *DeclAlias = - Function::Create(cast(F->getValueType()), - GlobalValue::ExternalLinkage, "", &M); - // Since the alias (DeclAlias) is actually a declaration, it can not have - // internal linkage. Compensate for that by giving it hidden visibility. - // With this we end up with a GOT relocation against a local symbol. - DeclAlias->setVisibility(F->hasLocalLinkage() - ? GlobalValue::HiddenVisibility - : F->getVisibility()); - DeclAlias->takeName(F); - // Unnamed functions can not be added to llvm.used. - F->setName(DeclAlias->getName() + ".cfi"); - F->replaceAllUsesWith(DeclAlias); + GlobalAlias *FAlias = GlobalAlias::create(F->getValueType(), 0, + F->getLinkage(), "", + CombinedGlobalElemPtr, &M); + FAlias->setVisibility(F->getVisibility()); + FAlias->takeName(F); + if (FAlias->hasName()) + F->setName(FAlias->getName() + ".cfi"); + F->replaceAllUsesWith(FAlias); } if (!F->isDeclarationForLinker()) F->setLinkage(GlobalValue::InternalLinkage); } - // Try to emit the jump table at the end of the text segment. - // Jump table must come after __cfi_check in the cross-dso mode. - // FIXME: this magic section name seems to do the trick. - AsmOS << ".section " << (ObjectFormat == Triple::MachO - ? "__TEXT,__text,regular,pure_instructions" - : ".text.cfi, \"ax\", %progbits") - << "\n"; - // Align the whole table by entry size. - AsmOS << ".balign " << EntrySize << "\n"; - if (Arch == Triple::thumb) - AsmOS << ".thumb_func\n"; - AsmOS << JumpTable->getName() << ":\n"; - for (unsigned I = 0; I != Functions.size(); ++I) - createJumpTableEntry(AsmOS, cast(Functions[I]->getGlobal()), I); - - M.appendModuleInlineAsm(AsmOS.str()); - - SmallVector Used; - Used.reserve(Functions.size()); - for (auto *F : Functions) - Used.push_back(F->getGlobal()); - appendToUsed(M, Used); + createJumpTable(JumpTableFn, Functions); } /// Assign a dummy layout using an incrementing counter, tag each function @@ -1126,6 +1083,7 @@ LowerTypeTestsModule::LowerTypeTestsModule(Module &M) : M(M) { Triple TargetTriple(M.getTargetTriple()); LinkerSubsectionsViaSymbols = TargetTriple.isMacOSX(); Arch = TargetTriple.getArch(); + OS = TargetTriple.getOS(); ObjectFormat = TargetTriple.getObjectFormat(); } diff --git a/llvm/test/Transforms/LowerTypeTests/function-disjoint.ll b/llvm/test/Transforms/LowerTypeTests/function-disjoint.ll index 1d23687..0f9d4a3 100644 --- a/llvm/test/Transforms/LowerTypeTests/function-disjoint.ll +++ b/llvm/test/Transforms/LowerTypeTests/function-disjoint.ll @@ -5,25 +5,8 @@ target datalayout = "e-p:64:64" -; X64: module asm "f = .cfi.jumptable + 0" - -; X64: module asm ".cfi.jumptable:" -; X64-NEXT: module asm "jmp f.cfi@plt" -; X64-NEXT: module asm "int3" -; X64-NEXT: module asm "int3" -; X64-NEXT: module asm "int3" - -; X64: module asm "g = .cfi.jumptable.1 + 0" - -; X64: module asm ".cfi.jumptable.1:" -; X64-NEXT: module asm "jmp g.cfi@plt" -; X64-NEXT: module asm "int3" -; X64-NEXT: module asm "int3" -; X64-NEXT: module asm "int3" - - -; X64: @.cfi.jumptable = external hidden constant [1 x [8 x i8]] -; X64: @.cfi.jumptable.1 = external hidden constant [1 x [8 x i8]] +; X64: @f = alias void (), void ()* @[[JT0:.*]] +; X64: @g = alias void (), void ()* @[[JT1:.*]] ; WASM32: private constant [0 x i8] zeroinitializer @0 = private unnamed_addr constant [2 x void ()*] [void ()* @f, void ()* @g], align 16 @@ -46,18 +29,21 @@ define void @g() !type !1 { declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone define i1 @foo(i8* %p) { - ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x [8 x i8]]* @.cfi.jumptable to i64) + ; X64: icmp eq i64 {{.*}}, ptrtoint (void ()* @[[JT0]] to i64) ; WASM32: icmp eq i64 {{.*}}, 1 %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") - ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x [8 x i8]]* @.cfi.jumptable.1 to i64) + ; X64: icmp eq i64 {{.*}}, ptrtoint (void ()* @[[JT1]] to i64) ; WASM32: icmp eq i64 {{.*}}, 2 %y = call i1 @llvm.type.test(i8* %p, metadata !"typeid2") %z = add i1 %x, %y ret i1 %z } -; X64: declare void @f() -; X64: declare void @g() +; X64: define private void @[[JT0]]() #{{.*}} section ".text.cfi" align 8 { +; X64: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(void ()* @f.cfi) + +; X64: define private void @[[JT1]]() #{{.*}} section ".text.cfi" align 8 { +; X64: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(void ()* @g.cfi) ; WASM32: ![[I0]] = !{i64 1} ; WASM32: ![[I1]] = !{i64 2} \ No newline at end of file diff --git a/llvm/test/Transforms/LowerTypeTests/function-ext.ll b/llvm/test/Transforms/LowerTypeTests/function-ext.ll index b3b1f77..7ed4330 100644 --- a/llvm/test/Transforms/LowerTypeTests/function-ext.ll +++ b/llvm/test/Transforms/LowerTypeTests/function-ext.ll @@ -4,18 +4,13 @@ ; Tests that we correctly handle external references, including the case where ; all functions in a bitset are external references. -; X64: module asm ".cfi.jumptable:" -; X64-NEXT: module asm "jmp foo@plt" -; X64-NOT: module asm "jmp {{.*}}@plt" - -; X64: @.cfi.jumptable = external hidden constant [1 x [8 x i8]] ; WASM32: private constant [0 x i8] zeroinitializer ; WASM32: declare !type !{{[0-9]+}} void @foo() declare !type !0 void @foo() define i1 @bar(i8* %ptr) { - ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x [8 x i8]]* @.cfi.jumptable to i64) + ; X64: icmp eq i64 {{.*}}, ptrtoint (void ()* @[[JT:.*]] to i64) ; WASM32: sub i64 {{.*}}, 0 ; WASM32: icmp ult i64 {{.*}}, 1 %p = call i1 @llvm.type.test(i8* %ptr, metadata !"void") @@ -27,3 +22,6 @@ declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone !0 = !{i64 0, !"void"} ; WASM-NOT: !{i64 0} ; WASM-NOT: !{i64 1} + +; X64: define private void @[[JT]]() #{{.*}} section ".text.cfi" align {{.*}} { +; X64: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(void ()* @foo) diff --git a/llvm/test/Transforms/LowerTypeTests/function-weak.ll b/llvm/test/Transforms/LowerTypeTests/function-weak.ll index 840b2aa..e59641b 100644 --- a/llvm/test/Transforms/LowerTypeTests/function-weak.ll +++ b/llvm/test/Transforms/LowerTypeTests/function-weak.ll @@ -1,13 +1,11 @@ -; RUN: opt -S -lowertypetests -mtriple=i686-unknown-linux-gnu < %s | FileCheck %s -; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s -; RUN: opt -S -lowertypetests -mtriple=arm-unknown-linux-gnu < %s | FileCheck %s -; RUN: opt -S -lowertypetests -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck %s +; RUN: opt -S -lowertypetests -mtriple=i686-unknown-linux-gnu < %s | FileCheck --check-prefixes=CHECK,X86 %s +; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefixes=CHECK,X86 %s +; RUN: opt -S -lowertypetests -mtriple=arm-unknown-linux-gnu < %s | FileCheck --check-prefixes=CHECK,ARM %s +; RUN: opt -S -lowertypetests -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck --check-prefixes=CHECK,ARM %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" -; CHECK: module asm ".weak f" - ; CHECK: @x = global void ()* null, align 8 @x = global void ()* @f, align 8 @@ -25,7 +23,6 @@ target triple = "x86_64-unknown-linux-gnu" ; CHECK: @s = global { void ()*, void ()*, i32 } zeroinitializer, align 8 @s = global { void ()*, void ()*, i32 } { void ()* @f, void ()* @f, i32 42 }, align 8 -; CHECK: @.cfi.jumptable = external hidden constant [1 x [{{.*}} x i8]] ; CHECK: @llvm.global_ctors = appending global {{.*}}{ i32 0, void ()* @__cfi_global_var_init ; CHECK: declare !type !0 extern_weak void @f() @@ -34,14 +31,14 @@ declare !type !0 extern_weak void @f() ; CHECK: define zeroext i1 @check_f() define zeroext i1 @check_f() { entry: -; CHECK: ret i1 icmp ne (void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null), void ()* null) +; CHECK: ret i1 icmp ne (void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT:.*]], void ()* null), void ()* null) ret i1 icmp ne (void ()* @f, void ()* null) } ; CHECK: define void @call_f() { define void @call_f() { entry: -; CHECK: call void select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null)() +; CHECK: call void select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT]], void ()* null)() call void @f() ret void } @@ -53,15 +50,17 @@ define i1 @foo(i8* %p) { ret i1 %x } +; X86: define private void @[[JT]]() #{{.*}} section ".text.cfi" align 8 { +; ARM: define private void @[[JT]]() #{{.*}} section ".text.cfi" align 4 { + ; CHECK: define internal void @__cfi_global_var_init() section ".text.startup" { ; CHECK-NEXT: entry: -; CHECK-NEXT: store { void ()*, void ()*, i32 } { void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null), void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null), i32 42 }, { void ()*, void ()*, i32 }* @s, align 8 -; CHECK-NEXT: store void ()* bitcast (i8* getelementptr (i8, i8* bitcast (void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null) to i8*), i64 42) to void ()*), void ()** @x4, align 8 -; CHECK-NEXT: store void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null), void ()** @x3, align 8 -; CHECK-NEXT: store void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null), void ()** @x2, align 8 -; CHECK-NEXT: store void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* bitcast ({{.*}}@.cfi.jumptable to void ()*), void ()* null), void ()** @x, align 8 +; CHECK-NEXT: store { void ()*, void ()*, i32 } { void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT]], void ()* null), void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT]], void ()* null), i32 42 }, { void ()*, void ()*, i32 }* @s, align 8 +; CHECK-NEXT: store void ()* bitcast (i8* getelementptr (i8, i8* bitcast (void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT]], void ()* null) to i8*), i64 42) to void ()*), void ()** @x4, align 8 +; CHECK-NEXT: store void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT]], void ()* null), void ()** @x3, align 8 +; CHECK-NEXT: store void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT]], void ()* null), void ()** @x2, align 8 +; CHECK-NEXT: store void ()* select (i1 icmp ne (void ()* @f, void ()* null), void ()* @[[JT]], void ()* null), void ()** @x, align 8 ; CHECK-NEXT: ret void ; CHECK-NEXT: } - !0 = !{i32 0, !"typeid1"} diff --git a/llvm/test/Transforms/LowerTypeTests/function.ll b/llvm/test/Transforms/LowerTypeTests/function.ll index effe769..2878258 100644 --- a/llvm/test/Transforms/LowerTypeTests/function.ll +++ b/llvm/test/Transforms/LowerTypeTests/function.ll @@ -1,5 +1,7 @@ -; RUN: opt -S -lowertypetests -mtriple=i686-unknown-linux-gnu < %s | FileCheck --check-prefixes=X86,NATIVE %s -; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefixes=X86,NATIVE %s +; RUN: opt -S -lowertypetests -mtriple=i686-unknown-linux-gnu < %s | FileCheck --check-prefixes=X86,X86-LINUX,NATIVE %s +; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefixes=X86,X86-LINUX,NATIVE %s +; RUN: opt -S -lowertypetests -mtriple=i686-pc-win32 < %s | FileCheck --check-prefixes=X86,X86-WIN32,NATIVE %s +; RUN: opt -S -lowertypetests -mtriple=x86_64-pc-win32 < %s | FileCheck --check-prefixes=X86,X86-WIN32,NATIVE %s ; RUN: opt -S -lowertypetests -mtriple=arm-unknown-linux-gnu < %s | FileCheck --check-prefixes=ARM,NATIVE %s ; RUN: opt -S -lowertypetests -mtriple=thumb-unknown-linux-gnu < %s | FileCheck --check-prefixes=THUMB,NATIVE %s ; RUN: opt -S -lowertypetests -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck --check-prefixes=ARM,NATIVE %s @@ -9,61 +11,18 @@ target datalayout = "e-p:64:64" -; X86: module asm ".globl f" -; X86-NEXT: module asm ".type f, function" -; X86-NEXT: module asm "f = .cfi.jumptable + 0" -; X86-NEXT: module asm ".size f, 8" -; X86-NEXT: module asm ".type g, function" -; X86-NEXT: module asm "g = .cfi.jumptable + 8" -; X86-NEXT: module asm ".size g, 8" -; X86-NEXT: module asm ".section .text.cfi, \22ax\22, %progbits" -; X86-NEXT: module asm ".balign 8" -; X86-NEXT: module asm ".cfi.jumptable:" -; X86-NEXT: module asm "jmp f.cfi@plt" -; X86-NEXT: module asm "int3" -; X86-NEXT: module asm "int3" -; X86-NEXT: module asm "int3" -; X86-NEXT: module asm "jmp g.cfi@plt" -; X86-NEXT: module asm "int3" -; X86-NEXT: module asm "int3" -; X86-NEXT: module asm "int3" - -; ARM: module asm ".globl f" -; ARM-NEXT: module asm ".type f, function" -; ARM-NEXT: module asm "f = .cfi.jumptable + 0" -; ARM-NEXT: module asm ".size f, 4" -; ARM-NEXT: module asm ".type g, function" -; ARM-NEXT: module asm "g = .cfi.jumptable + 4" -; ARM-NEXT: module asm ".size g, 4" -; ARM-NEXT: module asm ".section .text.cfi, \22ax\22, %progbits" -; ARM-NEXT: module asm ".balign 4" -; ARM-NEXT: module asm ".cfi.jumptable:" -; ARM-NEXT: module asm "b f.cfi" -; ARM-NEXT: module asm "b g.cfi" - -; THUMB: module asm ".globl f" -; THUMB-NEXT: module asm ".type f, function" -; THUMB-NEXT: module asm ".thumb_set f, .cfi.jumptable + 0" -; THUMB-NEXT: module asm ".size f, 4" -; THUMB-NEXT: module asm ".type g, function" -; THUMB-NEXT: module asm ".thumb_set g, .cfi.jumptable + 4" -; THUMB-NEXT: module asm ".size g, 4" -; THUMB-NEXT: module asm ".section .text.cfi, \22ax\22, %progbits" -; THUMB-NEXT: module asm ".balign 4" -; THUMB-NEXT: module asm ".thumb_func" -; THUMB-NEXT: module asm ".cfi.jumptable:" -; THUMB-NEXT: module asm "b.w f.cfi" -; THUMB-NEXT: module asm "b.w g.cfi" - - -; X86: @.cfi.jumptable = external hidden constant [2 x [8 x i8]] -; ARM: @.cfi.jumptable = external hidden constant [2 x [4 x i8]] -; THUMB: @.cfi.jumptable = external hidden constant [2 x [4 x i8]] -; WASM32: private constant [0 x i8] zeroinitializer +; NATIVE: @0 = private unnamed_addr constant [2 x void (...)*] [void (...)* bitcast (void ()* @f to void (...)*), void (...)* bitcast (void ()* @g to void (...)*)], align 16 @0 = private unnamed_addr constant [2 x void (...)*] [void (...)* bitcast (void ()* @f to void (...)*), void (...)* bitcast (void ()* @g to void (...)*)], align 16 -; NATIVE: @llvm.used = appending global [2 x i8*] [i8* bitcast (void ()* @f.cfi to i8*), i8* bitcast (void ()* @g.cfi to i8*)], section "llvm.metadata" +; NATIVE: private constant [0 x i8] zeroinitializer +; WASM32: private constant [0 x i8] zeroinitializer + +; NATIVE: @f = alias void (), void ()* @[[JT:.*]] + +; X86: @g = internal alias void (), bitcast ([8 x i8]* getelementptr inbounds ([2 x [8 x i8]], [2 x [8 x i8]]* bitcast (void ()* @[[JT]] to [2 x [8 x i8]]*), i64 0, i64 1) to void ()*) +; ARM: @g = internal alias void (), bitcast ([4 x i8]* getelementptr inbounds ([2 x [4 x i8]], [2 x [4 x i8]]* bitcast (void ()* @[[JT]] to [2 x [4 x i8]]*), i64 0, i64 1) to void ()*) +; THUMB: @g = internal alias void (), bitcast ([4 x i8]* getelementptr inbounds ([2 x [4 x i8]], [2 x [4 x i8]]* bitcast (void ()* @[[JT]] to [2 x [4 x i8]]*), i64 0, i64 1) to void ()*) ; NATIVE: define internal void @f.cfi() ; WASM32: define void @f() !type !{{[0-9]+}} !wasm.index ![[I0:[0-9]+]] @@ -82,16 +41,38 @@ define internal void @g() !type !0 { declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone define i1 @foo(i8* %p) { - ; X86: sub i64 {{.*}}, ptrtoint ([2 x [8 x i8]]* @.cfi.jumptable to i64) - ; ARM: sub i64 {{.*}}, ptrtoint ([2 x [4 x i8]]* @.cfi.jumptable to i64) + ; NATIVE: sub i64 {{.*}}, ptrtoint (void ()* @[[JT]] to i64) ; WASM32: sub i64 {{.*}}, 1 ; WASM32: icmp ult i64 {{.*}}, 2 %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") ret i1 %x } -; NATIVE: declare void @f() -; NATIVE: declare hidden void @g() +; X86-LINUX: define private void @[[JT]]() #[[ATTR:.*]] section ".text.cfi" align 8 { +; X86-WIN32: define private void @[[JT]]() section ".text.cfi" align 8 { +; ARM: define private void @[[JT]]() #[[ATTR:.*]] section ".text.cfi" align 4 { +; THUMB: define private void @[[JT]]() #[[ATTR:.*]] section ".text.cfi" align 4 { + +; X86: jmp ${0:c}@plt +; X86-SAME: int3 +; X86-SAME: int3 +; X86-SAME: int3 +; X86-SAME: jmp ${1:c}@plt +; X86-SAME: int3 +; X86-SAME: int3 +; X86-SAME: int3 + +; ARM: b $0 +; ARM-SAME: b $1 + +; THUMB: b.w $0 +; THUMB-SAME: b.w $1 + +; NATIVE-SAME: "s,s"(void ()* @f.cfi, void ()* @g.cfi) + +; X86-LINUX: attributes #[[ATTR]] = { {{.*}}naked +; ARM: attributes #[[ATTR]] = { {{.*}}naked +; THUMB: attributes #[[ATTR]] = { {{.*}}naked{{.*}}"target-cpu"="cortex-a8" ; WASM32: ![[I0]] = !{i64 1} ; WASM32: ![[I1]] = !{i64 2} diff --git a/llvm/test/Transforms/LowerTypeTests/section.ll b/llvm/test/Transforms/LowerTypeTests/section.ll index 9dfb173..9e81225 100644 --- a/llvm/test/Transforms/LowerTypeTests/section.ll +++ b/llvm/test/Transforms/LowerTypeTests/section.ll @@ -5,10 +5,7 @@ target triple = "x86_64-unknown-linux-gnu" -; CHECK: module asm ".section .text.cfi, -; CHECK: module asm ".cfi.jumptable:" -; CHECK-NEXT: module asm "jmp f.cfi@plt" - +; CHECK: @f = alias void (), void ()* @[[JT:.*]] ; CHECK: define internal void @f.cfi() section "xxx" define void @f() section "xxx" !type !0 { @@ -22,6 +19,8 @@ entry: ret i1 %0 } +; CHECK: define private void @[[JT]]() #{{.*}} section ".text.cfi" align {{.*}} { + declare i1 @llvm.type.test(i8*, metadata) nounwind readnone !0 = !{i64 0, !"_ZTSFvE"} -- 2.7.4