Add a new attribute called 'jumptable' that creates jump-instruction tables for functions marked with this attribute.

It includes a pass that rewrites all indirect calls to jumptable functions to pass through these tables.

This also adds backend support for generating the jump-instruction tables on ARM and X86.
Note that since the jumptable attribute creates a second function pointer for a
function, any function marked with jumptable must also be marked with unnamed_addr.

llvm-svn: 210280
This commit is contained in:
Tom Roeder 2014-06-05 19:29:43 +00:00
parent f7e289c0f2
commit 44cb65fff1
40 changed files with 1109 additions and 8 deletions

View File

@ -1020,6 +1020,14 @@ example:
inlining this function is desirable (such as the "inline" keyword in inlining this function is desirable (such as the "inline" keyword in
C/C++). It is just a hint; it imposes no requirements on the C/C++). It is just a hint; it imposes no requirements on the
inliner. 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. 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`` ``minsize``
This attribute suggests that optimization passes and code generator This attribute suggests that optimization passes and code generator
passes make choices that keep the code size of this function as small passes make choices that keep the code size of this function as small

View File

@ -166,7 +166,8 @@ typedef enum {
LLVMCold = 1ULL << 34, LLVMCold = 1ULL << 34,
LLVMOptimizeNone = 1ULL << 35, LLVMOptimizeNone = 1ULL << 35,
LLVMInAllocaAttribute = 1ULL << 36, LLVMInAllocaAttribute = 1ULL << 36,
LLVMNonNullAttribute = 1ULL << 37 LLVMNonNullAttribute = 1ULL << 37,
LLVMJumpTableAttribute = 1ULL << 38,
*/ */
} LLVMAttribute; } LLVMAttribute;

View File

@ -0,0 +1,60 @@
//===-- 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 <vector>
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<Function *, Function *> JumpPair;
typedef DenseMap<FunctionType *, std::vector<JumpPair> > 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; }
private:
JumpTables Tables;
};
}
#endif /* LLVM_ANALYSIS_JUMPINSTRTABLEINFO_H */

View File

@ -142,6 +142,10 @@ namespace llvm {
// information and prints it with -analyze. // information and prints it with -analyze.
// //
FunctionPass *createMemDepPrinter(); FunctionPass *createMemDepPrinter();
// createJumpInstrTableInfoPass - This creates a pass that stores information
// about the jump tables created by JumpInstrTables
ImmutablePass *createJumpInstrTableInfoPass();
} }
#endif #endif

View File

@ -372,7 +372,8 @@ namespace bitc {
ATTR_KIND_COLD = 36, ATTR_KIND_COLD = 36,
ATTR_KIND_OPTIMIZE_NONE = 37, ATTR_KIND_OPTIMIZE_NONE = 37,
ATTR_KIND_IN_ALLOCA = 38, ATTR_KIND_IN_ALLOCA = 38,
ATTR_KIND_NON_NULL = 39 ATTR_KIND_NON_NULL = 39,
ATTR_KIND_JUMP_TABLE = 40
}; };
} // End bitc namespace } // End bitc namespace

View File

@ -202,6 +202,21 @@ FunctionSections("function-sections",
cl::desc("Emit functions into separate sections"), cl::desc("Emit functions into separate sections"),
cl::init(false)); cl::init(false));
cl::opt<llvm::JumpTable::JumpTableType>
JTableType("jump-table-type",
cl::desc("Choose the type of Jump-Instruction Table for jumptable."),
cl::init(JumpTable::Single),
cl::values(
clEnumValN(JumpTable::Single, "single",
"Create a single table for all jumptable functions"),
clEnumValN(JumpTable::Arity, "arity",
"Create one table per number of parameters."),
clEnumValN(JumpTable::Simplified, "simplified",
"Create one table per simplified function type."),
clEnumValN(JumpTable::Full, "full",
"Create one table per unique function type."),
clEnumValEnd));
// Common utility function tightly tied to the options listed here. Initializes // Common utility function tightly tied to the options listed here. Initializes
// a TargetOptions object with CodeGen flags and returns it. // a TargetOptions object with CodeGen flags and returns it.
static inline TargetOptions InitTargetOptionsFromCodeGenFlags() { static inline TargetOptions InitTargetOptionsFromCodeGenFlags() {
@ -228,6 +243,7 @@ static inline TargetOptions InitTargetOptionsFromCodeGenFlags() {
Options.FunctionSections = FunctionSections; Options.FunctionSections = FunctionSections;
Options.MCOptions = InitMCTargetOptionsFromFlags(); Options.MCOptions = InitMCTargetOptionsFromFlags();
Options.JTType = JTableType;
return Options; return Options;
} }

View File

@ -0,0 +1,102 @@
//===-- 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"
#include "llvm/Target/TargetOptions.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();
JumpInstrTables(JumpTable::JumpTableType JTT);
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
struct TableMeta {
/// The number of this table
unsigned TableNum;
/// The current number of jump entries in the table.
unsigned Count;
};
typedef DenseMap<FunctionType *, struct TableMeta> 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;
/// The type of tables to build.
JumpTable::JumpTableType JTType;
};
/// Creates a JumpInstrTables pass for the given type of jump table.
ModulePass *createJumpInstrTablesPass(JumpTable::JumpTableType JTT);
}
#endif /* LLVM_CODEGEN_JUMPINSTRTABLES_H */

