1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(cold);
KEYWORD(inlinehint);
KEYWORD(inreg);
KEYWORD(jumptable);
KEYWORD(minsize);
KEYWORD(naked);
KEYWORD(nest);
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
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;
Expand Down Expand Up @@ -1210,6 +1211,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
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:
Expand Down Expand Up @@ -1271,6 +1273,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
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:
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ namespace lltok {
kw_cold,
kw_inlinehint,
kw_inreg,
kw_jumptable,
kw_minsize,
kw_naked,
kw_nest,
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ static Attribute::AttrKind GetAttrFromCode(uint64_t Code) {
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:
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
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:
Expand Down
49 changes: 49 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -889,6 +890,54 @@ bool AsmPrinter::doFinalization(Module &M) {
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.
SmallVector<Module::ModuleFlagEntry, 8> ModuleFlags;
M.getModuleFlagsMetadata(ModuleFlags);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_llvm_library(LLVMCodeGen
InterferenceCache.cpp
IntrinsicLowering.cpp
JITCodeEmitter.cpp
JumpInstrTables.cpp
LLVMTargetMachine.cpp
LatencyPriorityQueue.cpp
LexicalScopes.cpp
Expand Down
301 changes: 301 additions & 0 deletions llvm/lib/CodeGen/JumpInstrTables.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
//===-- JumpInstrTables.cpp: Jump-Instruction Tables ----------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief An implementation of jump-instruction tables.
///
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "jt"

#include "llvm/CodeGen/JumpInstrTables.h"

#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/JumpInstrTableInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

#include <vector>

using namespace llvm;

char JumpInstrTables::ID = 0;

INITIALIZE_PASS_BEGIN(JumpInstrTables, "jump-instr-tables",
"Jump-Instruction Tables", true, true)
INITIALIZE_PASS_DEPENDENCY(JumpInstrTableInfo);
INITIALIZE_PASS_END(JumpInstrTables, "jump-instr-tables",
"Jump-Instruction Tables", true, true)

STATISTIC(NumJumpTables, "Number of indirect call tables generated");
STATISTIC(NumFuncsInJumpTables, "Number of functions in the jump tables");

ModulePass *llvm::createJumpInstrTablesPass() {
// The default implementation uses a single table for all functions.
return new JumpInstrTables(JumpTable::Single);
}

ModulePass *llvm::createJumpInstrTablesPass(JumpTable::JumpTableType JTT) {
return new JumpInstrTables(JTT);
}

namespace {
static const char jump_func_prefix[] = "__llvm_jump_instr_table_";
static const char jump_section_prefix[] = ".jump.instr.table.text.";

// Checks to see if a given CallSite is making an indirect call, including
// cases where the indirect call is made through a bitcast.
bool isIndirectCall(CallSite &CS) {
if (CS.getCalledFunction())
return false;

// Check the value to see if it is merely a bitcast of a function. In
// this case, it will translate to a direct function call in the resulting
// assembly, so we won't treat it as an indirect call here.
const Value *V = CS.getCalledValue();
if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(V)) {
return !(CE->isCast() && isa<Function>(CE->getOperand(0)));
}

// Otherwise, since we know it's a call, it must be an indirect call
return true;
}

// Replaces Functions and GlobalAliases with a different Value.
bool replaceGlobalValueIndirectUse(GlobalValue *GV, Value *V, Use *U) {
User *Us = U->getUser();
if (!Us)
return false;
if (Instruction *I = dyn_cast<Instruction>(Us)) {
CallSite CS(I);

// Don't do the replacement if this use is a direct call to this function.
// If the use is not the called value, then replace it.
if (CS && (isIndirectCall(CS) || CS.isCallee(U))) {
return false;
}

U->set(V);
} else if (Constant *C = dyn_cast<Constant>(Us)) {
// Don't replace calls to bitcasts of function symbols, since they get
// translated to direct calls.
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Us)) {
if (CE->getOpcode() == Instruction::BitCast) {
// This bitcast must have exactly one user.
if (CE->user_begin() != CE->user_end()) {
User *ParentUs = *CE->user_begin();
if (CallInst *CI = dyn_cast<CallInst>(ParentUs)) {
CallSite CS(CI);
Use &CEU = *CE->use_begin();
if (CS.isCallee(&CEU)) {
return false;
}
}
}
}
}

// GlobalAlias doesn't support replaceUsesOfWithOnConstant. And the verifier
// requires alias to point to a defined function. So, GlobalAlias is handled
// as a separate case in runOnModule.
if (!isa<GlobalAlias>(C))
C->replaceUsesOfWithOnConstant(GV, V, U);
} else {
assert(false && "The Use of a Function symbol is neither an instruction nor"
" a constant");
}

return true;
}

// Replaces all replaceable address-taken uses of GV with a pointer to a
// jump-instruction table entry.
void replaceValueWithFunction(GlobalValue *GV, Function *F) {
// Go through all uses of this function and replace the uses of GV with the
// jump-table version of the function. Get the uses as a vector before
// replacing them, since replacing them changes the use list and invalidates
// the iterator otherwise.
for (Value::use_iterator I = GV->use_begin(), E = GV->use_end(); I != E;) {
Use &U = *I++;

// Replacement of constants replaces all instances in the constant. So, some
// uses might have already been handled by the time we reach them here.
if (U.get() == GV)
replaceGlobalValueIndirectUse(GV, F, &U);
}

return;
}
} // end anonymous namespace

JumpInstrTables::JumpInstrTables()
: ModulePass(ID), Metadata(), JITI(nullptr), TableCount(0),
JTType(JumpTable::Single) {
initializeJumpInstrTablesPass(*PassRegistry::getPassRegistry());
}

JumpInstrTables::JumpInstrTables(JumpTable::JumpTableType JTT)
: ModulePass(ID), Metadata(), JITI(nullptr), TableCount(0), JTType(JTT) {
initializeJumpInstrTablesPass(*PassRegistry::getPassRegistry());
}

JumpInstrTables::~JumpInstrTables() {}

void JumpInstrTables::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<JumpInstrTableInfo>();
}

