| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,6 +107,7 @@ namespace lltok { | |
| kw_cold, | ||
| kw_inlinehint, | ||
| kw_inreg, | ||
| kw_jumptable, | ||
| kw_minsize, | ||
| kw_naked, | ||
| kw_nest, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,301 @@ | ||
| //===-- JumpInstrTables.cpp: Jump-Instruction Tables ----------------------===// | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| /// | ||
| /// \file | ||
| /// \brief An implementation of jump-instruction tables. | ||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #define DEBUG_TYPE "jt" | ||
|
|
||
| #include "llvm/CodeGen/JumpInstrTables.h" | ||
|
|
||
| #include "llvm/ADT/Statistic.h" | ||
| #include "llvm/Analysis/JumpInstrTableInfo.h" | ||
| #include "llvm/CodeGen/Passes.h" | ||
| #include "llvm/IR/Attributes.h" | ||
| #include "llvm/IR/CallSite.h" | ||
| #include "llvm/IR/Constants.h" | ||
| #include "llvm/IR/DerivedTypes.h" | ||
| #include "llvm/IR/Function.h" | ||
| #include "llvm/IR/LLVMContext.h" | ||
| #include "llvm/IR/Module.h" | ||
| #include "llvm/IR/Operator.h" | ||
| #include "llvm/IR/Type.h" | ||
| #include "llvm/IR/Verifier.h" | ||
| #include "llvm/Support/CommandLine.h" | ||
| #include "llvm/Support/Debug.h" | ||
| #include "llvm/Support/raw_ostream.h" | ||
|
|
||
| #include <vector> | ||
|
|
||
| using namespace llvm; | ||
|
|
||
| char JumpInstrTables::ID = 0; | ||
|
|
||
| INITIALIZE_PASS_BEGIN(JumpInstrTables, "jump-instr-tables", | ||
| "Jump-Instruction Tables", true, true) | ||
| INITIALIZE_PASS_DEPENDENCY(JumpInstrTableInfo); | ||
| INITIALIZE_PASS_END(JumpInstrTables, "jump-instr-tables", | ||
| "Jump-Instruction Tables", true, true) | ||
|
|
||
| STATISTIC(NumJumpTables, "Number of indirect call tables generated"); | ||
| STATISTIC(NumFuncsInJumpTables, "Number of functions in the jump tables"); | ||
|
|
||
| ModulePass *llvm::createJumpInstrTablesPass() { | ||
| // The default implementation uses a single table for all functions. | ||
| return new JumpInstrTables(JumpTable::Single); | ||
| } | ||
|
|
||
| ModulePass *llvm::createJumpInstrTablesPass(JumpTable::JumpTableType JTT) { | ||
| return new JumpInstrTables(JTT); | ||
| } | ||
|
|
||
| namespace { | ||
| static const char jump_func_prefix[] = "__llvm_jump_instr_table_"; | ||
| static const char jump_section_prefix[] = ".jump.instr.table.text."; | ||
|
|
||
| // Checks to see if a given CallSite is making an indirect call, including | ||
| // cases where the indirect call is made through a bitcast. | ||
| bool isIndirectCall(CallSite &CS) { | ||
| if (CS.getCalledFunction()) | ||
| return false; | ||
|
|
||
| // Check the value to see if it is merely a bitcast of a function. In | ||
| // this case, it will translate to a direct function call in the resulting | ||
| // assembly, so we won't treat it as an indirect call here. | ||
| const Value *V = CS.getCalledValue(); | ||
| if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(V)) { | ||
| return !(CE->isCast() && isa<Function>(CE->getOperand(0))); | ||
| } | ||
|
|
||
| // Otherwise, since we know it's a call, it must be an indirect call | ||
| return true; | ||
| } | ||
|
|
||
| // Replaces Functions and GlobalAliases with a different Value. | ||
| bool replaceGlobalValueIndirectUse(GlobalValue *GV, Value *V, Use *U) { | ||
| User *Us = U->getUser(); | ||
| if (!Us) | ||
| return false; | ||
| if (Instruction *I = dyn_cast<Instruction>(Us)) { | ||
| CallSite CS(I); | ||
|
|
||
| // Don't do the replacement if this use is a direct call to this function. | ||
| // If the use is not the called value, then replace it. | ||
| if (CS && (isIndirectCall(CS) || CS.isCallee(U))) { | ||
| return false; | ||
| } | ||
|
|
||
| U->set(V); | ||
| } else if (Constant *C = dyn_cast<Constant>(Us)) { | ||
| // Don't replace calls to bitcasts of function symbols, since they get | ||
| // translated to direct calls. | ||
| if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Us)) { | ||
| if (CE->getOpcode() == Instruction::BitCast) { | ||
| // This bitcast must have exactly one user. | ||
| if (CE->user_begin() != CE->user_end()) { | ||
| User *ParentUs = *CE->user_begin(); | ||
| if (CallInst *CI = dyn_cast<CallInst>(ParentUs)) { | ||
| CallSite CS(CI); | ||
| Use &CEU = *CE->use_begin(); | ||
| if (CS.isCallee(&CEU)) { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // GlobalAlias doesn't support replaceUsesOfWithOnConstant. And the verifier | ||
| // requires alias to point to a defined function. So, GlobalAlias is handled | ||
| // as a separate case in runOnModule. | ||
| if (!isa<GlobalAlias>(C)) | ||
| C->replaceUsesOfWithOnConstant(GV, V, U); | ||
| } else { | ||
| assert(false && "The Use of a Function symbol is neither an instruction nor" | ||
| " a constant"); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| // Replaces all replaceable address-taken uses of GV with a pointer to a | ||
| // jump-instruction table entry. | ||
| void replaceValueWithFunction(GlobalValue *GV, Function *F) { | ||
| // Go through all uses of this function and replace the uses of GV with the | ||
| // jump-table version of the function. Get the uses as a vector before | ||
| // replacing them, since replacing them changes the use list and invalidates | ||
| // the iterator otherwise. | ||
| for (Value::use_iterator I = GV->use_begin(), E = GV->use_end(); I != E;) { | ||
| Use &U = *I++; | ||
|
|
||
| // Replacement of constants replaces all instances in the constant. So, some | ||
| // uses might have already been handled by the time we reach them here. | ||
| if (U.get() == GV) | ||
| replaceGlobalValueIndirectUse(GV, F, &U); | ||
| } | ||
|
|
||
| return; | ||
| } | ||
| } // end anonymous namespace | ||
|
|
||
| JumpInstrTables::JumpInstrTables() | ||
| : ModulePass(ID), Metadata(), JITI(nullptr), TableCount(0), | ||
| JTType(JumpTable::Single) { | ||
| initializeJumpInstrTablesPass(*PassRegistry::getPassRegistry()); | ||
| } | ||
|
|
||
| JumpInstrTables::JumpInstrTables(JumpTable::JumpTableType JTT) | ||
| : ModulePass(ID), Metadata(), JITI(nullptr), TableCount(0), JTType(JTT) { | ||
| initializeJumpInstrTablesPass(*PassRegistry::getPassRegistry()); | ||
| } | ||
|
|
||
| JumpInstrTables::~JumpInstrTables() {} | ||
|
|
||
| void JumpInstrTables::getAnalysisUsage(AnalysisUsage &AU) const { | ||
| AU.addRequired<JumpInstrTableInfo>(); | ||
| } | ||
|
|
||
| Function *JumpInstrTables::insertEntry(Module &M, Function *Target) { | ||
| FunctionType *OrigFunTy = Target->getFunctionType(); | ||
| FunctionType *FunTy = transformType(OrigFunTy); | ||
|
|
||
| JumpMap::iterator it = Metadata.find(FunTy); | ||
| if (Metadata.end() == it) { | ||
| struct TableMeta Meta; | ||
| Meta.TableNum = TableCount; | ||
| Meta.Count = 0; | ||
| Metadata[FunTy] = Meta; | ||
| it = Metadata.find(FunTy); | ||
| ++NumJumpTables; | ||
| ++TableCount; | ||
| } | ||
|
|
||
| it->second.Count++; | ||
|
|
||
| std::string NewName(jump_func_prefix); | ||
| NewName += (Twine(it->second.TableNum) + "_" + Twine(it->second.Count)).str(); | ||
| Function *JumpFun = | ||
| Function::Create(OrigFunTy, GlobalValue::ExternalLinkage, NewName, &M); | ||
| // The section for this table | ||
| JumpFun->setSection((jump_section_prefix + Twine(it->second.TableNum)).str()); | ||
| JITI->insertEntry(FunTy, Target, JumpFun); | ||
|
|
||
| ++NumFuncsInJumpTables; | ||
| return JumpFun; | ||
| } | ||
|
|
||
| bool JumpInstrTables::hasTable(FunctionType *FunTy) { | ||
| FunctionType *TransTy = transformType(FunTy); | ||
| return Metadata.end() != Metadata.find(TransTy); | ||
| } | ||
|
|
||
| FunctionType *JumpInstrTables::transformType(FunctionType *FunTy) { | ||
| // Returning nullptr forces all types into the same table, since all types map | ||
| // to the same type | ||
| Type *VoidPtrTy = Type::getInt8PtrTy(FunTy->getContext()); | ||
|
|
||
| // Ignore the return type. | ||
| Type *RetTy = VoidPtrTy; | ||
| bool IsVarArg = FunTy->isVarArg(); | ||
| std::vector<Type *> ParamTys(FunTy->getNumParams()); | ||
| FunctionType::param_iterator PI, PE; | ||
| int i = 0; | ||
|
|
||
| std::vector<Type *> EmptyParams; | ||
| Type *Int32Ty = Type::getInt32Ty(FunTy->getContext()); | ||
| FunctionType *VoidFnTy = FunctionType::get( | ||
| Type::getVoidTy(FunTy->getContext()), EmptyParams, false); | ||
| switch (JTType) { | ||
| case JumpTable::Single: | ||
|
|
||
| return FunctionType::get(RetTy, EmptyParams, false); | ||
| case JumpTable::Arity: | ||
| // Transform all types to void* so that all functions with the same arity | ||
| // end up in the same table. | ||
| for (PI = FunTy->param_begin(), PE = FunTy->param_end(); PI != PE; | ||
| PI++, i++) { | ||
| ParamTys[i] = VoidPtrTy; | ||
| } | ||
|
|
||
| return FunctionType::get(RetTy, ParamTys, IsVarArg); | ||
| case JumpTable::Simplified: | ||
| // Project all parameters types to one of 3 types: composite, integer, and | ||
| // function, matching the three subclasses of Type. | ||
| for (PI = FunTy->param_begin(), PE = FunTy->param_end(); PI != PE; | ||
| ++PI, ++i) { | ||
| assert((isa<IntegerType>(*PI) || isa<FunctionType>(*PI) || | ||
| isa<CompositeType>(*PI)) && | ||
| "This type is not an Integer or a Composite or a Function"); | ||
| if (isa<CompositeType>(*PI)) { | ||
| ParamTys[i] = VoidPtrTy; | ||
| } else if (isa<FunctionType>(*PI)) { | ||
| ParamTys[i] = VoidFnTy; | ||
| } else if (isa<IntegerType>(*PI)) { | ||
| ParamTys[i] = Int32Ty; | ||
| } | ||
| } | ||
|
|
||
| return FunctionType::get(RetTy, ParamTys, IsVarArg); | ||
| case JumpTable::Full: | ||
| // Don't transform this type at all. | ||
| return FunTy; | ||
| } | ||
|
|
||
| return nullptr; | ||
| } | ||
|
|
||
| bool JumpInstrTables::runOnModule(Module &M) { | ||
| // Make sure the module is well-formed, especially with respect to jumptable. | ||
| if (verifyModule(M)) | ||
| return false; | ||
|
|
||
| JITI = &getAnalysis<JumpInstrTableInfo>(); | ||
|
|
||
| // Get the set of jumptable-annotated functions. | ||
| DenseMap<Function *, Function *> Functions; | ||
| for (Function &F : M) { | ||
| if (F.hasFnAttribute(Attribute::JumpTable)) { | ||
| assert(F.hasUnnamedAddr() && | ||
| "Attribute 'jumptable' requires 'unnamed_addr'"); | ||
| Functions[&F] = NULL; | ||
| } | ||
| } | ||
|
|
||
| // Create the jump-table functions. | ||
| for (auto &KV : Functions) { | ||
| Function *F = KV.first; | ||
| KV.second = insertEntry(M, F); | ||
| } | ||
|
|
||
| // GlobalAlias is a special case, because the target of an alias statement | ||
| // must be a defined function. So, instead of replacing a given function in | ||
| // the alias, we replace all uses of aliases that target jumptable functions. | ||
| // Note that there's no need to create these functions, since only aliases | ||
| // that target known jumptable functions are replaced, and there's no way to | ||
| // put the jumptable annotation on a global alias. | ||
| DenseMap<GlobalAlias *, Function *> Aliases; | ||
| for (GlobalAlias &GA : M.aliases()) { | ||
| Constant *Aliasee = GA.getAliasee(); | ||
| if (Function *F = dyn_cast<Function>(Aliasee)) { | ||
| auto it = Functions.find(F); | ||
| if (it != Functions.end()) { | ||
| Aliases[&GA] = it->second; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Replace each address taken function with its jump-instruction table entry. | ||
| for (auto &KV : Functions) | ||
| replaceValueWithFunction(KV.first, KV.second); | ||
|
|
||
| for (auto &KV : Aliases) | ||
| replaceValueWithFunction(KV.first, KV.second); | ||
|
|
||
| return !Functions.empty(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| ; RUN: llc <%s -march=arm -jump-table-type=single | FileCheck --check-prefix=ARM %s | ||
| ; RUN: llc <%s -march=thumb -jump-table-type=single | FileCheck --check-prefix=THUMB %s | ||
|
|
||
| define void @indirect_fun() unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
| define void ()* @get_fun() { | ||
| ret void ()* @indirect_fun | ||
|
|
||
| ; ARM: ldr r0, [[LABEL:.*]] | ||
| ; ARM: mov pc, lr | ||
| ; ARM: [[LABEL]]: | ||
| ; ARM: .long __llvm_jump_instr_table_0_1 | ||
|
|
||
| ; THUMB: ldr r0, [[LABEL:.*]] | ||
| ; THUMB: bx lr | ||
| ; THUMB: [[LABEL]]: | ||
| ; THUMB: .long __llvm_jump_instr_table_0_1 | ||
| } | ||
|
|
||
| ; ARM: .globl __llvm_jump_instr_table_0_1 | ||
| ; ARM: .align 3 | ||
| ; ARM: .type __llvm_jump_instr_table_0_1,%function | ||
| ; ARM: __llvm_jump_instr_table_0_1: | ||
| ; ARM: b indirect_fun(PLT) | ||
|
|
||
| ; THUMB: .globl __llvm_jump_instr_table_0_1 | ||
| ; THUMB: .align 3 | ||
| ; THUMB: .thumb_func | ||
| ; THUMB: .type __llvm_jump_instr_table_0_1,%function | ||
| ; THUMB: __llvm_jump_instr_table_0_1: | ||
| ; THUMB: b indirect_fun(PLT) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| ; RUN: llc <%s -jump-table-type=single | FileCheck %s | ||
| target triple = "x86_64-unknown-linux-gnu" | ||
| define i32 @f() unnamed_addr jumptable { | ||
| entry: | ||
| ret i32 0 | ||
| } | ||
|
|
||
| @i = alias internal i32 ()* @f | ||
| @j = alias i32 ()* @f | ||
|
|
||
| define i32 @main(i32 %argc, i8** %argv) { | ||
| %temp = alloca i32 ()*, align 8 | ||
| store i32 ()* @i, i32()** %temp, align 8 | ||
| ; CHECK: movq $__llvm_jump_instr_table_0_1 | ||
| %1 = load i32 ()** %temp, align 8 | ||
| ; CHECK: movl $__llvm_jump_instr_table_0_1 | ||
| %2 = call i32 ()* %1() | ||
| %3 = call i32 ()* @i() | ||
| ; CHECK: callq i | ||
| %4 = call i32 ()* @j() | ||
| ; CHECK: callq j | ||
| ret i32 %3 | ||
| } | ||
|
|
||
| ; There should only be one table, even though there are two GlobalAliases, | ||
| ; because they both alias the same value. | ||
|
|
||
| ; CHECK: .globl __llvm_jump_instr_table_0_1 | ||
| ; CHECK: .align 8, 0x90 | ||
| ; CHECK: .type __llvm_jump_instr_table_0_1,@function | ||
| ; CHECK: __llvm_jump_instr_table_0_1: | ||
| ; CHECK: jmp f@PLT | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| ; RUN: llc <%s -jump-table-type=single | FileCheck %s | ||
| target triple = "x86_64-unknown-linux-gnu" | ||
| define i32 @f() unnamed_addr jumptable { | ||
| ret i32 0 | ||
| } | ||
|
|
||
| define i32 @g(i8* %a) unnamed_addr jumptable { | ||
| ret i32 0 | ||
| } | ||
|
|
||
| define void @h(void ()* %func) unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define i32 @main() { | ||
| %g = alloca i32 (...)*, align 8 | ||
| store i32 (...)* bitcast (i32 ()* @f to i32 (...)*), i32 (...)** %g, align 8 | ||
| ; CHECK: movq $__llvm_jump_instr_table_0_[[ENTRY:1|2|3]], (%rsp) | ||
| ; CHECK: movl $__llvm_jump_instr_table_0_[[ENTRY]], %ecx | ||
| %1 = load i32 (...)** %g, align 8 | ||
| %call = call i32 (...)* %1() | ||
| call void (void ()*)* @h(void ()* bitcast (void (void ()*)* @h to void ()*)) | ||
| ; CHECK: movl $__llvm_jump_instr_table_0_{{1|2|3}}, %edi | ||
| ; CHECK: callq h | ||
|
|
||
| %a = call i32 (i32*)* bitcast (i32 (i8*)* @g to i32(i32*)*)(i32* null) | ||
| ; CHECK: callq g | ||
| ret i32 %a | ||
| } | ||
|
|
||
| ; CHECK: .globl __llvm_jump_instr_table_0_1 | ||
| ; CHECK: .align 8, 0x90 | ||
| ; CHECK: .type __llvm_jump_instr_table_0_1,@function | ||
| ; CHECK: __llvm_jump_instr_table_0_1: | ||
| ; CHECK: jmp {{f|g|h}}@PLT | ||
| ; CHECK: .globl __llvm_jump_instr_table_0_2 | ||
| ; CHECK: .align 8, 0x90 | ||
| ; CHECK: .type __llvm_jump_instr_table_0_2,@function | ||
| ; CHECK: __llvm_jump_instr_table_0_2: | ||
| ; CHECK: jmp {{f|g|h}}@PLT | ||
| ; CHECK: .globl __llvm_jump_instr_table_0_3 | ||
| ; CHECK: .align 8, 0x90 | ||
| ; CHECK: .type __llvm_jump_instr_table_0_3,@function | ||
| ; CHECK: __llvm_jump_instr_table_0_3: | ||
| ; CHECK: jmp {{f|g|h}}@PLT | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,272 @@ | ||
| ; RUN: llc <%s -jump-table-type=single | FileCheck --check-prefix=SINGLE %s | ||
| ; RUN: llc <%s -jump-table-type=arity | FileCheck --check-prefix=ARITY %s | ||
| ; RUN: llc <%s -jump-table-type=simplified | FileCheck --check-prefix=SIMPL %s | ||
| ; RUN: llc <%s -jump-table-type=full | FileCheck --check-prefix=FULL %s | ||
|
|
||
| target triple = "x86_64-unknown-linux-gnu" | ||
|
|
||
| %struct.fun_struct = type { i32 (...)* } | ||
|
|
||
| define void @indirect_fun() unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define void @indirect_fun_match() unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define i32 @indirect_fun_i32() unnamed_addr jumptable { | ||
| ret i32 0 | ||
| } | ||
|
|
||
| define i32 @indirect_fun_i32_1(i32 %a) unnamed_addr jumptable { | ||
| ret i32 %a | ||
| } | ||
|
|
||
| define i32 @indirect_fun_i32_2(i32 %a, i32 %b) unnamed_addr jumptable { | ||
| ret i32 %a | ||
| } | ||
|
|
||
| define i32* @indirect_fun_i32S_2(i32* %a, i32 %b) unnamed_addr jumptable { | ||
| ret i32* %a | ||
| } | ||
|
|
||
| define void @indirect_fun_struct(%struct.fun_struct %fs) unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define void @indirect_fun_fun(i32 (...)* %fun, i32 %a) unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define i32 @indirect_fun_fun_ret(i32 (...)* %fun, i32 %a) unnamed_addr jumptable { | ||
| ret i32 %a | ||
| } | ||
|
|
||
| define void @indirect_fun_array([19 x i8] %a) unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define void @indirect_fun_vec(<3 x i32> %a) unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define void @indirect_fun_vec_2(<4 x float> %a) unnamed_addr jumptable { | ||
| ret void | ||
| } | ||
|
|
||
| define i32 @m(void ()* %fun) { | ||
| call void ()* %fun() | ||
| ret i32 0 | ||
| } | ||
|
|
||
| define void ()* @get_fun() { | ||
| ret void ()* @indirect_fun | ||
| ; SINGLE: movl $__llvm_jump_instr_table_0_ | ||
| ; ARITY: movl $__llvm_jump_instr_table_ | ||
| ; SIMPL: movl $__llvm_jump_instr_table_ | ||
| ; FULL: movl $__llvm_jump_instr_table_ | ||
| } | ||
|
|
||
| define i32 @main(i32 %argc, i8** %argv) { | ||
| %f = call void ()* ()* @get_fun() | ||
| %a = call i32 @m(void ()* %f) | ||
| ret i32 %a | ||
| } | ||
|
|
||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_1 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_1,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_1: | ||
| ; SINGLE-DAG: jmp indirect_fun_array@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_2 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_2,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_2: | ||
| ; SINGLE-DAG: jmp indirect_fun_i32_2@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_3 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_3,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_3: | ||
| ; SINGLE-DAG: jmp indirect_fun_vec_2@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_4 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_4,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_4: | ||
| ; SINGLE-DAG: jmp indirect_fun_i32S_2@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_5 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_5,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_5: | ||
| ; SINGLE-DAG: jmp indirect_fun_struct@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_6 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_6,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_6: | ||
| ; SINGLE-DAG: jmp indirect_fun_i32_1@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_7 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_7,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_7: | ||
| ; SINGLE-DAG: jmp indirect_fun_i32@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_8 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_8,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_8: | ||
| ; SINGLE-DAG: jmp indirect_fun_fun@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_9 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_9,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_9: | ||
| ; SINGLE-DAG: jmp indirect_fun_fun_ret@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_10 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_10,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_10: | ||
| ; SINGLE-DAG: jmp indirect_fun@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_11 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_11,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_11: | ||
| ; SINGLE-DAG: jmp indirect_fun_match@PLT | ||
| ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_12 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: .type __llvm_jump_instr_table_0_12,@function | ||
| ; SINGLE-DAG: __llvm_jump_instr_table_0_12: | ||
| ; SINGLE-DAG: jmp indirect_fun_vec@PLT | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: ud2 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: ud2 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: ud2 | ||
| ; SINGLE-DAG: .align 8, 0x90 | ||
| ; SINGLE-DAG: ud2 | ||
|
|
||
|
|
||
| ; ARITY-DAG: .globl __llvm_jump_instr_table_2_1 | ||
| ; ARITY-DAG: .align 8, 0x90 | ||
| ; ARITY-DAG: .type __llvm_jump_instr_table_2_1,@function | ||
| ; ARITY-DAG: __llvm_jump_instr_table_2_1: | ||
| ; ARITY-DAG: jmp indirect_fun{{.*}}@PLT | ||
| ; ARITY-DAG: .align 8, 0x90 | ||
| ; ARITY-DAG: ud2 | ||
| ; ARITY-DAG: .globl __llvm_jump_instr_table_0_1 | ||
| ; ARITY-DAG: .align 8, 0x90 | ||
| ; ARITY-DAG: .type __llvm_jump_instr_table_0_1,@function | ||
| ; ARITY-DAG: __llvm_jump_instr_table_0_1: | ||
| ; ARITY-DAG: jmp indirect_fun{{.*}}@PLT | ||
| ; ARITY-DAG: .globl __llvm_jump_instr_table_1_1 | ||
| ; ARITY-DAG: .align 8, 0x90 | ||
| ; ARITY-DAG: .type __llvm_jump_instr_table_1_1,@function | ||
| ; ARITY-DAG: __llvm_jump_instr_table_1_1: | ||
| ; ARITY-DAG: jmp indirect_fun{{.*}}@PLT | ||
|
|
||
| ; SIMPL-DAG: .globl __llvm_jump_instr_table_2_1 | ||
| ; SIMPL-DAG: .align 8, 0x90 | ||
| ; SIMPL-DAG: .type __llvm_jump_instr_table_2_1,@function | ||
| ; SIMPL-DAG: __llvm_jump_instr_table_2_1: | ||
| ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT | ||
| ; SIMPL-DAG: .align 8, 0x90 | ||
| ; SIMPL-DAG: ud2 | ||
| ; SIMPL-DAG: .globl __llvm_jump_instr_table_0_1 | ||
| ; SIMPL-DAG: .align 8, 0x90 | ||
| ; SIMPL-DAG: .type __llvm_jump_instr_table_0_1,@function | ||
| ; SIMPL-DAG: __llvm_jump_instr_table_0_1: | ||
| ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT | ||
| ; SIMPL-DAG: .globl __llvm_jump_instr_table_1_1 | ||
| ; SIMPL-DAG: .align 8, 0x90 | ||
| ; SIMPL-DAG: .type __llvm_jump_instr_table_1_1,@function | ||
| ; SIMPL-DAG: __llvm_jump_instr_table_1_1: | ||
| ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT | ||
| ; SIMPL-DAG: .globl __llvm_jump_instr_table_3_1 | ||
| ; SIMPL-DAG: .align 8, 0x90 | ||
| ; SIMPL-DAG: .type __llvm_jump_instr_table_3_1,@function | ||
| ; SIMPL-DAG: __llvm_jump_instr_table_3_1: | ||
| ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT | ||
| ; SIMPL-DAG: .globl __llvm_jump_instr_table_4_1 | ||
| ; SIMPL-DAG: .align 8, 0x90 | ||
| ; SIMPL-DAG: .type __llvm_jump_instr_table_4_1,@function | ||
| ; SIMPL-DAG: __llvm_jump_instr_table_4_1: | ||
| ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT | ||
|
|
||
|
|
||
| ; FULL-DAG: .globl __llvm_jump_instr_table_10_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_10_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_10_1: | ||
| ; FULL-DAG: jmp indirect_fun_i32_1@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_9_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_9_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_9_1: | ||
| ; FULL-DAG: jmp indirect_fun_i32_2@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_7_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_7_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_7_1: | ||
| ; FULL-DAG: jmp indirect_fun_i32S_2@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_3_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_3_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_3_1: | ||
| ; FULL-DAG: jmp indirect_fun_vec_2@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_2_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_2_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_2_1: | ||
| ; FULL-DAG: jmp indirect_fun@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_8_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_8_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_8_1: | ||
| ; FULL-DAG: jmp indirect_fun_i32@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_1_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_1_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_1_1: | ||
| ; FULL-DAG: jmp indirect_fun_array@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_0_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_0_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_0_1: | ||
| ; FULL-DAG: jmp indirect_fun_vec@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_6_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_6_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_6_1: | ||
| ; FULL-DAG: jmp indirect_fun_struct@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_5_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_5_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_5_1: | ||
| ; FULL-DAG: jmp indirect_fun_fun@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 | ||
| ; FULL-DAG: .globl __llvm_jump_instr_table_4_1 | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: .type __llvm_jump_instr_table_4_1,@function | ||
| ; FULL-DAG:__llvm_jump_instr_table_4_1: | ||
| ; FULL-DAG: jmp indirect_fun_fun_ret@PLT | ||
| ; FULL-DAG: .align 8, 0x90 | ||
| ; FULL-DAG: ud2 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| ; RUN: not llc <%s 2>&1 | FileCheck %s | ||
|
|
||
| define i32 @f() jumptable { | ||
| ret i32 0 | ||
| } | ||
|
|
||
| ; CHECK: Attribute 'jumptable' requires 'unnamed_addr' | ||
| ; CHECK: i32 ()* @f | ||
| ; CHECK: LLVM ERROR: Broken function found, compilation aborted! |