View File

@ -588,6 +588,8 @@ namespace llvm {
/// the intrinsic for later emission to the StackMap. /// the intrinsic for later emission to the StackMap.
extern char &StackMapLivenessID; extern char &StackMapLivenessID;
/// createJumpInstrTables - This pass creates jump-instruction tables.
ModulePass *createJumpInstrTablesPass();
} // End llvm namespace } // End llvm namespace
#endif #endif

View File

@ -75,6 +75,7 @@ public:
Cold, ///< Marks function as being in a cold path. Cold, ///< Marks function as being in a cold path.
InlineHint, ///< Source said inlining was desirable InlineHint, ///< Source said inlining was desirable
InReg, ///< Force argument to be passed in register InReg, ///< Force argument to be passed in register
JumpTable, ///< Build jump-instruction tables and replace refs.
MinSize, ///< Function must be optimized for size first MinSize, ///< Function must be optimized for size first
Naked, ///< Naked function Naked, ///< Naked function
Nest, ///< Nested function static chain Nest, ///< Nested function static chain

View File

@ -146,6 +146,8 @@ void initializeInstCountPass(PassRegistry&);
void initializeInstNamerPass(PassRegistry&); void initializeInstNamerPass(PassRegistry&);
void initializeInternalizePassPass(PassRegistry&); void initializeInternalizePassPass(PassRegistry&);
void initializeIntervalPartitionPass(PassRegistry&); void initializeIntervalPartitionPass(PassRegistry&);
void initializeJumpInstrTableInfoPass(PassRegistry&);
void initializeJumpInstrTablesPass(PassRegistry&);
void initializeJumpThreadingPass(PassRegistry&); void initializeJumpThreadingPass(PassRegistry&);
void initializeLCSSAPass(PassRegistry&); void initializeLCSSAPass(PassRegistry&);
void initializeLICMPass(PassRegistry&); void initializeLICMPass(PassRegistry&);

View File

