#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"
bool LinkerSubsectionsViaSymbols;
Triple::ArchType Arch;
+ Triple::OSType OS;
Triple::ObjectFormatType ObjectFormat;
IntegerType *Int1Ty = Type::getInt1Ty(M.getContext());
std::vector<ByteArrayInfo> ByteArrayInfos;
- Mangler Mang;
Function *WeakInitializerFn = nullptr;
BitSetInfo
ArrayRef<GlobalTypeMember *> 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<Value *> &AsmArgs, Function *Dest);
void verifyTypeMDNode(GlobalObject *GO, MDNode *Type);
void buildBitSetsFromFunctions(ArrayRef<Metadata *> TypeIds,
ArrayRef<GlobalTypeMember *> Functions);
void findGlobalVariableUsersOf(Constant *C,
SmallSetVector<GlobalVariable *, 8> &Out);
+ void createJumpTable(Function *F, ArrayRef<GlobalTypeMember *> Functions);
+
public:
LowerTypeTestsModule(Module &M);
bool lower();
}
}
-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<Value *> &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() {
PlaceholderFn->eraseFromParent();
}
+void LowerTypeTestsModule::createJumpTable(
+ Function *F, ArrayRef<GlobalTypeMember *> Functions) {
+ std::string AsmStr, ConstraintStr;
+ raw_string_ostream AsmOS(AsmStr), ConstraintOS(ConstraintStr);
+ SmallVector<Value *, 16> AsmArgs;
+ AsmArgs.reserve(Functions.size() * 2);
+
+ for (unsigned I = 0; I != Functions.size(); ++I)
+ createJumpTableEntry(AsmOS, ConstraintOS, AsmArgs,
+ cast<Function>(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<Type *, 16> 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(
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<Function>(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<Constant *>{ConstantInt::get(IntPtrTy, 0),
+ ConstantInt::get(IntPtrTy, I)}),
+ F->getType());
if (LinkerSubsectionsViaSymbols || F->isDeclarationForLinker()) {
- Constant *CombinedGlobalElemPtr = ConstantExpr::getBitCast(
- ConstantExpr::getGetElementPtr(
- JumpTableType, JumpTable,
- ArrayRef<Constant *>{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<FunctionType>(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<Function>(Functions[I]->getGlobal()), I);
-
- M.appendModuleInlineAsm(AsmOS.str());
-
- SmallVector<GlobalValue *, 16> 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
Triple TargetTriple(M.getTargetTriple());
LinkerSubsectionsViaSymbols = TargetTriple.isMacOSX();
Arch = TargetTriple.getArch();
+ OS = TargetTriple.getOS();
ObjectFormat = TargetTriple.getObjectFormat();
}
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
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
; 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")
!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)
-; 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
; 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()
; 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
}
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"}
-; 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
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]+]]
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}
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 {
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"}