Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -985,6 +985,15 @@ inlining this function is desirable (such as the "inline" keyword in C/C++). It is just a hint; it imposes no requirements on the inliner. +``jumptable`` + This attribute indicates that the function should be added to a + jump-instruction table at code-generation time, and that all address-taken + references to this function should be replaced with a reference to the + appropriate jump-instruction-table function pointer. It has no effect if the + ``-build-jump-instr-tables`` flag is not specified. Note that this creates a + new pointer for the original function, which means that code that depends on + function-pointer identity can break. So, any function annotated with + ``jumptable`` must also be ``unnamed_addr``. ``minsize`` This attribute suggests that optimization passes and code generator passes make choices that keep the code size of this function as small Index: include/llvm-c/Core.h =================================================================== --- include/llvm-c/Core.h +++ include/llvm-c/Core.h @@ -165,7 +165,8 @@ LLVMStackProtectStrongAttribute = 1ULL<<33, LLVMCold = 1ULL << 34, LLVMOptimizeNone = 1ULL << 35, - LLVMInAllocaAttribute = 1ULL << 36 + LLVMInAllocaAttribute = 1ULL << 36, + LLVMJumpTable = 1ULL << 39 */ } LLVMAttribute; Index: include/llvm/Analysis/JumpInstrTableInfo.h =================================================================== --- /dev/null +++ include/llvm/Analysis/JumpInstrTableInfo.h @@ -0,0 +1,63 @@ +//===-- JumpInstrTableInfo.h: Info for Jump-Instruction Tables --*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Information about jump-instruction tables that have been created by +/// JumpInstrTables pass. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_JUMPINSTRTABLEINFO_H +#define LLVM_ANALYSIS_JUMPINSTRTABLEINFO_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Pass.h" + +#include + +namespace llvm { +class Function; +class FunctionType; + +/// This class stores information about jump-instruction tables created by the +/// JumpInstrTables pass (in lib/CodeGen/JumpInstrTables.cpp). Each table is a +/// map from a function type to a vector of pairs. The first element of each +/// pair is the function that has the jumptable annotation. The second element +/// is a function that was declared by JumpInstrTables and used to replace all +/// address-taking sites for the original function. +/// +/// The information in this pass is used in AsmPrinter +/// (lib/CodeGen/AsmPrinter/AsmPrinter.cpp) to generate the required assembly +/// for the jump-instruction tables. +class JumpInstrTableInfo : public ImmutablePass { +public: + static char ID; + + JumpInstrTableInfo(); + virtual ~JumpInstrTableInfo(); + const char *getPassName() const override { + return "Jump-Instruction Table Info"; + } + + typedef std::pair JumpPair; + typedef DenseMap > JumpTables; + + /// Inserts an entry in a table, adding the table if it doesn't exist. + void insertEntry(FunctionType *TableFunTy, Function *Target, Function *Jump); + + /// Gets the tables. + const JumpTables &getTables() const { return Tables; } + + /// Gets the alignment for a jump-instruction-table entry. + unsigned getAlignment(); + +private: + JumpTables Tables; +}; +} + +#endif /* LLVM_ANALYSIS_JUMPINSTRTABLEINFO_H */ Index: include/llvm/Analysis/Passes.h =================================================================== --- include/llvm/Analysis/Passes.h +++ include/llvm/Analysis/Passes.h @@ -142,6 +142,10 @@ // information and prints it with -analyze. // FunctionPass *createMemDepPrinter(); + + // createJumpInstrTableInfoPass - This creates a pass that stores information + // about the jump tables created by JumpInstrTables + ImmutablePass *createJumpInstrTableInfoPass(); } #endif Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -371,7 +371,8 @@ ATTR_KIND_BUILTIN = 35, ATTR_KIND_COLD = 36, ATTR_KIND_OPTIMIZE_NONE = 37, - ATTR_KIND_IN_ALLOCA = 38 + ATTR_KIND_IN_ALLOCA = 38, + ATTR_KIND_JUMP_TABLE = 39 }; } // End bitc namespace Index: include/llvm/CodeGen/JumpInstrTables.h =================================================================== --- /dev/null +++ include/llvm/CodeGen/JumpInstrTables.h @@ -0,0 +1,94 @@ +//===-- JumpInstrTables.h: Jump-Instruction Tables --------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief An implementation of tables consisting of jump instructions +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_JUMPINSTRTABLES_H +#define LLVM_CODEGEN_JUMPINSTRTABLES_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Pass.h" + +namespace llvm { +class Constant; +class Function; +class FunctionType; +class JumpInstrTableInfo; +class Module; + +/// A class to manage a set of jump tables indexed on function type. It looks at +/// each function in the module to find all the functions that have the +/// jumptable attribute set. For each such function, it creates a new +/// jump-instruction-table function and stores the mapping in the ImmutablePass +/// JumpInstrTableInfo. +/// +/// These special functions get lowered in AsmPrinter to assembly of the form: +/// .globl f +/// .type f,@function +/// .align 8,0x90 +/// f: +/// jmp f_orig@PLT +/// +/// Support for an architecture depends on two functions in TargetInstrInfo: +/// getUnconditionalBranch, and getTrap. AsmPrinter uses these to generate the +/// appropriate instructions for the jump statement (an unconditional branch) +/// and for padding to make the table have a size that is a power of two. This +/// padding uses a trap instruction to ensure that calls to this area halt the +/// program. The default implementations of these functions call +/// llvm_unreachable. +class JumpInstrTables : public ModulePass { +public: + static char ID; + + JumpInstrTables(); + virtual ~JumpInstrTables(); + bool runOnModule(Module &M) override; + const char *getPassName() const override { return "Jump-Instruction Tables"; } + void getAnalysisUsage(AnalysisUsage &AU) const override; + + /// Creates a jump-instruction table function for the Target and adds it to + /// the tables. + Function *insertEntry(Module &M, Function *Target); + + /// Checks to see if there is already a table for the given FunctionType. + bool hasTable(FunctionType *FunTy); + +private: + /// The metadata used while a jump table is being built + typedef struct TableMeta { + /// The number of this table + unsigned TableNum; + + /// The current number of jump entries in the table. + unsigned Count; + } TableMetadata; + + typedef DenseMap JumpMap; + + /// Maps the function into a subset of function types, depending on the + /// jump-instruction table style selected from JumpTableTypes in + /// JumpInstrTables.cpp. The choice of mapping determines the number of + /// jump-instruction tables generated by this pass. E.g., the simplest mapping + /// converts every function type into void f(); so, all functions end up in a + /// single table. + FunctionType *transformType(FunctionType *FunTy); + + /// The current state of functions and jump entries in the table(s). + JumpMap Metadata; + + /// The ImmutablePass that stores information about the generated tables. + JumpInstrTableInfo *JITI; + + /// The total number of tables. + unsigned TableCount; +}; +} + +#endif /* LLVM_CODEGEN_JUMPINSTRTABLES_H */ Index: include/llvm/CodeGen/Passes.h =================================================================== --- include/llvm/CodeGen/Passes.h +++ include/llvm/CodeGen/Passes.h @@ -592,6 +592,8 @@ /// the intrinsic for later emission to the StackMap. extern char &StackMapLivenessID; + /// createJumpInstrTables - This pass creates jump-instruction tables. + ModulePass *createJumpInstrTablesPass(); } // End llvm namespace #endif Index: include/llvm/IR/Attributes.h =================================================================== --- include/llvm/IR/Attributes.h +++ include/llvm/IR/Attributes.h @@ -75,6 +75,7 @@ Cold, ///< Marks function as being in a cold path. InlineHint, ///< Source said inlining was desirable InReg, ///< Force argument to be passed in register + JumpTable, ///< Build jump-instruction tables and replace refs. MinSize, ///< Function must be optimized for size first Naked, ///< Naked function Nest, ///< Nested function static chain Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -146,6 +146,8 @@ void initializeInstNamerPass(PassRegistry&); void initializeInternalizePassPass(PassRegistry&); void initializeIntervalPartitionPass(PassRegistry&); +void initializeJumpInstrTableInfoPass(PassRegistry&); +void initializeJumpInstrTablesPass(PassRegistry&); void initializeJumpThreadingPass(PassRegistry&); void initializeLCSSAPass(PassRegistry&); void initializeLICMPass(PassRegistry&); Index: include/llvm/LinkAllPasses.h =================================================================== --- include/llvm/LinkAllPasses.h +++ include/llvm/LinkAllPasses.h @@ -85,6 +85,8 @@ (void) llvm::createIndVarSimplifyPass(); (void) llvm::createInstructionCombiningPass(); (void) llvm::createInternalizePass(); + (void) llvm::createJumpInstrTableInfoPass(); + (void) llvm::createJumpInstrTablesPass(); (void) llvm::createLCSSAPass(); (void) llvm::createLICMPass(); (void) llvm::createLazyValueInfoPass(); Index: include/llvm/Target/TargetInstrInfo.h =================================================================== --- include/llvm/Target/TargetInstrInfo.h +++ include/llvm/Target/TargetInstrInfo.h @@ -29,6 +29,7 @@ class MDNode; class MCInst; class MCSchedModel; +class MCSymbolRefExpr; class SDNode; class ScheduleHazardRecognizer; class SelectionDAG; @@ -321,6 +322,20 @@ virtual void ReplaceTailWithBranchTo(MachineBasicBlock::iterator Tail, MachineBasicBlock *NewDest) const; + /// getUnconditionalBranch - Get an instruction that performs an unconditional + /// branch to the given symbol. + virtual void + getUnconditionalBranch(MCInst &MI, + const MCSymbolRefExpr *BranchTarget) const { + llvm_unreachable("Target didn't implement " + "TargetInstrInfo::getUnconditionalBranch!"); + } + + /// getTrap - Get a machine trap instruction + virtual void getTrap(MCInst &MI) const { + llvm_unreachable("Target didn't implement TargetInstrInfo::getTrap!"); + } + /// isLegalToSplitMBBAt - Return true if it's legal to split the given basic /// block at the specified instruction (i.e. instruction would be the start /// of a new basic block). Index: lib/Analysis/Analysis.cpp =================================================================== --- lib/Analysis/Analysis.cpp +++ lib/Analysis/Analysis.cpp @@ -48,6 +48,7 @@ initializeIVUsersPass(Registry); initializeInstCountPass(Registry); initializeIntervalPartitionPass(Registry); + initializeJumpInstrTableInfoPass(Registry); initializeLazyValueInfoPass(Registry); initializeLibCallAliasAnalysisPass(Registry); initializeLintPass(Registry); Index: lib/Analysis/CMakeLists.txt =================================================================== --- lib/Analysis/CMakeLists.txt +++ lib/Analysis/CMakeLists.txt @@ -25,6 +25,7 @@ InstructionSimplify.cpp Interval.cpp IntervalPartition.cpp + JumpInstrTableInfo.cpp LazyCallGraph.cpp LazyValueInfo.cpp LibCallAliasAnalysis.cpp Index: lib/Analysis/JumpInstrTableInfo.cpp =================================================================== --- /dev/null +++ lib/Analysis/JumpInstrTableInfo.cpp @@ -0,0 +1,50 @@ +//===-- JumpInstrTableInfo.cpp: Info for Jump-Instruction Tables ----------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Information about jump-instruction tables that have been created by +/// JumpInstrTables pass. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "jiti" + +#include "llvm/Analysis/JumpInstrTableInfo.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; + +INITIALIZE_PASS(JumpInstrTableInfo, "jump-instr-table-info", + "Jump-Instruction Table Info", true, true); +char JumpInstrTableInfo::ID = 0; + +ImmutablePass *llvm::createJumpInstrTableInfoPass() { + return new JumpInstrTableInfo(); +} + +namespace { +static cl::opt +JumpInstrTableAlignment("jump-instr-table-alignment", cl::Hidden, + cl::desc("Alignment in bytes of a jumptable entry"), + cl::init(3)); +}; + +JumpInstrTableInfo::JumpInstrTableInfo() : ImmutablePass(ID), Tables() { + initializeJumpInstrTableInfoPass(*PassRegistry::getPassRegistry()); +} + +JumpInstrTableInfo::~JumpInstrTableInfo() {} + +void JumpInstrTableInfo::insertEntry(FunctionType *TableFunTy, Function *Target, + Function *Jump) { + Tables[TableFunTy].push_back(JumpPair(Target, Jump)); +} + +unsigned JumpInstrTableInfo::getAlignment() { return JumpInstrTableAlignment; } Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -584,6 +584,7 @@ KEYWORD(cold); KEYWORD(inlinehint); KEYWORD(inreg); + KEYWORD(jumptable); KEYWORD(minsize); KEYWORD(naked); KEYWORD(nest); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -923,6 +923,7 @@ case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break; case lltok::kw_cold: B.addAttribute(Attribute::Cold); break; case lltok::kw_inlinehint: B.addAttribute(Attribute::InlineHint); break; + case lltok::kw_jumptable: B.addAttribute(Attribute::JumpTable); break; case lltok::kw_minsize: B.addAttribute(Attribute::MinSize); break; case lltok::kw_naked: B.addAttribute(Attribute::Naked); break; case lltok::kw_nobuiltin: B.addAttribute(Attribute::NoBuiltin); break; @@ -1184,6 +1185,7 @@ case lltok::kw_alwaysinline: case lltok::kw_builtin: case lltok::kw_inlinehint: + case lltok::kw_jumptable: case lltok::kw_minsize: case lltok::kw_naked: case lltok::kw_nobuiltin: @@ -1244,6 +1246,7 @@ case lltok::kw_builtin: case lltok::kw_cold: case lltok::kw_inlinehint: + case lltok::kw_jumptable: case lltok::kw_minsize: case lltok::kw_naked: case lltok::kw_nobuiltin: Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -107,6 +107,7 @@ kw_cold, kw_inlinehint, kw_inreg, + kw_jumptable, kw_minsize, kw_naked, kw_nest, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -549,6 +549,8 @@ return Attribute::InlineHint; case bitc::ATTR_KIND_IN_REG: return Attribute::InReg; + case bitc::ATTR_KIND_JUMP_TABLE: + return Attribute::JumpTable; case bitc::ATTR_KIND_MIN_SIZE: return Attribute::MinSize; case bitc::ATTR_KIND_NAKED: Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -177,6 +177,8 @@ return bitc::ATTR_KIND_INLINE_HINT; case Attribute::InReg: return bitc::ATTR_KIND_IN_REG; + case Attribute::JumpTable: + return bitc::ATTR_KIND_JUMP_TABLE; case Attribute::MinSize: return bitc::ATTR_KIND_MIN_SIZE; case Attribute::Naked: Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/ConstantFolding.h" +#include "llvm/Analysis/JumpInstrTableInfo.h" #include "llvm/CodeGen/GCMetadataPrinter.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineFrameInfo.h" @@ -951,6 +952,51 @@ } } + // Get information about jump-instruction tables to print. + JumpInstrTableInfo *JITI = getAnalysisIfAvailable(); + + if (JITI && JITI->getTables().size() > 0) { + unsigned Arch = Triple(getTargetTriple()).getArch(); + bool IsThumb = (Arch == Triple::thumb || Arch == Triple::thumbeb); + MCInst TrapInst; + TM.getInstrInfo()->getTrap(TrapInst); + for (const auto &KV : JITI->getTables()) { + uint64_t Count = 0; + for (const auto &FunPair : KV.second) { + // Emit the function labels to make this be a function entry point. + MCSymbol *FunSym = + OutContext.GetOrCreateSymbol(FunPair.second->getName()); + OutStreamer.EmitSymbolAttribute(FunSym, MCSA_Global); + EmitAlignment(JITI->getAlignment()); + if (IsThumb) + OutStreamer.EmitThumbFunc(FunSym); + if (MAI->hasDotTypeDotSizeDirective()) + OutStreamer.EmitSymbolAttribute(FunSym, MCSA_ELF_TypeFunction); + OutStreamer.EmitLabel(FunSym); + + // Emit the jump instruction to transfer control to the original + // function. + MCInst JumpToFun; + MCSymbol *TargetSymbol = + OutContext.GetOrCreateSymbol(FunPair.first->getName()); + const MCSymbolRefExpr *TargetSymRef = MCSymbolRefExpr::Create( + TargetSymbol, MCSymbolRefExpr::VK_PLT, OutContext); + TM.getInstrInfo()->getUnconditionalBranch(JumpToFun, TargetSymRef); + OutStreamer.EmitInstruction(JumpToFun, getSubtargetInfo()); + ++Count; + } + + // Emit enough padding instructions to fill up to the next power of two. + // This assumes that the aligned trap instruction takes the same amount of + // space as the aligned jump instruction. + uint64_t Remaining = NextPowerOf2(Count) - Count; + for (uint64_t C = 0; C < Remaining; ++C) { + EmitAlignment(JITI->getAlignment()); + OutStreamer.EmitInstruction(TrapInst, getSubtargetInfo()); + } + } + } + GCModuleInfo *MI = getAnalysisIfAvailable(); assert(MI && "AsmPrinter didn't require GCModuleInfo?"); for (GCModuleInfo::iterator I = MI->end(), E = MI->begin(); I != E; ) Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -27,6 +27,7 @@ InterferenceCache.cpp IntrinsicLowering.cpp JITCodeEmitter.cpp + JumpInstrTables.cpp LLVMTargetMachine.cpp LatencyPriorityQueue.cpp LexicalScopes.cpp Index: lib/CodeGen/JumpInstrTables.cpp =================================================================== --- /dev/null +++ lib/CodeGen/JumpInstrTables.cpp @@ -0,0 +1,306 @@ +//===-- 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/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include + +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() { return new JumpInstrTables(); } + +namespace { +/// Different versions of jump tables, as used by the command-line parameter +/// JumpTableType. +enum JumpTableTypes { + jump_single_table, + jump_arity_table, + jump_simplified_type_table, + jump_full_type_table +}; + +static const char jump_func_prefix[] = "__llvm_jump_instr_table_"; +static const char jump_section_prefix[] = ".jump.instr.table.text."; + +/// The algorithm used to split up tables by type +static cl::opt JumpTableType( + cl::desc("Function jump table type:"), + cl::values( + clEnumVal(jump_single_table, "A single table for all indirect calls"), + clEnumVal(jump_arity_table, "One table per number of parameters"), + clEnumVal(jump_simplified_type_table, + "One table per function type with types" + " projected into: pointer to non-function," + " struct, primitive, and function pointer"), + clEnumVal(jump_full_type_table, "One table per function type"), + clEnumValEnd)); + +// 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(V)) { + return !(CE->isCast() && isa(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(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(Us)) { + // Don't replace calls to bitcasts of function symbols, since they get + // translated to direct calls. + if (ConstantExpr *CE = dyn_cast(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(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(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) { + initializeJumpInstrTablesPass(*PassRegistry::getPassRegistry()); +} + +JumpInstrTables::~JumpInstrTables() {} + +void JumpInstrTables::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); +} + +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) { + TableMetadata Meta; + Meta.TableNum = TableCount; + Meta.Count = 0; + Metadata[FunTy] = Meta; + it = Metadata.find(FunTy); + ++NumJumpTables; + ++TableCount; + } + + it->second.Count++; + + Twine NewName = jump_func_prefix + Twine(it->second.TableNum) + "_" + + Twine(it->second.Count); + Function *JumpFun = Function::Create(OrigFunTy, GlobalValue::ExternalLinkage, + NewName.str(), &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); + JumpMap::iterator it = Metadata.find(TransTy); + return Metadata.end() != it; +} + +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 ParamTys(FunTy->getNumParams()); + FunctionType::param_iterator PI, PE; + int i = 0; + + std::vector EmptyParams; + Type *Int32Ty = Type::getInt32Ty(FunTy->getContext()); + FunctionType *VoidFnTy = FunctionType::get( + Type::getVoidTy(FunTy->getContext()), EmptyParams, false); + switch (JumpTableType) { + case jump_single_table: + + return FunctionType::get(RetTy, EmptyParams, false); + case jump_arity_table: + // 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 jump_simplified_type_table: + // 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(*PI) || isa(*PI) || + isa(*PI)) && + "This type is not an Integer or a Composite or a Function"); + if (isa(*PI)) { + ParamTys[i] = VoidPtrTy; + } else if (isa(*PI)) { + ParamTys[i] = VoidFnTy; + } else if (isa(*PI)) { + ParamTys[i] = Int32Ty; + } + } + + return FunctionType::get(RetTy, ParamTys, IsVarArg); + case jump_full_type_table: + // Don't transform this type at all. + return FunTy; + } + + return nullptr; +} + +bool JumpInstrTables::runOnModule(Module &M) { + JITI = &getAnalysis(); + + // Get the set of jumptable-annotated functions. + DenseMap 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 Aliases; + for (GlobalAlias &GA : M.aliases()) { + Constant *Aliasee = GA.getAliasee(); + if (Function *F = dyn_cast(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(); +} Index: lib/CodeGen/LLVMTargetMachine.cpp =================================================================== --- lib/CodeGen/LLVMTargetMachine.cpp +++ lib/CodeGen/LLVMTargetMachine.cpp @@ -12,6 +12,8 @@ //===----------------------------------------------------------------------===// #include "llvm/Target/TargetMachine.h" + +#include "llvm/Analysis/Passes.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineFunctionAnalysis.h" #include "llvm/CodeGen/MachineModuleInfo.h" @@ -52,6 +54,11 @@ AsmVerbose("asm-verbose", cl::desc("Add comments to directives."), cl::init(cl::BOU_UNSET)); +static cl::opt +BuildJumpInstrTables( + "build-jump-instr-tables", cl::Hidden, cl::init(false), + cl::desc("Build jump-instruction tables for address-taken functions")); + static bool getVerboseAsm() { switch (AsmVerbose) { case cl::BOU_UNSET: return TargetMachine::getAsmVerbosityDefault(); @@ -100,6 +107,11 @@ bool DisableVerify, AnalysisID StartAfter, AnalysisID StopAfter) { + // Always create the empty jump-instruction table info. + PM.add(createJumpInstrTableInfoPass()); + if (BuildJumpInstrTables) + PM.add(createJumpInstrTablesPass()); + // Add internal analysis passes from the target machine. TM->addAnalysisPasses(PM); Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -173,6 +173,8 @@ return "inlinehint"; if (hasAttribute(Attribute::InReg)) return "inreg"; + if (hasAttribute(Attribute::JumpTable)) + return "jumptable"; if (hasAttribute(Attribute::MinSize)) return "minsize"; if (hasAttribute(Attribute::Naked)) @@ -392,6 +394,7 @@ case Attribute::Builtin: return 1ULL << 41; case Attribute::OptimizeNone: return 1ULL << 42; case Attribute::InAlloca: return 1ULL << 43; + case Attribute::JumpTable: return 1ULL << 44; } llvm_unreachable("Unsupported attribute type"); } Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -727,7 +727,8 @@ I->getKindAsEnum() == Attribute::Builtin || I->getKindAsEnum() == Attribute::NoBuiltin || I->getKindAsEnum() == Attribute::Cold || - I->getKindAsEnum() == Attribute::OptimizeNone) { + I->getKindAsEnum() == Attribute::OptimizeNone || + I->getKindAsEnum() == Attribute::JumpTable) { if (!isFunction) { CheckFailed("Attribute '" + I->getAsString() + "' only applies to functions!", V); @@ -896,6 +897,14 @@ Attribute::MinSize), "Attributes 'minsize and optnone' are incompatible!", V); } + + if (Attrs.hasAttribute(AttributeSet::FunctionIndex, + Attribute::JumpTable)) { + const GlobalValue *GV = cast(V); + Assert1(GV->hasUnnamedAddr(), + "Attribute 'jumptable' requires 'unnamed_addr'", V); + + } } void Verifier::VerifyBitcastType(const Value *V, Type *DestTy, Type *SrcTy) { Index: lib/LTO/LTOCodeGenerator.cpp =================================================================== --- lib/LTO/LTOCodeGenerator.cpp +++ lib/LTO/LTOCodeGenerator.cpp @@ -96,6 +96,7 @@ initializeConstantMergePass(R); initializeDAHPass(R); initializeInstCombinerPass(R); + initializeJumpInstrTablesPass(R); initializeSimpleInlinerPass(R); initializePruneEHPass(R); initializeGlobalDCEPass(R); Index: lib/Target/ARM/ARMBaseInstrInfo.h =================================================================== --- lib/Target/ARM/ARMBaseInstrInfo.h +++ lib/Target/ARM/ARMBaseInstrInfo.h @@ -229,6 +229,13 @@ const TargetRegisterInfo*) const override; void breakPartialRegDependency(MachineBasicBlock::iterator, unsigned, const TargetRegisterInfo *TRI) const override; + + void + getUnconditionalBranch(MCInst &Branch, + const MCSymbolRefExpr *BranchTarget) const override; + + void getTrap(MCInst &MI) const override; + /// Get the number of addresses by LDM or VLDM or zero for unknown. unsigned getNumLDMAddresses(const MachineInstr *MI) const; Index: lib/Target/ARM/ARMBaseInstrInfo.cpp =================================================================== --- lib/Target/ARM/ARMBaseInstrInfo.cpp +++ lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCExpr.h" #include "llvm/Support/BranchProbability.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -4358,6 +4359,29 @@ MI->addRegisterKilled(DReg, TRI, true); } +void ARMBaseInstrInfo::getUnconditionalBranch( + MCInst &Branch, const MCSymbolRefExpr *BranchTarget) const { + if (Subtarget.isThumb()) + Branch.setOpcode(ARM::tB); + else if (Subtarget.isThumb2()) + Branch.setOpcode(ARM::t2B); + else + Branch.setOpcode(ARM::Bcc); + + Branch.addOperand(MCOperand::CreateExpr(BranchTarget)); + Branch.addOperand(MCOperand::CreateImm(ARMCC::AL)); + Branch.addOperand(MCOperand::CreateReg(0)); +} + +void ARMBaseInstrInfo::getTrap(MCInst &MI) const { + if (Subtarget.isThumb()) + MI.setOpcode(ARM::tTRAP); + else if (Subtarget.useNaClTrap()) + MI.setOpcode(ARM::TRAPNaCl); + else + MI.setOpcode(ARM::TRAP); +} + bool ARMBaseInstrInfo::hasNOP() const { return (Subtarget.getFeatureBits() & ARM::HasV6T2Ops) != 0; } Index: lib/Target/X86/X86InstrInfo.h =================================================================== --- lib/Target/X86/X86InstrInfo.h +++ lib/Target/X86/X86InstrInfo.h @@ -389,6 +389,12 @@ const SmallVectorImpl &MOs, unsigned Size, unsigned Alignment) const; + void + getUnconditionalBranch(MCInst &Branch, + const MCSymbolRefExpr *BranchTarget) const override; + + void getTrap(MCInst &MI) const override; + bool isHighLatencyDef(int opc) const override; bool hasHighOperandLatency(const InstrItineraryData *ItinData, Index: lib/Target/X86/X86InstrInfo.cpp =================================================================== --- lib/Target/X86/X86InstrInfo.cpp +++ lib/Target/X86/X86InstrInfo.cpp @@ -28,6 +28,7 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/LLVMContext.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -5239,6 +5240,16 @@ NopInst.setOpcode(X86::NOOP); } +void X86InstrInfo::getUnconditionalBranch( + MCInst &Branch, const MCSymbolRefExpr *BranchTarget) const { + Branch.setOpcode(X86::JMP_4); + Branch.addOperand(MCOperand::CreateExpr(BranchTarget)); +} + +void X86InstrInfo::getTrap(MCInst &MI) const { + MI.setOpcode(X86::TRAP); +} + bool X86InstrInfo::isHighLatencyDef(int opc) const { switch (opc) { default: return false; Index: lib/Transforms/IPO/IPO.cpp =================================================================== --- lib/Transforms/IPO/IPO.cpp +++ lib/Transforms/IPO/IPO.cpp @@ -30,6 +30,7 @@ initializeGlobalDCEPass(Registry); initializeGlobalOptPass(Registry); initializeIPCPPass(Registry); + initializeJumpInstrTablesPass(Registry); initializeAlwaysInlinerPass(Registry); initializeSimpleInlinerPass(Registry); initializeInternalizePassPass(Registry); Index: test/Bitcode/attributes.ll =================================================================== --- test/Bitcode/attributes.ll +++ test/Bitcode/attributes.ll @@ -203,7 +203,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #24 +; CHECK: call void @nobuiltin() #25 ret void; } @@ -218,6 +218,12 @@ ret void } +define void @f37() unnamed_addr jumptable { +; CHECK: define void @f37() unnamed_addr #24 + call void bitcast (void (i8*)* @f36 to void ()*)() + unreachable +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -242,5 +248,5 @@ ; CHECK: attributes #21 = { sspstrong } ; CHECK: attributes #22 = { minsize } ; CHECK: attributes #23 = { noinline optnone } -; CHECK: attributes #24 = { nobuiltin } - +; CHECK: attributes #24 = { jumptable } +; CHECK: attributes #25 = { nobuiltin } Index: test/CodeGen/ARM/jump_tables.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/jump_tables.ll @@ -0,0 +1,32 @@ +; RUN: llc -build-jump-instr-tables <%s -march=arm -jump_single_table | FileCheck --check-prefix=ARM %s +; RUN: llc -build-jump-instr-tables <%s -march=thumb -jump_single_table | 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) Index: test/CodeGen/X86/jump_table_alias.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/jump_table_alias.ll @@ -0,0 +1,33 @@ +; RUN: llc -build-jump-instr-tables <%s -jump_single_table | 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 + Index: test/CodeGen/X86/jump_table_bitcast.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/jump_table_bitcast.ll @@ -0,0 +1,46 @@ +; RUN: llc -build-jump-instr-tables <%s -jump_single_table | 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 + Index: test/CodeGen/X86/jump_tables.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/jump_tables.ll @@ -0,0 +1,276 @@ +; RUN: llc -build-jump-instr-tables <%s -jump_single_table | FileCheck --check-prefix=SINGLE %s +; RUN: llc -build-jump-instr-tables <%s -jump_arity_table | FileCheck --check-prefix=ARITY %s +; RUN: llc -build-jump-instr-tables <%s -jump_simplified_type_table | FileCheck --check-prefix=SIMPL %s +; RUN: llc -build-jump-instr-tables <%s -jump_full_type_table | FileCheck --check-prefix=FULL %s +; RUN: llc <%s | FileCheck --check-prefix=NONE %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 + +; NONE-NOT: __llvm_jump_instr_table +; NONE-NOT: ud2 Index: test/Verifier/jumptable.ll =================================================================== --- /dev/null +++ test/Verifier/jumptable.ll @@ -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!