@ -85,6 +85,8 @@ namespace {
(void) llvm::createIndVarSimplifyPass(); (void) llvm::createIndVarSimplifyPass();
(void) llvm::createInstructionCombiningPass(); (void) llvm::createInstructionCombiningPass();
(void) llvm::createInternalizePass(); (void) llvm::createInternalizePass();
(void) llvm::createJumpInstrTableInfoPass();
(void) llvm::createJumpInstrTablesPass();
(void) llvm::createLCSSAPass(); (void) llvm::createLCSSAPass();
(void) llvm::createLICMPass(); (void) llvm::createLICMPass();
(void) llvm::createLazyValueInfoPass(); (void) llvm::createLazyValueInfoPass();

View File

@ -29,6 +29,7 @@ class MachineRegisterInfo;
class MDNode; class MDNode;
class MCInst; class MCInst;
class MCSchedModel; class MCSchedModel;
class MCSymbolRefExpr;
class SDNode; class SDNode;
class ScheduleHazardRecognizer; class ScheduleHazardRecognizer;
class SelectionDAG; class SelectionDAG;
@ -321,6 +322,20 @@ public:
virtual void ReplaceTailWithBranchTo(MachineBasicBlock::iterator Tail, virtual void ReplaceTailWithBranchTo(MachineBasicBlock::iterator Tail,
MachineBasicBlock *NewDest) const; 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 /// isLegalToSplitMBBAt - Return true if it's legal to split the given basic
/// block at the specified instruction (i.e. instruction would be the start /// block at the specified instruction (i.e. instruction would be the start
/// of a new basic block). /// of a new basic block).

View File

@ -39,6 +39,17 @@ namespace llvm {
}; };
} }
namespace JumpTable {
enum JumpTableType {
Single, // Use a single table for all indirect jumptable calls.
Arity, // Use one table per number of function parameters.
Simplified, // Use one table per function type, with types projected
// into 4 types: pointer to non-function, struct,
// primitive, and function pointer.
Full // Use one table per unique function type
};
}
class TargetOptions { class TargetOptions {
public: public:
TargetOptions() TargetOptions()
@ -54,7 +65,7 @@ namespace llvm {
CompressDebugSections(false), FunctionSections(false), CompressDebugSections(false), FunctionSections(false),
DataSections(false), TrapUnreachable(false), TrapFuncName(""), DataSections(false), TrapUnreachable(false), TrapFuncName(""),
FloatABIType(FloatABI::Default), FloatABIType(FloatABI::Default),
AllowFPOpFusion(FPOpFusion::Standard) {} AllowFPOpFusion(FPOpFusion::Standard), JTType(JumpTable::Single) {}
/// PrintMachineCode - This flag is enabled when the -print-machineinstrs /// PrintMachineCode - This flag is enabled when the -print-machineinstrs
/// option is specified on the command line, and should enable debugging /// option is specified on the command line, and should enable debugging
@ -205,6 +216,10 @@ namespace llvm {
/// the value of this option. /// the value of this option.
FPOpFusion::FPOpFusionMode AllowFPOpFusion; FPOpFusion::FPOpFusionMode AllowFPOpFusion;
/// JTType - This flag specifies the type of jump-instruction table to
/// create for functions that have the jumptable attribute.
JumpTable::JumpTableType JTType;
/// Machine level options. /// Machine level options.
MCTargetOptions MCOptions; MCTargetOptions MCOptions;
}; };

View File

@ -48,6 +48,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
initializeIVUsersPass(Registry); initializeIVUsersPass(Registry);
initializeInstCountPass(Registry); initializeInstCountPass(Registry);
initializeIntervalPartitionPass(Registry); initializeIntervalPartitionPass(Registry);
initializeJumpInstrTableInfoPass(Registry);
initializeLazyValueInfoPass(Registry); initializeLazyValueInfoPass(Registry);
initializeLibCallAliasAnalysisPass(Registry); initializeLibCallAliasAnalysisPass(Registry);
initializeLintPass(Registry); initializeLintPass(Registry);

View File

@ -25,6 +25,7 @@ add_llvm_library(LLVMAnalysis
InstructionSimplify.cpp InstructionSimplify.cpp
Interval.cpp Interval.cpp
IntervalPartition.cpp IntervalPartition.cpp
JumpInstrTableInfo.cpp
LazyCallGraph.cpp LazyCallGraph.cpp
LazyValueInfo.cpp LazyValueInfo.cpp
LibCallAliasAnalysis.cpp LibCallAliasAnalysis.cpp

View File

@ -0,0 +1,40 @@
//===-- 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"
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();
}
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));
}

View File

@ -583,6 +583,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(cold); KEYWORD(cold);
KEYWORD(inlinehint); KEYWORD(inlinehint);
KEYWORD(inreg); KEYWORD(inreg);
KEYWORD(jumptable);
KEYWORD(minsize); KEYWORD(minsize);
KEYWORD(naked); KEYWORD(naked);
KEYWORD(nest); KEYWORD(nest);

View File

@ -947,6 +947,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break; case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break;
case lltok::kw_cold: B.addAttribute(Attribute::Cold); break; case lltok::kw_cold: B.addAttribute(Attribute::Cold); break;
case lltok::kw_inlinehint: B.addAttribute(Attribute::InlineHint); 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_minsize: B.addAttribute(Attribute::MinSize); break;
case lltok::kw_naked: B.addAttribute(Attribute::Naked); break; case lltok::kw_naked: B.addAttribute(Attribute::Naked); break;
case lltok::kw_nobuiltin: B.addAttribute(Attribute::NoBuiltin); break; case lltok::kw_nobuiltin: B.addAttribute(Attribute::NoBuiltin); break;
@ -1210,6 +1211,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_alwaysinline: case lltok::kw_alwaysinline:
case lltok::kw_builtin: case lltok::kw_builtin:
case lltok::kw_inlinehint: case lltok::kw_inlinehint:
case lltok::kw_jumptable:
case lltok::kw_minsize: case lltok::kw_minsize:
case lltok::kw_naked: case lltok::kw_naked:
case lltok::kw_nobuiltin: case lltok::kw_nobuiltin:
@ -1271,6 +1273,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_builtin: case lltok::kw_builtin:
case lltok::kw_cold: case lltok::kw_cold:
case lltok::kw_inlinehint: case lltok::kw_inlinehint:
case lltok::kw_jumptable:
case lltok::kw_minsize: case lltok::kw_minsize:
case lltok::kw_naked: case lltok::kw_naked:
case lltok::kw_nobuiltin: case lltok::kw_nobuiltin:

View File

@ -107,6 +107,7 @@ namespace lltok {
kw_cold, kw_cold,
kw_inlinehint, kw_inlinehint,
kw_inreg, kw_inreg,
kw_jumptable,
kw_minsize, kw_minsize,
kw_naked, kw_naked,
kw_nest, kw_nest,

View File

@ -549,6 +549,8 @@ static Attribute::AttrKind GetAttrFromCode(uint64_t Code) {
return Attribute::InlineHint; return Attribute::InlineHint;
case bitc::ATTR_KIND_IN_REG: case bitc::ATTR_KIND_IN_REG:
return Attribute::InReg; return Attribute::InReg;
case bitc::ATTR_KIND_JUMP_TABLE:
return Attribute::JumpTable;
case bitc::ATTR_KIND_MIN_SIZE: case bitc::ATTR_KIND_MIN_SIZE:
return Attribute::MinSize; return Attribute::MinSize;
case bitc::ATTR_KIND_NAKED: case bitc::ATTR_KIND_NAKED:

View File

@ -177,6 +177,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_INLINE_HINT; return bitc::ATTR_KIND_INLINE_HINT;
case Attribute::InReg: case Attribute::InReg:
return bitc::ATTR_KIND_IN_REG; return bitc::ATTR_KIND_IN_REG;
case Attribute::JumpTable:
return bitc::ATTR_KIND_JUMP_TABLE;
case Attribute::MinSize: case Attribute::MinSize:
return bitc::ATTR_KIND_MIN_SIZE; return bitc::ATTR_KIND_MIN_SIZE;
case Attribute::Naked: case Attribute::Naked:

View File

@ -18,6 +18,7 @@
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/JumpInstrTableInfo.h"
#include "llvm/CodeGen/GCMetadataPrinter.h" #include "llvm/CodeGen/GCMetadataPrinter.h"
#include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFrameInfo.h"
@ -889,6 +890,54 @@ bool AsmPrinter::doFinalization(Module &M) {
EmitVisibility(Name, V, false); EmitVisibility(Name, V, false);
} }
// Get information about jump-instruction tables to print.
JumpInstrTableInfo *JITI = getAnalysisIfAvailable<JumpInstrTableInfo>();
if (JITI && !JITI->getTables().empty()) {
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);
// FIXME: JumpTableInstrInfo should store information about the required
// alignment of table entries and the size of the padding instruction.
EmitAlignment(3);
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 trap instruction takes 8 bytes or fewer.
uint64_t Remaining = NextPowerOf2(Count) - Count;
for (uint64_t C = 0; C < Remaining; ++C) {
EmitAlignment(3);
OutStreamer.EmitInstruction(TrapInst, getSubtargetInfo());
}
}
}
// Emit module flags. // Emit module flags.
SmallVector<Module::ModuleFlagEntry, 8> ModuleFlags; SmallVector<Module::ModuleFlagEntry, 8> ModuleFlags;
M.getModuleFlagsMetadata(ModuleFlags); M.getModuleFlagsMetadata(ModuleFlags);

View File

@ -27,6 +27,7 @@ add_llvm_library(LLVMCodeGen
InterferenceCache.cpp InterferenceCache.cpp
IntrinsicLowering.cpp IntrinsicLowering.cpp
JITCodeEmitter.cpp JITCodeEmitter.cpp
JumpInstrTables.cpp
LLVMTargetMachine.cpp LLVMTargetMachine.cpp
LatencyPriorityQueue.cpp LatencyPriorityQueue.cpp
LexicalScopes.cpp LexicalScopes.cpp

View File

@ -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();
}

View File