Function *JumpInstrTables::insertEntry(Module &M, Function *Target) {
FunctionType *OrigFunTy = Target->getFunctionType();
FunctionType *FunTy = transformType(OrigFunTy);

JumpMap::iterator it = Metadata.find(FunTy);
if (Metadata.end() == it) {
struct TableMeta Meta;
Meta.TableNum = TableCount;
Meta.Count = 0;
Metadata[FunTy] = Meta;
it = Metadata.find(FunTy);
++NumJumpTables;
++TableCount;
}

it->second.Count++;

std::string NewName(jump_func_prefix);
NewName += (Twine(it->second.TableNum) + "_" + Twine(it->second.Count)).str();
Function *JumpFun =
Function::Create(OrigFunTy, GlobalValue::ExternalLinkage, NewName, &M);
// The section for this table
JumpFun->setSection((jump_section_prefix + Twine(it->second.TableNum)).str());
JITI->insertEntry(FunTy, Target, JumpFun);

++NumFuncsInJumpTables;
return JumpFun;
}

bool JumpInstrTables::hasTable(FunctionType *FunTy) {
FunctionType *TransTy = transformType(FunTy);
return Metadata.end() != Metadata.find(TransTy);
}

FunctionType *JumpInstrTables::transformType(FunctionType *FunTy) {
// Returning nullptr forces all types into the same table, since all types map
// to the same type
Type *VoidPtrTy = Type::getInt8PtrTy(FunTy->getContext());

// Ignore the return type.
Type *RetTy = VoidPtrTy;
bool IsVarArg = FunTy->isVarArg();
std::vector<Type *> ParamTys(FunTy->getNumParams());
FunctionType::param_iterator PI, PE;
int i = 0;

std::vector<Type *> EmptyParams;
Type *Int32Ty = Type::getInt32Ty(FunTy->getContext());
FunctionType *VoidFnTy = FunctionType::get(
Type::getVoidTy(FunTy->getContext()), EmptyParams, false);
switch (JTType) {
case JumpTable::Single:

return FunctionType::get(RetTy, EmptyParams, false);
case JumpTable::Arity:
// Transform all types to void* so that all functions with the same arity
// end up in the same table.
for (PI = FunTy->param_begin(), PE = FunTy->param_end(); PI != PE;
PI++, i++) {
ParamTys[i] = VoidPtrTy;
}

return FunctionType::get(RetTy, ParamTys, IsVarArg);
case JumpTable::Simplified:
// Project all parameters types to one of 3 types: composite, integer, and
// function, matching the three subclasses of Type.
for (PI = FunTy->param_begin(), PE = FunTy->param_end(); PI != PE;
++PI, ++i) {
assert((isa<IntegerType>(*PI) || isa<FunctionType>(*PI) ||
isa<CompositeType>(*PI)) &&
"This type is not an Integer or a Composite or a Function");
if (isa<CompositeType>(*PI)) {
ParamTys[i] = VoidPtrTy;
} else if (isa<FunctionType>(*PI)) {
ParamTys[i] = VoidFnTy;
} else if (isa<IntegerType>(*PI)) {
ParamTys[i] = Int32Ty;
}
}

return FunctionType::get(RetTy, ParamTys, IsVarArg);
case JumpTable::Full:
// Don't transform this type at all.
return FunTy;
}

return nullptr;
}