@ -12,11 +12,15 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetMachine.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/JumpInstrTables.h"
#include "llvm/CodeGen/MachineFunctionAnalysis.h" #include "llvm/CodeGen/MachineFunctionAnalysis.h"
#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/Passes.h"
#include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/Verifier.h"
#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h" #include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCInstrInfo.h"
@ -82,6 +86,7 @@ static MCContext *addPassesToGenerateCode(LLVMTargetMachine *TM,
bool DisableVerify, bool DisableVerify,
AnalysisID StartAfter, AnalysisID StartAfter,
AnalysisID StopAfter) { AnalysisID StopAfter) {
// Add internal analysis passes from the target machine. // Add internal analysis passes from the target machine.
TM->addAnalysisPasses(PM); TM->addAnalysisPasses(PM);
@ -136,6 +141,11 @@ bool LLVMTargetMachine::addPassesToEmitFile(PassManagerBase &PM,
bool DisableVerify, bool DisableVerify,
AnalysisID StartAfter, AnalysisID StartAfter,
AnalysisID StopAfter) { AnalysisID StopAfter) {
// Passes to handle jumptable function annotations. These can't be handled at
// JIT time, so we don't add them directly to addPassesToGenerateCode.
PM.add(createJumpInstrTableInfoPass());
PM.add(createJumpInstrTablesPass(Options.JTType));
// Add common CodeGen passes. // Add common CodeGen passes.
MCContext *Context = addPassesToGenerateCode(this, PM, DisableVerify, MCContext *Context = addPassesToGenerateCode(this, PM, DisableVerify,
StartAfter, StopAfter); StartAfter, StopAfter);

View File

@ -173,6 +173,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "inlinehint"; return "inlinehint";
if (hasAttribute(Attribute::InReg)) if (hasAttribute(Attribute::InReg))
return "inreg"; return "inreg";
if (hasAttribute(Attribute::JumpTable))
return "jumptable";
if (hasAttribute(Attribute::MinSize)) if (hasAttribute(Attribute::MinSize))
return "minsize"; return "minsize";
if (hasAttribute(Attribute::Naked)) if (hasAttribute(Attribute::Naked))
@ -395,6 +397,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
case Attribute::OptimizeNone: return 1ULL << 42; case Attribute::OptimizeNone: return 1ULL << 42;
case Attribute::InAlloca: return 1ULL << 43; case Attribute::InAlloca: return 1ULL << 43;
case Attribute::NonNull: return 1ULL << 44; case Attribute::NonNull: return 1ULL << 44;
case Attribute::JumpTable: return 1ULL << 45;
} }
llvm_unreachable("Unsupported attribute type"); llvm_unreachable("Unsupported attribute type");
} }

View File

@ -735,7 +735,8 @@ void Verifier::VerifyAttributeTypes(AttributeSet Attrs, unsigned Idx,
I->getKindAsEnum() == Attribute::Builtin || I->getKindAsEnum() == Attribute::Builtin ||
I->getKindAsEnum() == Attribute::NoBuiltin || I->getKindAsEnum() == Attribute::NoBuiltin ||
I->getKindAsEnum() == Attribute::Cold || I->getKindAsEnum() == Attribute::Cold ||
I->getKindAsEnum() == Attribute::OptimizeNone) { I->getKindAsEnum() == Attribute::OptimizeNone ||
I->getKindAsEnum() == Attribute::JumpTable) {
if (!isFunction) { if (!isFunction) {
CheckFailed("Attribute '" + I->getAsString() + CheckFailed("Attribute '" + I->getAsString() +
"' only applies to functions!", V); "' only applies to functions!", V);
@ -909,6 +910,14 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs,
Attribute::MinSize), Attribute::MinSize),
"Attributes 'minsize and optnone' are incompatible!", V); "Attributes 'minsize and optnone' are incompatible!", V);
} }
if (Attrs.hasAttribute(AttributeSet::FunctionIndex,
Attribute::JumpTable)) {
const GlobalValue *GV = cast<GlobalValue>(V);
Assert1(GV->hasUnnamedAddr(),
"Attribute 'jumptable' requires 'unnamed_addr'", V);
}
} }
void Verifier::VerifyBitcastType(const Value *V, Type *DestTy, Type *SrcTy) { void Verifier::VerifyBitcastType(const Value *V, Type *DestTy, Type *SrcTy) {

View File

@ -96,6 +96,7 @@ void LTOCodeGenerator::initializeLTOPasses() {
initializeConstantMergePass(R); initializeConstantMergePass(R);
initializeDAHPass(R); initializeDAHPass(R);
initializeInstCombinerPass(R); initializeInstCombinerPass(R);
initializeJumpInstrTablesPass(R);
initializeSimpleInlinerPass(R); initializeSimpleInlinerPass(R);
initializePruneEHPass(R); initializePruneEHPass(R);
initializeGlobalDCEPass(R); initializeGlobalDCEPass(R);

View File

@ -32,6 +32,7 @@
#include "llvm/IR/Function.h" #include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalValue.h"
#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/Support/BranchProbability.h" #include "llvm/Support/BranchProbability.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
@ -4358,6 +4359,29 @@ breakPartialRegDependency(MachineBasicBlock::iterator MI,
MI->addRegisterKilled(DReg, TRI, true); 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 { bool ARMBaseInstrInfo::hasNOP() const {
return (Subtarget.getFeatureBits() & ARM::HasV6T2Ops) != 0; return (Subtarget.getFeatureBits() & ARM::HasV6T2Ops) != 0;
} }

View File

@ -229,6 +229,13 @@ public:
const TargetRegisterInfo*) const override; const TargetRegisterInfo*) const override;
void breakPartialRegDependency(MachineBasicBlock::iterator, unsigned, void breakPartialRegDependency(MachineBasicBlock::iterator, unsigned,
const TargetRegisterInfo *TRI) const override; 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. /// Get the number of addresses by LDM or VLDM or zero for unknown.
unsigned getNumLDMAddresses(const MachineInstr *MI) const; unsigned getNumLDMAddresses(const MachineInstr *MI) const;

View File

@ -28,6 +28,7 @@
#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h" #include "llvm/MC/MCInst.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
@ -5299,6 +5300,16 @@ void X86InstrInfo::getNoopForMachoTarget(MCInst &NopInst) const {
NopInst.setOpcode(X86::NOOP); 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 { bool X86InstrInfo::isHighLatencyDef(int opc) const {
switch (opc) { switch (opc) {
default: return false; default: return false;

View File

@ -396,6 +396,12 @@ public:
const SmallVectorImpl<MachineOperand> &MOs, const SmallVectorImpl<MachineOperand> &MOs,
unsigned Size, unsigned Alignment) const; 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 isHighLatencyDef(int opc) const override;
bool hasHighOperandLatency(const InstrItineraryData *ItinData, bool hasHighOperandLatency(const InstrItineraryData *ItinData,

View File

@ -30,6 +30,7 @@ void llvm::initializeIPO(PassRegistry &Registry) {
initializeGlobalDCEPass(Registry); initializeGlobalDCEPass(Registry);
initializeGlobalOptPass(Registry); initializeGlobalOptPass(Registry);
initializeIPCPPass(Registry); initializeIPCPPass(Registry);
initializeJumpInstrTablesPass(Registry);
initializeAlwaysInlinerPass(Registry); initializeAlwaysInlinerPass(Registry);
initializeSimpleInlinerPass(Registry); initializeSimpleInlinerPass(Registry);
initializeInternalizePassPass(Registry); initializeInternalizePassPass(Registry);

View File

@ -203,7 +203,7 @@ define void @f34()
; CHECK: define void @f34() ; CHECK: define void @f34()
{ {
call void @nobuiltin() nobuiltin call void @nobuiltin() nobuiltin
; CHECK: call void @nobuiltin() #24 ; CHECK: call void @nobuiltin() #25
ret void; ret void;
} }
@ -223,6 +223,12 @@ define nonnull i8* @f37(i8* nonnull %a) {
ret i8* %a ret i8* %a
} }
define void @f38() unnamed_addr jumptable {
; CHECK: define void @f38() unnamed_addr #24
call void bitcast (void (i8*)* @f36 to void ()*)()
unreachable
}
; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone } ; CHECK: attributes #2 = { readnone }
@ -247,5 +253,5 @@ define nonnull i8* @f37(i8* nonnull %a) {
; CHECK: attributes #21 = { sspstrong } ; CHECK: attributes #21 = { sspstrong }
; CHECK: attributes #22 = { minsize } ; CHECK: attributes #22 = { minsize }
; CHECK: attributes #23 = { noinline optnone } ; CHECK: attributes #23 = { noinline optnone }
; CHECK: attributes #24 = { nobuiltin } ; CHECK: attributes #24 = { jumptable }
; CHECK: attributes #25 = { nobuiltin }

View File

@ -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)

View File

@ -5,6 +5,6 @@
; STOP: Loop Strength Reduction ; STOP: Loop Strength Reduction
; STOP-NEXT: Machine Function Analysis ; STOP-NEXT: Machine Function Analysis
; START: -machine-branch-prob -gc-lowering ; START: -machine-branch-prob -jump-instr-tables -gc-lowering
; START: FunctionPass Manager ; START: FunctionPass Manager
; START-NEXT: Lower Garbage Collection Instructions ; START-NEXT: Lower Garbage Collection Instructions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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!