bool JumpInstrTables::runOnModule(Module &M) {
// Make sure the module is well-formed, especially with respect to jumptable.
if (verifyModule(M))
return false;

JITI = &getAnalysis<JumpInstrTableInfo>();

// Get the set of jumptable-annotated functions.
DenseMap<Function *, Function *> Functions;
for (Function &F : M) {
if (F.hasFnAttribute(Attribute::JumpTable)) {
assert(F.hasUnnamedAddr() &&
"Attribute 'jumptable' requires 'unnamed_addr'");
Functions[&F] = NULL;
}
}

// Create the jump-table functions.
for (auto &KV : Functions) {
Function *F = KV.first;
KV.second = insertEntry(M, F);
}

// GlobalAlias is a special case, because the target of an alias statement
// must be a defined function. So, instead of replacing a given function in
// the alias, we replace all uses of aliases that target jumptable functions.
// Note that there's no need to create these functions, since only aliases
// that target known jumptable functions are replaced, and there's no way to
// put the jumptable annotation on a global alias.
DenseMap<GlobalAlias *, Function *> Aliases;
for (GlobalAlias &GA : M.aliases()) {
Constant *Aliasee = GA.getAliasee();
if (Function *F = dyn_cast<Function>(Aliasee)) {
auto it = Functions.find(F);
if (it != Functions.end()) {
Aliases[&GA] = it->second;
}
}
}

// Replace each address taken function with its jump-instruction table entry.
for (auto &KV : Functions)
replaceValueWithFunction(KV.first, KV.second);

for (auto &KV : Aliases)
replaceValueWithFunction(KV.first, KV.second);

return !Functions.empty();
}
10 changes: 10 additions & 0 deletions llvm/lib/CodeGen/LLVMTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@
//===----------------------------------------------------------------------===//

#include "llvm/Target/TargetMachine.h"

#include "llvm/Analysis/Passes.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/JumpInstrTables.h"
#include "llvm/CodeGen/MachineFunctionAnalysis.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/Verifier.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
Expand Down Expand Up @@ -82,6 +86,7 @@ static MCContext *addPassesToGenerateCode(LLVMTargetMachine *TM,
bool DisableVerify,
AnalysisID StartAfter,
AnalysisID StopAfter) {

// Add internal analysis passes from the target machine.
TM->addAnalysisPasses(PM);

Expand Down Expand Up @@ -136,6 +141,11 @@ bool LLVMTargetMachine::addPassesToEmitFile(PassManagerBase &PM,
bool DisableVerify,
AnalysisID StartAfter,
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.
MCContext *Context = addPassesToGenerateCode(this, PM, DisableVerify,
StartAfter, StopAfter);
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "inlinehint";
if (hasAttribute(Attribute::InReg))
return "inreg";
if (hasAttribute(Attribute::JumpTable))
return "jumptable";
if (hasAttribute(Attribute::MinSize))
return "minsize";
if (hasAttribute(Attribute::Naked))
Expand Down Expand Up @@ -395,6 +397,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
case Attribute::OptimizeNone: return 1ULL << 42;
case Attribute::InAlloca: return 1ULL << 43;
case Attribute::NonNull: return 1ULL << 44;
case Attribute::JumpTable: return 1ULL << 45;
}
llvm_unreachable("Unsupported attribute type");
}
Expand Down
11 changes: 10 additions & 1 deletion llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,8 @@ void Verifier::VerifyAttributeTypes(AttributeSet Attrs, unsigned Idx,
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);
Expand Down Expand Up @@ -909,6 +910,14 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs,
Attribute::MinSize),
"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) {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/LTO/LTOCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ void LTOCodeGenerator::initializeLTOPasses() {
initializeConstantMergePass(R);
initializeDAHPass(R);
initializeInstCombinerPass(R);
initializeJumpInstrTablesPass(R);
initializeSimpleInlinerPass(R);
initializePruneEHPass(R);
initializeGlobalDCEPass(R);
Expand Down
24 changes: 24 additions & 0 deletions llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -4358,6 +4359,29 @@ breakPartialRegDependency(MachineBasicBlock::iterator MI,
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;
}
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/Target/ARM/ARMBaseInstrInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ class ARMBaseInstrInfo : public ARMGenInstrInfo {
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;

Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/Target/X86/X86InstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -5299,6 +5300,16 @@ void X86InstrInfo::getNoopForMachoTarget(MCInst &NopInst) const {
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;
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/X86/X86InstrInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ class X86InstrInfo final : public X86GenInstrInfo {
const SmallVectorImpl<MachineOperand> &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,
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/IPO/IPO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ void llvm::initializeIPO(PassRegistry &Registry) {
initializeGlobalDCEPass(Registry);
initializeGlobalOptPass(Registry);
initializeIPCPPass(Registry);
initializeJumpInstrTablesPass(Registry);
initializeAlwaysInlinerPass(Registry);
initializeSimpleInlinerPass(Registry);
initializeInternalizePassPass(Registry);
Expand Down
12 changes: 9 additions & 3 deletions llvm/test/Bitcode/attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ define void @f34()
; CHECK: define void @f34()
{
call void @nobuiltin() nobuiltin
; CHECK: call void @nobuiltin() #24
; CHECK: call void @nobuiltin() #25
ret void;
}

Expand All @@ -223,6 +223,12 @@ define nonnull i8* @f37(i8* nonnull %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 #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
Expand All @@ -247,5 +253,5 @@ define nonnull i8* @f37(i8* nonnull %a) {
; 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 }
32 changes: 32 additions & 0 deletions llvm/test/CodeGen/ARM/jump_tables.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
; RUN: llc <%s -march=arm -jump-table-type=single | FileCheck --check-prefix=ARM %s
; RUN: llc <%s -march=thumb -jump-table-type=single | FileCheck --check-prefix=THUMB %s

define void @indirect_fun() unnamed_addr jumptable {
ret void
}
define void ()* @get_fun() {
ret void ()* @indirect_fun

; ARM: ldr r0, [[LABEL:.*]]
; ARM: mov pc, lr
; ARM: [[LABEL]]:
; ARM: .long __llvm_jump_instr_table_0_1

; THUMB: ldr r0, [[LABEL:.*]]
; THUMB: bx lr
; THUMB: [[LABEL]]:
; THUMB: .long __llvm_jump_instr_table_0_1
}

; ARM: .globl __llvm_jump_instr_table_0_1
; ARM: .align 3
; ARM: .type __llvm_jump_instr_table_0_1,%function
; ARM: __llvm_jump_instr_table_0_1:
; ARM: b indirect_fun(PLT)

; THUMB: .globl __llvm_jump_instr_table_0_1
; THUMB: .align 3
; THUMB: .thumb_func
; THUMB: .type __llvm_jump_instr_table_0_1,%function
; THUMB: __llvm_jump_instr_table_0_1:
; THUMB: b indirect_fun(PLT)
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/Generic/stop-after.ll
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
; STOP: Loop Strength Reduction
; STOP-NEXT: Machine Function Analysis

; START: -machine-branch-prob -gc-lowering
; START: -machine-branch-prob -jump-instr-tables -gc-lowering
; START: FunctionPass Manager
; START-NEXT: Lower Garbage Collection Instructions
33 changes: 33 additions & 0 deletions llvm/test/CodeGen/X86/jump_table_alias.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
; RUN: llc <%s -jump-table-type=single | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
define i32 @f() unnamed_addr jumptable {
entry:
ret i32 0
}

@i = alias internal i32 ()* @f
@j = alias i32 ()* @f

define i32 @main(i32 %argc, i8** %argv) {
%temp = alloca i32 ()*, align 8
store i32 ()* @i, i32()** %temp, align 8
; CHECK: movq $__llvm_jump_instr_table_0_1
%1 = load i32 ()** %temp, align 8
; CHECK: movl $__llvm_jump_instr_table_0_1
%2 = call i32 ()* %1()
%3 = call i32 ()* @i()
; CHECK: callq i
%4 = call i32 ()* @j()
; CHECK: callq j
ret i32 %3
}

; There should only be one table, even though there are two GlobalAliases,
; because they both alias the same value.

; CHECK: .globl __llvm_jump_instr_table_0_1
; CHECK: .align 8, 0x90
; CHECK: .type __llvm_jump_instr_table_0_1,@function
; CHECK: __llvm_jump_instr_table_0_1:
; CHECK: jmp f@PLT

46 changes: 46 additions & 0 deletions llvm/test/CodeGen/X86/jump_table_bitcast.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
; RUN: llc <%s -jump-table-type=single | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
define i32 @f() unnamed_addr jumptable {
ret i32 0
}

define i32 @g(i8* %a) unnamed_addr jumptable {
ret i32 0
}

define void @h(void ()* %func) unnamed_addr jumptable {
ret void
}

define i32 @main() {
%g = alloca i32 (...)*, align 8
store i32 (...)* bitcast (i32 ()* @f to i32 (...)*), i32 (...)** %g, align 8
; CHECK: movq $__llvm_jump_instr_table_0_[[ENTRY:1|2|3]], (%rsp)
; CHECK: movl $__llvm_jump_instr_table_0_[[ENTRY]], %ecx
%1 = load i32 (...)** %g, align 8
%call = call i32 (...)* %1()
call void (void ()*)* @h(void ()* bitcast (void (void ()*)* @h to void ()*))
; CHECK: movl $__llvm_jump_instr_table_0_{{1|2|3}}, %edi
; CHECK: callq h

%a = call i32 (i32*)* bitcast (i32 (i8*)* @g to i32(i32*)*)(i32* null)
; CHECK: callq g
ret i32 %a
}

; CHECK: .globl __llvm_jump_instr_table_0_1
; CHECK: .align 8, 0x90
; CHECK: .type __llvm_jump_instr_table_0_1,@function
; CHECK: __llvm_jump_instr_table_0_1:
; CHECK: jmp {{f|g|h}}@PLT
; CHECK: .globl __llvm_jump_instr_table_0_2
; CHECK: .align 8, 0x90
; CHECK: .type __llvm_jump_instr_table_0_2,@function
; CHECK: __llvm_jump_instr_table_0_2:
; CHECK: jmp {{f|g|h}}@PLT
; CHECK: .globl __llvm_jump_instr_table_0_3
; CHECK: .align 8, 0x90
; CHECK: .type __llvm_jump_instr_table_0_3,@function
; CHECK: __llvm_jump_instr_table_0_3:
; CHECK: jmp {{f|g|h}}@PLT

272 changes: 272 additions & 0 deletions llvm/test/CodeGen/X86/jump_tables.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
; RUN: llc <%s -jump-table-type=single | FileCheck --check-prefix=SINGLE %s
; RUN: llc <%s -jump-table-type=arity | FileCheck --check-prefix=ARITY %s
; RUN: llc <%s -jump-table-type=simplified | FileCheck --check-prefix=SIMPL %s
; RUN: llc <%s -jump-table-type=full | FileCheck --check-prefix=FULL %s

target triple = "x86_64-unknown-linux-gnu"

%struct.fun_struct = type { i32 (...)* }

define void @indirect_fun() unnamed_addr jumptable {
ret void
}

define void @indirect_fun_match() unnamed_addr jumptable {
ret void
}

define i32 @indirect_fun_i32() unnamed_addr jumptable {
ret i32 0
}

define i32 @indirect_fun_i32_1(i32 %a) unnamed_addr jumptable {
ret i32 %a
}

define i32 @indirect_fun_i32_2(i32 %a, i32 %b) unnamed_addr jumptable {
ret i32 %a
}

define i32* @indirect_fun_i32S_2(i32* %a, i32 %b) unnamed_addr jumptable {
ret i32* %a
}

define void @indirect_fun_struct(%struct.fun_struct %fs) unnamed_addr jumptable {
ret void
}

define void @indirect_fun_fun(i32 (...)* %fun, i32 %a) unnamed_addr jumptable {
ret void
}

define i32 @indirect_fun_fun_ret(i32 (...)* %fun, i32 %a) unnamed_addr jumptable {
ret i32 %a
}

define void @indirect_fun_array([19 x i8] %a) unnamed_addr jumptable {
ret void
}

define void @indirect_fun_vec(<3 x i32> %a) unnamed_addr jumptable {
ret void
}

define void @indirect_fun_vec_2(<4 x float> %a) unnamed_addr jumptable {
ret void
}

define i32 @m(void ()* %fun) {
call void ()* %fun()
ret i32 0
}

define void ()* @get_fun() {
ret void ()* @indirect_fun
; SINGLE: movl $__llvm_jump_instr_table_0_
; ARITY: movl $__llvm_jump_instr_table_
; SIMPL: movl $__llvm_jump_instr_table_
; FULL: movl $__llvm_jump_instr_table_
}

define i32 @main(i32 %argc, i8** %argv) {
%f = call void ()* ()* @get_fun()
%a = call i32 @m(void ()* %f)
ret i32 %a
}

; SINGLE-DAG: .globl __llvm_jump_instr_table_0_1
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_1,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_1:
; SINGLE-DAG: jmp indirect_fun_array@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_2
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_2,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_2:
; SINGLE-DAG: jmp indirect_fun_i32_2@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_3
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_3,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_3:
; SINGLE-DAG: jmp indirect_fun_vec_2@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_4
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_4,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_4:
; SINGLE-DAG: jmp indirect_fun_i32S_2@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_5
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_5,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_5:
; SINGLE-DAG: jmp indirect_fun_struct@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_6
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_6,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_6:
; SINGLE-DAG: jmp indirect_fun_i32_1@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_7
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_7,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_7:
; SINGLE-DAG: jmp indirect_fun_i32@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_8
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_8,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_8:
; SINGLE-DAG: jmp indirect_fun_fun@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_9
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_9,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_9:
; SINGLE-DAG: jmp indirect_fun_fun_ret@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_10
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_10,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_10:
; SINGLE-DAG: jmp indirect_fun@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_11
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_11,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_11:
; SINGLE-DAG: jmp indirect_fun_match@PLT
; SINGLE-DAG: .globl __llvm_jump_instr_table_0_12
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: .type __llvm_jump_instr_table_0_12,@function
; SINGLE-DAG: __llvm_jump_instr_table_0_12:
; SINGLE-DAG: jmp indirect_fun_vec@PLT
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: ud2
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: ud2
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: ud2
; SINGLE-DAG: .align 8, 0x90
; SINGLE-DAG: ud2


; ARITY-DAG: .globl __llvm_jump_instr_table_2_1
; ARITY-DAG: .align 8, 0x90
; ARITY-DAG: .type __llvm_jump_instr_table_2_1,@function
; ARITY-DAG: __llvm_jump_instr_table_2_1:
; ARITY-DAG: jmp indirect_fun{{.*}}@PLT
; ARITY-DAG: .align 8, 0x90
; ARITY-DAG: ud2
; ARITY-DAG: .globl __llvm_jump_instr_table_0_1
; ARITY-DAG: .align 8, 0x90
; ARITY-DAG: .type __llvm_jump_instr_table_0_1,@function
; ARITY-DAG: __llvm_jump_instr_table_0_1:
; ARITY-DAG: jmp indirect_fun{{.*}}@PLT
; ARITY-DAG: .globl __llvm_jump_instr_table_1_1
; ARITY-DAG: .align 8, 0x90
; ARITY-DAG: .type __llvm_jump_instr_table_1_1,@function
; ARITY-DAG: __llvm_jump_instr_table_1_1:
; ARITY-DAG: jmp indirect_fun{{.*}}@PLT

; SIMPL-DAG: .globl __llvm_jump_instr_table_2_1
; SIMPL-DAG: .align 8, 0x90
; SIMPL-DAG: .type __llvm_jump_instr_table_2_1,@function
; SIMPL-DAG: __llvm_jump_instr_table_2_1:
; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT
; SIMPL-DAG: .align 8, 0x90
; SIMPL-DAG: ud2
; SIMPL-DAG: .globl __llvm_jump_instr_table_0_1
; SIMPL-DAG: .align 8, 0x90
; SIMPL-DAG: .type __llvm_jump_instr_table_0_1,@function
; SIMPL-DAG: __llvm_jump_instr_table_0_1:
; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT
; SIMPL-DAG: .globl __llvm_jump_instr_table_1_1
; SIMPL-DAG: .align 8, 0x90
; SIMPL-DAG: .type __llvm_jump_instr_table_1_1,@function
; SIMPL-DAG: __llvm_jump_instr_table_1_1:
; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT
; SIMPL-DAG: .globl __llvm_jump_instr_table_3_1
; SIMPL-DAG: .align 8, 0x90
; SIMPL-DAG: .type __llvm_jump_instr_table_3_1,@function
; SIMPL-DAG: __llvm_jump_instr_table_3_1:
; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT
; SIMPL-DAG: .globl __llvm_jump_instr_table_4_1
; SIMPL-DAG: .align 8, 0x90
; SIMPL-DAG: .type __llvm_jump_instr_table_4_1,@function
; SIMPL-DAG: __llvm_jump_instr_table_4_1:
; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT


; FULL-DAG: .globl __llvm_jump_instr_table_10_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_10_1,@function
; FULL-DAG:__llvm_jump_instr_table_10_1:
; FULL-DAG: jmp indirect_fun_i32_1@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_9_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_9_1,@function
; FULL-DAG:__llvm_jump_instr_table_9_1:
; FULL-DAG: jmp indirect_fun_i32_2@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_7_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_7_1,@function
; FULL-DAG:__llvm_jump_instr_table_7_1:
; FULL-DAG: jmp indirect_fun_i32S_2@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_3_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_3_1,@function
; FULL-DAG:__llvm_jump_instr_table_3_1:
; FULL-DAG: jmp indirect_fun_vec_2@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_2_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_2_1,@function
; FULL-DAG:__llvm_jump_instr_table_2_1:
; FULL-DAG: jmp indirect_fun@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_8_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_8_1,@function
; FULL-DAG:__llvm_jump_instr_table_8_1:
; FULL-DAG: jmp indirect_fun_i32@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_1_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_1_1,@function
; FULL-DAG:__llvm_jump_instr_table_1_1:
; FULL-DAG: jmp indirect_fun_array@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_0_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_0_1,@function
; FULL-DAG:__llvm_jump_instr_table_0_1:
; FULL-DAG: jmp indirect_fun_vec@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_6_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_6_1,@function
; FULL-DAG:__llvm_jump_instr_table_6_1:
; FULL-DAG: jmp indirect_fun_struct@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_5_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_5_1,@function
; FULL-DAG:__llvm_jump_instr_table_5_1:
; FULL-DAG: jmp indirect_fun_fun@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
; FULL-DAG: .globl __llvm_jump_instr_table_4_1
; FULL-DAG: .align 8, 0x90
; FULL-DAG: .type __llvm_jump_instr_table_4_1,@function
; FULL-DAG:__llvm_jump_instr_table_4_1:
; FULL-DAG: jmp indirect_fun_fun_ret@PLT
; FULL-DAG: .align 8, 0x90
; FULL-DAG: ud2
9 changes: 9 additions & 0 deletions llvm/test/Verifier/jumptable.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
; RUN: not llc <%s 2>&1 | FileCheck %s

define i32 @f() jumptable {
ret i32 0
}

; CHECK: Attribute 'jumptable' requires 'unnamed_addr'
; CHECK: i32 ()* @f
; CHECK: LLVM ERROR: Broken function found, compilation aborted!