blob: 6cda46a10839e960c26b38d25d998f1bbdb66865 [file] [log] [blame]
//===- bolt/Target/X86/X86MCPlusBuilder.cpp -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides X86-specific MCPlus builder.
//
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/X86BaseInfo.h"
#include "MCTargetDesc/X86InstrRelaxTables.h"
#include "MCTargetDesc/X86MCTargetDesc.h"
#include "X86MCSymbolizer.h"
#include "bolt/Core/MCPlus.h"
#include "bolt/Core/MCPlusBuilder.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegister.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include <set>
#define DEBUG_TYPE "mcplus"
using namespace llvm;
using namespace bolt;
namespace opts {
extern cl::OptionCategory BoltOptCategory;
static cl::opt<bool> X86StripRedundantAddressSize(
"x86-strip-redundant-address-size",
cl::desc("Remove redundant Address-Size override prefix"), cl::init(true),
cl::cat(BoltOptCategory));
} // namespace opts
namespace {
unsigned getShortBranchOpcode(unsigned Opcode) {
switch (Opcode) {
default:
return Opcode;
case X86::JMP_2: return X86::JMP_1;
case X86::JMP_4: return X86::JMP_1;
case X86::JCC_2: return X86::JCC_1;
case X86::JCC_4: return X86::JCC_1;
}
}
unsigned getShortArithOpcode(unsigned Opcode) {
return X86::getShortOpcodeArith(Opcode);
}
bool isMOVSX64rm32(const MCInst &Inst) {
return Inst.getOpcode() == X86::MOVSX64rm32;
}
bool isADD64rr(const MCInst &Inst) { return Inst.getOpcode() == X86::ADD64rr; }
bool isADDri(const MCInst &Inst) {
return Inst.getOpcode() == X86::ADD64ri32 ||
Inst.getOpcode() == X86::ADD64ri8;
}
#define GET_INSTRINFO_OPERAND_TYPES_ENUM
#define GET_INSTRINFO_OPERAND_TYPE
#define GET_INSTRINFO_MEM_OPERAND_SIZE
#include "X86GenInstrInfo.inc"
class X86MCPlusBuilder : public MCPlusBuilder {
public:
X86MCPlusBuilder(const MCInstrAnalysis *Analysis, const MCInstrInfo *Info,
const MCRegisterInfo *RegInfo)
: MCPlusBuilder(Analysis, Info, RegInfo) {}
std::unique_ptr<MCSymbolizer>
createTargetSymbolizer(BinaryFunction &Function) const override {
return std::make_unique<X86MCSymbolizer>(Function);
}
bool isBranch(const MCInst &Inst) const override {
return Analysis->isBranch(Inst) && !isTailCall(Inst);
}
bool isNoop(const MCInst &Inst) const override {
return X86::isNOP(Inst.getOpcode());
}
unsigned getCondCode(const MCInst &Inst) const override {
unsigned Opcode = Inst.getOpcode();
if (X86::isJCC(Opcode))
return Inst.getOperand(Info->get(Opcode).NumOperands - 1).getImm();
return X86::COND_INVALID;
}
unsigned getInvertedCondCode(unsigned CC) const override {
switch (CC) {
default: return X86::COND_INVALID;
case X86::COND_E: return X86::COND_NE;
case X86::COND_NE: return X86::COND_E;
case X86::COND_L: return X86::COND_GE;
case X86::COND_LE: return X86::COND_G;
case X86::COND_G: return X86::COND_LE;
case X86::COND_GE: return X86::COND_L;
case X86::COND_B: return X86::COND_AE;
case X86::COND_BE: return X86::COND_A;
case X86::COND_A: return X86::COND_BE;
case X86::COND_AE: return X86::COND_B;
case X86::COND_S: return X86::COND_NS;
case X86::COND_NS: return X86::COND_S;
case X86::COND_P: return X86::COND_NP;
case X86::COND_NP: return X86::COND_P;
case X86::COND_O: return X86::COND_NO;
case X86::COND_NO: return X86::COND_O;
}
}
unsigned getCondCodesLogicalOr(unsigned CC1, unsigned CC2) const override {
enum DecodedCondCode : uint8_t {
DCC_EQUAL = 0x1,
DCC_GREATER = 0x2,
DCC_LESSER = 0x4,
DCC_GREATER_OR_LESSER = 0x6,
DCC_UNSIGNED = 0x8,
DCC_SIGNED = 0x10,
DCC_INVALID = 0x20,
};
auto decodeCondCode = [&](unsigned CC) -> uint8_t {
switch (CC) {
default: return DCC_INVALID;
case X86::COND_E: return DCC_EQUAL;
case X86::COND_NE: return DCC_GREATER | DCC_LESSER;
case X86::COND_L: return DCC_LESSER | DCC_SIGNED;
case X86::COND_LE: return DCC_EQUAL | DCC_LESSER | DCC_SIGNED;
case X86::COND_G: return DCC_GREATER | DCC_SIGNED;
case X86::COND_GE: return DCC_GREATER | DCC_EQUAL | DCC_SIGNED;
case X86::COND_B: return DCC_LESSER | DCC_UNSIGNED;
case X86::COND_BE: return DCC_EQUAL | DCC_LESSER | DCC_UNSIGNED;
case X86::COND_A: return DCC_GREATER | DCC_UNSIGNED;
case X86::COND_AE: return DCC_GREATER | DCC_EQUAL | DCC_UNSIGNED;
}
};
uint8_t DCC = decodeCondCode(CC1) | decodeCondCode(CC2);
if (DCC & DCC_INVALID)
return X86::COND_INVALID;
if (DCC & DCC_SIGNED && DCC & DCC_UNSIGNED)
return X86::COND_INVALID;
switch (DCC) {
default: return X86::COND_INVALID;
case DCC_EQUAL | DCC_LESSER | DCC_SIGNED: return X86::COND_LE;
case DCC_EQUAL | DCC_LESSER | DCC_UNSIGNED: return X86::COND_BE;
case DCC_EQUAL | DCC_GREATER | DCC_SIGNED: return X86::COND_GE;
case DCC_EQUAL | DCC_GREATER | DCC_UNSIGNED: return X86::COND_AE;
case DCC_GREATER | DCC_LESSER | DCC_SIGNED: return X86::COND_NE;
case DCC_GREATER | DCC_LESSER | DCC_UNSIGNED: return X86::COND_NE;
case DCC_GREATER | DCC_LESSER: return X86::COND_NE;
case DCC_EQUAL | DCC_SIGNED: return X86::COND_E;
case DCC_EQUAL | DCC_UNSIGNED: return X86::COND_E;
case DCC_EQUAL: return X86::COND_E;
case DCC_LESSER | DCC_SIGNED: return X86::COND_L;
case DCC_LESSER | DCC_UNSIGNED: return X86::COND_B;
case DCC_GREATER | DCC_SIGNED: return X86::COND_G;
case DCC_GREATER | DCC_UNSIGNED: return X86::COND_A;
}
}
bool isValidCondCode(unsigned CC) const override {
return (CC != X86::COND_INVALID);
}
bool isBreakpoint(const MCInst &Inst) const override {
return Inst.getOpcode() == X86::INT3;
}
bool isPrefix(const MCInst &Inst) const override {
const MCInstrDesc &Desc = Info->get(Inst.getOpcode());
return X86II::isPrefix(Desc.TSFlags);
}
bool isRep(const MCInst &Inst) const override {
return Inst.getFlags() == X86::IP_HAS_REPEAT;
}
bool deleteREPPrefix(MCInst &Inst) const override {
if (Inst.getFlags() == X86::IP_HAS_REPEAT) {
Inst.setFlags(0);
return true;
}
return false;
}
// FIXME: For compatibility with old LLVM only!
bool isTerminator(const MCInst &Inst) const override {
unsigned Opcode = Inst.getOpcode();
return Info->get(Opcode).isTerminator() || X86::isUD1(Opcode) ||
X86::isUD2(Opcode);
}
bool isIndirectCall(const MCInst &Inst) const override {
return isCall(Inst) &&
((getMemoryOperandNo(Inst) != -1) || Inst.getOperand(0).isReg());
}
bool isPop(const MCInst &Inst) const override {
return getPopSize(Inst) == 0 ? false : true;
}
bool isTerminateBranch(const MCInst &Inst) const override {
return Inst.getOpcode() == X86::ENDBR32 || Inst.getOpcode() == X86::ENDBR64;
}
int getPopSize(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
case X86::POP16r:
case X86::POP16rmm:
case X86::POP16rmr:
case X86::POPF16:
case X86::POPA16:
case X86::POPDS16:
case X86::POPES16:
case X86::POPFS16:
case X86::POPGS16:
case X86::POPSS16:
return 2;
case X86::POP32r:
case X86::POP32rmm:
case X86::POP32rmr:
case X86::POPA32:
case X86::POPDS32:
case X86::POPES32:
case X86::POPF32:
case X86::POPFS32:
case X86::POPGS32:
case X86::POPSS32:
return 4;
case X86::POP64r:
case X86::POP64rmm:
case X86::POP64rmr:
case X86::POPF64:
case X86::POPFS64:
case X86::POPGS64:
return 8;
}
return 0;
}
bool isPush(const MCInst &Inst) const override {
return getPushSize(Inst) == 0 ? false : true;
}
int getPushSize(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
case X86::PUSH16i8:
case X86::PUSH16r:
case X86::PUSH16rmm:
case X86::PUSH16rmr:
case X86::PUSHA16:
case X86::PUSHCS16:
case X86::PUSHDS16:
case X86::PUSHES16:
case X86::PUSHF16:
case X86::PUSHFS16:
case X86::PUSHGS16:
case X86::PUSHSS16:
case X86::PUSHi16:
return 2;
case X86::PUSH32i8:
case X86::PUSH32r:
case X86::PUSH32rmm:
case X86::PUSH32rmr:
case X86::PUSHA32:
case X86::PUSHCS32:
case X86::PUSHDS32:
case X86::PUSHES32:
case X86::PUSHF32:
case X86::PUSHFS32:
case X86::PUSHGS32:
case X86::PUSHSS32:
case X86::PUSHi32:
return 4;
case X86::PUSH64i32:
case X86::PUSH64i8:
case X86::PUSH64r:
case X86::PUSH64rmm:
case X86::PUSH64rmr:
case X86::PUSHF64:
case X86::PUSHFS64:
case X86::PUSHGS64:
return 8;
}
return 0;
}
bool isSUB(const MCInst &Inst) const override {
return X86::isSUB(Inst.getOpcode());
}
bool isLEA64r(const MCInst &Inst) const override {
return Inst.getOpcode() == X86::LEA64r;
}
bool isLeave(const MCInst &Inst) const override {
return Inst.getOpcode() == X86::LEAVE || Inst.getOpcode() == X86::LEAVE64;
}
bool isMoveMem2Reg(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
case X86::MOV16rm:
case X86::MOV32rm:
case X86::MOV64rm:
return true;
}
return false;
}
bool isUnsupportedBranch(unsigned Opcode) const override {
switch (Opcode) {
default:
return false;
case X86::LOOP:
case X86::LOOPE:
case X86::LOOPNE:
case X86::JECXZ:
case X86::JRCXZ:
return true;
}
}
bool isLoad(const MCInst &Inst) const override {
if (isPop(Inst))
return true;
int MemOpNo = getMemoryOperandNo(Inst);
const MCInstrDesc &MCII = Info->get(Inst.getOpcode());
if (MemOpNo == -1)
return false;
return MCII.mayLoad();
}
bool isStore(const MCInst &Inst) const override {
if (isPush(Inst))
return true;
int MemOpNo = getMemoryOperandNo(Inst);
const MCInstrDesc &MCII = Info->get(Inst.getOpcode());
if (MemOpNo == -1)
return false;
return MCII.mayStore();
}
bool isCleanRegXOR(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
case X86::XOR16rr:
case X86::XOR32rr:
case X86::XOR64rr:
break;
default:
return false;
}
return (Inst.getOperand(0).getReg() == Inst.getOperand(2).getReg());
}
bool isPacked(const MCInst &Inst) const override {
const MCInstrDesc &Desc = Info->get(Inst.getOpcode());
return (Desc.TSFlags & X86II::OpPrefixMask) == X86II::PD;
}
unsigned getTrapFillValue() const override { return 0xCC; }
struct IndJmpMatcherFrag1 : MCInstMatcher {
std::unique_ptr<MCInstMatcher> Base;
std::unique_ptr<MCInstMatcher> Scale;
std::unique_ptr<MCInstMatcher> Index;
std::unique_ptr<MCInstMatcher> Offset;
IndJmpMatcherFrag1(std::unique_ptr<MCInstMatcher> Base,
std::unique_ptr<MCInstMatcher> Scale,
std::unique_ptr<MCInstMatcher> Index,
std::unique_ptr<MCInstMatcher> Offset)
: Base(std::move(Base)), Scale(std::move(Scale)),
Index(std::move(Index)), Offset(std::move(Offset)) {}
bool match(const MCRegisterInfo &MRI, MCPlusBuilder &MIB,
MutableArrayRef<MCInst> InInstrWindow, int OpNum) override {
if (!MCInstMatcher::match(MRI, MIB, InInstrWindow, OpNum))
return false;
if (CurInst->getOpcode() != X86::JMP64m)
return false;
int MemOpNo = MIB.getMemoryOperandNo(*CurInst);
if (MemOpNo == -1)
return false;
if (!Base->match(MRI, MIB, this->InstrWindow, MemOpNo + X86::AddrBaseReg))
return false;
if (!Scale->match(MRI, MIB, this->InstrWindow,
MemOpNo + X86::AddrScaleAmt))
return false;
if (!Index->match(MRI, MIB, this->InstrWindow,
MemOpNo + X86::AddrIndexReg))
return false;
if (!Offset->match(MRI, MIB, this->InstrWindow, MemOpNo + X86::AddrDisp))
return false;
return true;
}
void annotate(MCPlusBuilder &MIB, StringRef Annotation) override {
MIB.addAnnotation(*CurInst, Annotation, true);
Base->annotate(MIB, Annotation);
Scale->annotate(MIB, Annotation);
Index->annotate(MIB, Annotation);
Offset->annotate(MIB, Annotation);
}
};
std::unique_ptr<MCInstMatcher>
matchIndJmp(std::unique_ptr<MCInstMatcher> Base,
std::unique_ptr<MCInstMatcher> Scale,
std::unique_ptr<MCInstMatcher> Index,
std::unique_ptr<MCInstMatcher> Offset) const override {
return std::unique_ptr<MCInstMatcher>(
new IndJmpMatcherFrag1(std::move(Base), std::move(Scale),
std::move(Index), std::move(Offset)));
}
struct IndJmpMatcherFrag2 : MCInstMatcher {
std::unique_ptr<MCInstMatcher> Reg;
IndJmpMatcherFrag2(std::unique_ptr<MCInstMatcher> Reg)
: Reg(std::move(Reg)) {}
bool match(const MCRegisterInfo &MRI, MCPlusBuilder &MIB,
MutableArrayRef<MCInst> InInstrWindow, int OpNum) override {
if (!MCInstMatcher::match(MRI, MIB, InInstrWindow, OpNum))
return false;
if (CurInst->getOpcode() != X86::JMP64r)
return false;
return Reg->match(MRI, MIB, this->InstrWindow, 0);
}
void annotate(MCPlusBuilder &MIB, StringRef Annotation) override {
MIB.addAnnotation(*CurInst, Annotation, true);
Reg->annotate(MIB, Annotation);
}
};
std::unique_ptr<MCInstMatcher>
matchIndJmp(std::unique_ptr<MCInstMatcher> Target) const override {
return std::unique_ptr<MCInstMatcher>(
new IndJmpMatcherFrag2(std::move(Target)));
}
struct LoadMatcherFrag1 : MCInstMatcher {
std::unique_ptr<MCInstMatcher> Base;
std::unique_ptr<MCInstMatcher> Scale;
std::unique_ptr<MCInstMatcher> Index;
std::unique_ptr<MCInstMatcher> Offset;
LoadMatcherFrag1(std::unique_ptr<MCInstMatcher> Base,
std::unique_ptr<MCInstMatcher> Scale,
std::unique_ptr<MCInstMatcher> Index,
std::unique_ptr<MCInstMatcher> Offset)
: Base(std::move(Base)), Scale(std::move(Scale)),
Index(std::move(Index)), Offset(std::move(Offset)) {}
bool match(const MCRegisterInfo &MRI, MCPlusBuilder &MIB,
MutableArrayRef<MCInst> InInstrWindow, int OpNum) override {
if (!MCInstMatcher::match(MRI, MIB, InInstrWindow, OpNum))
return false;
if (CurInst->getOpcode() != X86::MOV64rm &&
CurInst->getOpcode() != X86::MOVSX64rm32)
return false;
int MemOpNo = MIB.getMemoryOperandNo(*CurInst);
if (MemOpNo == -1)
return false;
if (!Base->match(MRI, MIB, this->InstrWindow, MemOpNo + X86::AddrBaseReg))
return false;
if (!Scale->match(MRI, MIB, this->InstrWindow,
MemOpNo + X86::AddrScaleAmt))
return false;
if (!Index->match(MRI, MIB, this->InstrWindow,
MemOpNo + X86::AddrIndexReg))
return false;
if (!Offset->match(MRI, MIB, this->InstrWindow, MemOpNo + X86::AddrDisp))
return false;
return true;
}
void annotate(MCPlusBuilder &MIB, StringRef Annotation) override {
MIB.addAnnotation(*CurInst, Annotation, true);
Base->annotate(MIB, Annotation);
Scale->annotate(MIB, Annotation);
Index->annotate(MIB, Annotation);
Offset->annotate(MIB, Annotation);
}
};
std::unique_ptr<MCInstMatcher>
matchLoad(std::unique_ptr<MCInstMatcher> Base,
std::unique_ptr<MCInstMatcher> Scale,
std::unique_ptr<MCInstMatcher> Index,
std::unique_ptr<MCInstMatcher> Offset) const override {
return std::unique_ptr<MCInstMatcher>(
new LoadMatcherFrag1(std::move(Base), std::move(Scale),
std::move(Index), std::move(Offset)));
}
struct AddMatcher : MCInstMatcher {
std::unique_ptr<MCInstMatcher> A;
std::unique_ptr<MCInstMatcher> B;
AddMatcher(std::unique_ptr<MCInstMatcher> A,
std::unique_ptr<MCInstMatcher> B)
: A(std::move(A)), B(std::move(B)) {}
bool match(const MCRegisterInfo &MRI, MCPlusBuilder &MIB,
MutableArrayRef<MCInst> InInstrWindow, int OpNum) override {
if (!MCInstMatcher::match(MRI, MIB, InInstrWindow, OpNum))
return false;
if (CurInst->getOpcode() == X86::ADD64rr ||
CurInst->getOpcode() == X86::ADD64rr_DB ||
CurInst->getOpcode() == X86::ADD64rr_REV) {
if (!A->match(MRI, MIB, this->InstrWindow, 1)) {
if (!B->match(MRI, MIB, this->InstrWindow, 1))
return false;
return A->match(MRI, MIB, this->InstrWindow, 2);
}
if (B->match(MRI, MIB, this->InstrWindow, 2))
return true;
if (!B->match(MRI, MIB, this->InstrWindow, 1))
return false;
return A->match(MRI, MIB, this->InstrWindow, 2);
}
return false;
}
void annotate(MCPlusBuilder &MIB, StringRef Annotation) override {
MIB.addAnnotation(*CurInst, Annotation, true);
A->annotate(MIB, Annotation);
B->annotate(MIB, Annotation);
}
};
std::unique_ptr<MCInstMatcher>
matchAdd(std::unique_ptr<MCInstMatcher> A,
std::unique_ptr<MCInstMatcher> B) const override {
return std::unique_ptr<MCInstMatcher>(
new AddMatcher(std::move(A), std::move(B)));
}
struct LEAMatcher : MCInstMatcher {
std::unique_ptr<MCInstMatcher> Target;
LEAMatcher(std::unique_ptr<MCInstMatcher> Target)
: Target(std::move(Target)) {}
bool match(const MCRegisterInfo &MRI, MCPlusBuilder &MIB,
MutableArrayRef<MCInst> InInstrWindow, int OpNum) override {
if (!MCInstMatcher::match(MRI, MIB, InInstrWindow, OpNum))
return false;
if (CurInst->getOpcode() != X86::LEA64r)
return false;
if (CurInst->getOperand(1 + X86::AddrScaleAmt).getImm() != 1 ||
CurInst->getOperand(1 + X86::AddrIndexReg).getReg() !=
X86::NoRegister ||
(CurInst->getOperand(1 + X86::AddrBaseReg).getReg() !=
X86::NoRegister &&
CurInst->getOperand(1 + X86::AddrBaseReg).getReg() != X86::RIP))
return false;
return Target->match(MRI, MIB, this->InstrWindow, 1 + X86::AddrDisp);
}
void annotate(MCPlusBuilder &MIB, StringRef Annotation) override {
MIB.addAnnotation(*CurInst, Annotation, true);
Target->annotate(MIB, Annotation);
}
};
std::unique_ptr<MCInstMatcher>
matchLoadAddr(std::unique_ptr<MCInstMatcher> Target) const override {
return std::unique_ptr<MCInstMatcher>(new LEAMatcher(std::move(Target)));
}
bool hasPCRelOperand(const MCInst &Inst) const override {
for (const MCOperand &Operand : Inst)
if (Operand.isReg() && Operand.getReg() == X86::RIP)
return true;
return false;
}
int getMemoryOperandNo(const MCInst &Inst) const override {
unsigned Opcode = Inst.getOpcode();
const MCInstrDesc &Desc = Info->get(Opcode);
int MemOpNo = X86II::getMemoryOperandNo(Desc.TSFlags);
if (MemOpNo >= 0)
MemOpNo += X86II::getOperandBias(Desc);
return MemOpNo;
}
bool hasEVEXEncoding(const MCInst &Inst) const override {
const MCInstrDesc &Desc = Info->get(Inst.getOpcode());
return (Desc.TSFlags & X86II::EncodingMask) == X86II::EVEX;
}
bool isMacroOpFusionPair(ArrayRef<MCInst> Insts) const override {
const auto *I = Insts.begin();
while (I != Insts.end() && isPrefix(*I))
++I;
if (I == Insts.end())
return false;
const MCInst &FirstInst = *I;
++I;
while (I != Insts.end() && isPrefix(*I))
++I;
if (I == Insts.end())
return false;
const MCInst &SecondInst = *I;
if (!isConditionalBranch(SecondInst))
return false;
// Cannot fuse if the first instruction uses RIP-relative memory.
if (hasPCRelOperand(FirstInst))
return false;
const X86::FirstMacroFusionInstKind CmpKind =
X86::classifyFirstOpcodeInMacroFusion(FirstInst.getOpcode());
if (CmpKind == X86::FirstMacroFusionInstKind::Invalid)
return false;
X86::CondCode CC = static_cast<X86::CondCode>(getCondCode(SecondInst));
X86::SecondMacroFusionInstKind BranchKind =
X86::classifySecondCondCodeInMacroFusion(CC);
if (BranchKind == X86::SecondMacroFusionInstKind::Invalid)
return false;
return X86::isMacroFused(CmpKind, BranchKind);
}
bool
evaluateX86MemoryOperand(const MCInst &Inst, unsigned *BaseRegNum,
int64_t *ScaleImm, unsigned *IndexRegNum,
int64_t *DispImm, unsigned *SegmentRegNum,
const MCExpr **DispExpr = nullptr) const override {
assert(BaseRegNum && ScaleImm && IndexRegNum && SegmentRegNum &&
"one of the input pointers is null");
int MemOpNo = getMemoryOperandNo(Inst);
if (MemOpNo < 0)
return false;
unsigned MemOpOffset = static_cast<unsigned>(MemOpNo);
if (MemOpOffset + X86::AddrSegmentReg >= MCPlus::getNumPrimeOperands(Inst))
return false;
const MCOperand &Base = Inst.getOperand(MemOpOffset + X86::AddrBaseReg);
const MCOperand &Scale = Inst.getOperand(MemOpOffset + X86::AddrScaleAmt);
const MCOperand &Index = Inst.getOperand(MemOpOffset + X86::AddrIndexReg);
const MCOperand &Disp = Inst.getOperand(MemOpOffset + X86::AddrDisp);
const MCOperand &Segment =
Inst.getOperand(MemOpOffset + X86::AddrSegmentReg);
// Make sure it is a well-formed memory operand.
if (!Base.isReg() || !Scale.isImm() || !Index.isReg() ||
(!Disp.isImm() && !Disp.isExpr()) || !Segment.isReg())
return false;
*BaseRegNum = Base.getReg();
*ScaleImm = Scale.getImm();
*IndexRegNum = Index.getReg();
if (Disp.isImm()) {
assert(DispImm && "DispImm needs to be set");
*DispImm = Disp.getImm();
if (DispExpr)
*DispExpr = nullptr;
} else {
assert(DispExpr && "DispExpr needs to be set");
*DispExpr = Disp.getExpr();
if (DispImm)
*DispImm = 0;
}
*SegmentRegNum = Segment.getReg();
return true;
}
bool evaluateMemOperandTarget(const MCInst &Inst, uint64_t &Target,
uint64_t Address,
uint64_t Size) const override {
unsigned BaseRegNum;
int64_t ScaleValue;
unsigned IndexRegNum;
int64_t DispValue;
unsigned SegRegNum;
const MCExpr *DispExpr = nullptr;
if (!evaluateX86MemoryOperand(Inst, &BaseRegNum, &ScaleValue, &IndexRegNum,
&DispValue, &SegRegNum, &DispExpr))
return false;
// Make sure it's a well-formed addressing we can statically evaluate.
if ((BaseRegNum != X86::RIP && BaseRegNum != X86::NoRegister) ||
IndexRegNum != X86::NoRegister || SegRegNum != X86::NoRegister ||
DispExpr)
return false;
Target = DispValue;
if (BaseRegNum == X86::RIP) {
assert(Size != 0 && "instruction size required in order to statically "
"evaluate RIP-relative address");
Target += Address + Size;
}
return true;
}
MCInst::iterator getMemOperandDisp(MCInst &Inst) const override {
int MemOpNo = getMemoryOperandNo(Inst);
if (MemOpNo < 0)
return Inst.end();
return Inst.begin() + (MemOpNo + X86::AddrDisp);
}
bool replaceMemOperandDisp(MCInst &Inst, MCOperand Operand) const override {
MCOperand *OI = getMemOperandDisp(Inst);
if (OI == Inst.end())
return false;
*OI = Operand;
return true;
}
/// Get the registers used as function parameters.
/// This function is specific to the x86_64 abi on Linux.
BitVector getRegsUsedAsParams() const override {
BitVector Regs = BitVector(RegInfo->getNumRegs(), false);
Regs |= getAliases(X86::RSI);
Regs |= getAliases(X86::RDI);
Regs |= getAliases(X86::RDX);
Regs |= getAliases(X86::RCX);
Regs |= getAliases(X86::R8);
Regs |= getAliases(X86::R9);
return Regs;
}
void getCalleeSavedRegs(BitVector &Regs) const override {
Regs |= getAliases(X86::RBX);
Regs |= getAliases(X86::RBP);
Regs |= getAliases(X86::R12);
Regs |= getAliases(X86::R13);
Regs |= getAliases(X86::R14);
Regs |= getAliases(X86::R15);
}
void getDefaultDefIn(BitVector &Regs) const override {
assert(Regs.size() >= RegInfo->getNumRegs() &&
"The size of BitVector is less than RegInfo->getNumRegs().");
Regs.set(X86::RAX);
Regs.set(X86::RCX);
Regs.set(X86::RDX);
Regs.set(X86::RSI);
Regs.set(X86::RDI);
Regs.set(X86::R8);
Regs.set(X86::R9);
Regs.set(X86::XMM0);
Regs.set(X86::XMM1);
Regs.set(X86::XMM2);
Regs.set(X86::XMM3);
Regs.set(X86::XMM4);
Regs.set(X86::XMM5);
Regs.set(X86::XMM6);
Regs.set(X86::XMM7);
}
void getDefaultLiveOut(BitVector &Regs) const override {
assert(Regs.size() >= RegInfo->getNumRegs() &&
"The size of BitVector is less than RegInfo->getNumRegs().");
Regs |= getAliases(X86::RAX);
Regs |= getAliases(X86::RDX);
Regs |= getAliases(X86::RCX);
Regs |= getAliases(X86::XMM0);
Regs |= getAliases(X86::XMM1);
}
void getGPRegs(BitVector &Regs, bool IncludeAlias) const override {
if (IncludeAlias) {
Regs |= getAliases(X86::RAX);
Regs |= getAliases(X86::RBX);
Regs |= getAliases(X86::RBP);
Regs |= getAliases(X86::RSI);
Regs |= getAliases(X86::RDI);
Regs |= getAliases(X86::RDX);
Regs |= getAliases(X86::RCX);
Regs |= getAliases(X86::R8);
Regs |= getAliases(X86::R9);
Regs |= getAliases(X86::R10);
Regs |= getAliases(X86::R11);
Regs |= getAliases(X86::R12);
Regs |= getAliases(X86::R13);
Regs |= getAliases(X86::R14);
Regs |= getAliases(X86::R15);
return;
}
Regs.set(X86::RAX);
Regs.set(X86::RBX);
Regs.set(X86::RBP);
Regs.set(X86::RSI);
Regs.set(X86::RDI);
Regs.set(X86::RDX);
Regs.set(X86::RCX);
Regs.set(X86::R8);
Regs.set(X86::R9);
Regs.set(X86::R10);
Regs.set(X86::R11);
Regs.set(X86::R12);
Regs.set(X86::R13);
Regs.set(X86::R14);
Regs.set(X86::R15);
}
void getClassicGPRegs(BitVector &Regs) const override {
Regs |= getAliases(X86::RAX);
Regs |= getAliases(X86::RBX);
Regs |= getAliases(X86::RBP);
Regs |= getAliases(X86::RSI);
Regs |= getAliases(X86::RDI);
Regs |= getAliases(X86::RDX);
Regs |= getAliases(X86::RCX);
}
void getRepRegs(BitVector &Regs) const override {
Regs |= getAliases(X86::RCX);
}
MCPhysReg getAliasSized(MCPhysReg Reg, uint8_t Size) const override {
switch (Reg) {
case X86::RAX: case X86::EAX: case X86::AX: case X86::AL: case X86::AH:
switch (Size) {
case 8: return X86::RAX; case 4: return X86::EAX;
case 2: return X86::AX; case 1: return X86::AL;
default: llvm_unreachable("Unexpected size");
}
case X86::RBX: case X86::EBX: case X86::BX: case X86::BL: case X86::BH:
switch (Size) {
case 8: return X86::RBX; case 4: return X86::EBX;
case 2: return X86::BX; case 1: return X86::BL;
default: llvm_unreachable("Unexpected size");
}
case X86::RDX: case X86::EDX: case X86::DX: case X86::DL: case X86::DH:
switch (Size) {
case 8: return X86::RDX; case 4: return X86::EDX;
case 2: return X86::DX; case 1: return X86::DL;
default: llvm_unreachable("Unexpected size");
}
case X86::RDI: case X86::EDI: case X86::DI: case X86::DIL:
switch (Size) {
case 8: return X86::RDI; case 4: return X86::EDI;
case 2: return X86::DI; case 1: return X86::DIL;
default: llvm_unreachable("Unexpected size");
}
case X86::RSI: case X86::ESI: case X86::SI: case X86::SIL:
switch (Size) {
case 8: return X86::RSI; case 4: return X86::ESI;
case 2: return X86::SI; case 1: return X86::SIL;
default: llvm_unreachable("Unexpected size");
}
case X86::RCX: case X86::ECX: case X86::CX: case X86::CL: case X86::CH:
switch (Size) {
case 8: return X86::RCX; case 4: return X86::ECX;
case 2: return X86::CX; case 1: return X86::CL;
default: llvm_unreachable("Unexpected size");
}
case X86::RSP: case X86::ESP: case X86::SP: case X86::SPL:
switch (Size) {
case 8: return X86::RSP; case 4: return X86::ESP;
case 2: return X86::SP; case 1: return X86::SPL;
default: llvm_unreachable("Unexpected size");
}
case X86::RBP: case X86::EBP: case X86::BP: case X86::BPL:
switch (Size) {
case 8: return X86::RBP; case 4: return X86::EBP;
case 2: return X86::BP; case 1: return X86::BPL;
default: llvm_unreachable("Unexpected size");
}
case X86::R8: case X86::R8D: case X86::R8W: case X86::R8B:
switch (Size) {
case 8: return X86::R8; case 4: return X86::R8D;
case 2: return X86::R8W; case 1: return X86::R8B;
default: llvm_unreachable("Unexpected size");
}
case X86::R9: case X86::R9D: case X86::R9W: case X86::R9B:
switch (Size) {
case 8: return X86::R9; case 4: return X86::R9D;
case 2: return X86::R9W; case 1: return X86::R9B;
default: llvm_unreachable("Unexpected size");
}
case X86::R10: case X86::R10D: case X86::R10W: case X86::R10B:
switch (Size) {
case 8: return X86::R10; case 4: return X86::R10D;
case 2: return X86::R10W; case 1: return X86::R10B;
default: llvm_unreachable("Unexpected size");
}
case X86::R11: case X86::R11D: case X86::R11W: case X86::R11B:
switch (Size) {
case 8: return X86::R11; case 4: return X86::R11D;
case 2: return X86::R11W; case 1: return X86::R11B;
default: llvm_unreachable("Unexpected size");
}
case X86::R12: case X86::R12D: case X86::R12W: case X86::R12B:
switch (Size) {
case 8: return X86::R12; case 4: return X86::R12D;
case 2: return X86::R12W; case 1: return X86::R12B;
default: llvm_unreachable("Unexpected size");
}
case X86::R13: case X86::R13D: case X86::R13W: case X86::R13B:
switch (Size) {
case 8: return X86::R13; case 4: return X86::R13D;
case 2: return X86::R13W; case 1: return X86::R13B;
default: llvm_unreachable("Unexpected size");
}
case X86::R14: case X86::R14D: case X86::R14W: case X86::R14B:
switch (Size) {
case 8: return X86::R14; case 4: return X86::R14D;
case 2: return X86::R14W; case 1: return X86::R14B;
default: llvm_unreachable("Unexpected size");
}
case X86::R15: case X86::R15D: case X86::R15W: case X86::R15B:
switch (Size) {
case 8: return X86::R15; case 4: return X86::R15D;
case 2: return X86::R15W; case 1: return X86::R15B;
default: llvm_unreachable("Unexpected size");
}
default:
dbgs() << Reg << " (get alias sized)\n";
llvm_unreachable("Unexpected reg number");
break;
}
}
bool isUpper8BitReg(MCPhysReg Reg) const override {
switch (Reg) {
case X86::AH:
case X86::BH:
case X86::CH:
case X86::DH:
return true;
default:
return false;
}
}
bool cannotUseREX(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
case X86::MOV8mr_NOREX:
case X86::MOV8rm_NOREX:
case X86::MOV8rr_NOREX:
case X86::MOVSX32rm8_NOREX:
case X86::MOVSX32rr8_NOREX:
case X86::MOVZX32rm8_NOREX:
case X86::MOVZX32rr8_NOREX:
case X86::MOV8mr:
case X86::MOV8rm:
case X86::MOV8rr:
case X86::MOVSX32rm8:
case X86::MOVSX32rr8:
case X86::MOVZX32rm8:
case X86::MOVZX32rr8:
case X86::TEST8ri:
for (const MCOperand &Operand : MCPlus::primeOperands(Inst)) {
if (!Operand.isReg())
continue;
if (isUpper8BitReg(Operand.getReg()))
return true;
}
[[fallthrough]];
default:
return false;
}
}
static uint8_t getMemDataSize(const MCInst &Inst, int MemOpNo) {
using namespace llvm::X86;
int OpType = getOperandType(Inst.getOpcode(), MemOpNo);
return getMemOperandSize(OpType) / 8;
}
/// Classifying a stack access as *not* "SIMPLE" here means we don't know how
/// to change this instruction memory access. It will disable any changes to
/// the stack layout, so we can't do the most aggressive form of shrink
/// wrapping. We must do so in a way that keeps the original stack layout.
/// Otherwise you need to adjust the offset of all instructions accessing the
/// stack: we can't do that anymore because there is one instruction that is
/// not simple. There are other implications as well. We have heuristics to
/// detect when a register is callee-saved and thus eligible for shrink
/// wrapping. If you are restoring a register using a non-simple stack access,
/// then it is classified as NOT callee-saved, and it disables shrink wrapping
/// for *that* register (but not for others).
///
/// Classifying a stack access as "size 0" or detecting an indexed memory
/// access (to address a vector, for example) here means we know there is a
/// stack access, but we can't quite understand how wide is the access in
/// bytes. This is very serious because we can't understand how memory
/// accesses alias with each other for this function. This will essentially
/// disable not only shrink wrapping but all frame analysis, it will fail it
/// as "we don't understand this function and we give up on it".
bool isStackAccess(const MCInst &Inst, bool &IsLoad, bool &IsStore,
bool &IsStoreFromReg, MCPhysReg &Reg, int32_t &SrcImm,
uint16_t &StackPtrReg, int64_t &StackOffset, uint8_t &Size,
bool &IsSimple, bool &IsIndexed) const override {
// Detect simple push/pop cases first
if (int Sz = getPushSize(Inst)) {
IsLoad = false;
IsStore = true;
IsStoreFromReg = true;
StackPtrReg = X86::RSP;
StackOffset = -Sz;
Size = Sz;
IsSimple = true;
if (Inst.getOperand(0).isImm())
SrcImm = Inst.getOperand(0).getImm();
else if (Inst.getOperand(0).isReg())
Reg = Inst.getOperand(0).getReg();
else
IsSimple = false;
return true;
}
if (int Sz = getPopSize(Inst)) {
IsLoad = true;
IsStore = false;
if (Inst.getNumOperands() == 0 || !Inst.getOperand(0).isReg()) {
IsSimple = false;
} else {
Reg = Inst.getOperand(0).getReg();
IsSimple = true;
}
StackPtrReg = X86::RSP;
StackOffset = 0;
Size = Sz;
return true;
}
struct InstInfo {
// Size in bytes that Inst loads from memory.
uint8_t DataSize;
bool IsLoad;
bool IsStore;
bool StoreFromReg;
bool Simple;
};
InstInfo I;
int MemOpNo = getMemoryOperandNo(Inst);
const MCInstrDesc &MCII = Info->get(Inst.getOpcode());
// If it is not dealing with a memory operand, we discard it
if (MemOpNo == -1 || MCII.isCall())
return false;
switch (Inst.getOpcode()) {
default: {
bool IsLoad = MCII.mayLoad();
bool IsStore = MCII.mayStore();
// Is it LEA? (deals with memory but is not loading nor storing)
if (!IsLoad && !IsStore) {
I = {0, IsLoad, IsStore, false, false};
break;
}
uint8_t Sz = getMemDataSize(Inst, MemOpNo);
I = {Sz, IsLoad, IsStore, false, false};
break;
}
// Report simple stack accesses
case X86::MOV8rm: I = {1, true, false, false, true}; break;
case X86::MOV16rm: I = {2, true, false, false, true}; break;
case X86::MOV32rm: I = {4, true, false, false, true}; break;
case X86::MOV64rm: I = {8, true, false, false, true}; break;
case X86::MOV8mr: I = {1, false, true, true, true}; break;
case X86::MOV16mr: I = {2, false, true, true, true}; break;
case X86::MOV32mr: I = {4, false, true, true, true}; break;
case X86::MOV64mr: I = {8, false, true, true, true}; break;
case X86::MOV8mi: I = {1, false, true, false, true}; break;
case X86::MOV16mi: I = {2, false, true, false, true}; break;
case X86::MOV32mi: I = {4, false, true, false, true}; break;
} // end switch (Inst.getOpcode())
unsigned BaseRegNum;
int64_t ScaleValue;
unsigned IndexRegNum;
int64_t DispValue;
unsigned SegRegNum;
const MCExpr *DispExpr;
if (!evaluateX86MemoryOperand(Inst, &BaseRegNum, &ScaleValue, &IndexRegNum,
&DispValue, &SegRegNum, &DispExpr)) {
LLVM_DEBUG(dbgs() << "Evaluate failed on ");
LLVM_DEBUG(Inst.dump());
return false;
}
// Make sure it's a stack access
if (BaseRegNum != X86::RBP && BaseRegNum != X86::RSP)
return false;
IsLoad = I.IsLoad;
IsStore = I.IsStore;
IsStoreFromReg = I.StoreFromReg;
Size = I.DataSize;
IsSimple = I.Simple;
StackPtrReg = BaseRegNum;
StackOffset = DispValue;
IsIndexed = IndexRegNum != X86::NoRegister || SegRegNum != X86::NoRegister;
if (!I.Simple)
return true;
// Retrieve related register in simple MOV from/to stack operations.
unsigned MemOpOffset = static_cast<unsigned>(MemOpNo);
if (I.IsLoad) {
MCOperand RegOpnd = Inst.getOperand(0);
assert(RegOpnd.isReg() && "unexpected destination operand");
Reg = RegOpnd.getReg();
} else if (I.IsStore) {
MCOperand SrcOpnd =
Inst.getOperand(MemOpOffset + X86::AddrSegmentReg + 1);
if (I.StoreFromReg) {
assert(SrcOpnd.isReg() && "unexpected source operand");
Reg = SrcOpnd.getReg();
} else {
assert(SrcOpnd.isImm() && "unexpected source operand");
SrcImm = SrcOpnd.getImm();
}
}
return true;
}
void changeToPushOrPop(MCInst &Inst) const override {
assert(!isPush(Inst) && !isPop(Inst));
struct InstInfo {
// Size in bytes that Inst loads from memory.
uint8_t DataSize;
bool IsLoad;
bool StoreFromReg;
};
InstInfo I;
switch (Inst.getOpcode()) {
default: {
llvm_unreachable("Unhandled opcode");
return;
}
case X86::MOV16rm: I = {2, true, false}; break;
case X86::MOV32rm: I = {4, true, false}; break;
case X86::MOV64rm: I = {8, true, false}; break;
case X86::MOV16mr: I = {2, false, true}; break;
case X86::MOV32mr: I = {4, false, true}; break;
case X86::MOV64mr: I = {8, false, true}; break;
case X86::MOV16mi: I = {2, false, false}; break;
case X86::MOV32mi: I = {4, false, false}; break;
} // end switch (Inst.getOpcode())
unsigned BaseRegNum;
int64_t ScaleValue;
unsigned IndexRegNum;
int64_t DispValue;
unsigned SegRegNum;
const MCExpr *DispExpr;
if (!evaluateX86MemoryOperand(Inst, &BaseRegNum, &ScaleValue, &IndexRegNum,
&DispValue, &SegRegNum, &DispExpr)) {
llvm_unreachable("Evaluate failed");
return;
}
// Make sure it's a stack access
if (BaseRegNum != X86::RBP && BaseRegNum != X86::RSP) {
llvm_unreachable("Not a stack access");
return;
}
unsigned MemOpOffset = getMemoryOperandNo(Inst);
unsigned NewOpcode = 0;
if (I.IsLoad) {
switch (I.DataSize) {
case 2: NewOpcode = X86::POP16r; break;
case 4: NewOpcode = X86::POP32r; break;
case 8: NewOpcode = X86::POP64r; break;
default:
llvm_unreachable("Unexpected size");
}
unsigned RegOpndNum = Inst.getOperand(0).getReg();
Inst.clear();
Inst.setOpcode(NewOpcode);
Inst.addOperand(MCOperand::createReg(RegOpndNum));
} else {
MCOperand SrcOpnd =
Inst.getOperand(MemOpOffset + X86::AddrSegmentReg + 1);
if (I.StoreFromReg) {
switch (I.DataSize) {
case 2: NewOpcode = X86::PUSH16r; break;
case 4: NewOpcode = X86::PUSH32r; break;
case 8: NewOpcode = X86::PUSH64r; break;
default:
llvm_unreachable("Unexpected size");
}
assert(SrcOpnd.isReg() && "Unexpected source operand");
unsigned RegOpndNum = SrcOpnd.getReg();
Inst.clear();
Inst.setOpcode(NewOpcode);
Inst.addOperand(MCOperand::createReg(RegOpndNum));
} else {
switch (I.DataSize) {
case 2: NewOpcode = X86::PUSH16i8; break;
case 4: NewOpcode = X86::PUSH32i8; break;
case 8: NewOpcode = X86::PUSH64i32; break;
default:
llvm_unreachable("Unexpected size");
}
assert(SrcOpnd.isImm() && "Unexpected source operand");
int64_t SrcImm = SrcOpnd.getImm();
Inst.clear();
Inst.setOpcode(NewOpcode);
Inst.addOperand(MCOperand::createImm(SrcImm));
}
}
}
bool isStackAdjustment(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
default:
return false;
case X86::SUB64ri32:
case X86::SUB64ri8:
case X86::ADD64ri32:
case X86::ADD64ri8:
case X86::LEA64r:
break;
}
const MCInstrDesc &MCII = Info->get(Inst.getOpcode());
for (int I = 0, E = MCII.getNumDefs(); I != E; ++I) {
const MCOperand &Operand = Inst.getOperand(I);
if (Operand.isReg() && Operand.getReg() == X86::RSP)
return true;
}
return false;
}
bool
evaluateStackOffsetExpr(const MCInst &Inst, int64_t &Output,
std::pair<MCPhysReg, int64_t> Input1,
std::pair<MCPhysReg, int64_t> Input2) const override {
auto getOperandVal = [&](MCPhysReg Reg) -> ErrorOr<int64_t> {
if (Reg == Input1.first)
return Input1.second;
if (Reg == Input2.first)
return Input2.second;
return make_error_code(errc::result_out_of_range);
};
switch (Inst.getOpcode()) {
default:
return false;
case X86::SUB64ri32:
case X86::SUB64ri8:
if (!Inst.getOperand(2).isImm())
return false;
if (ErrorOr<int64_t> InputVal =
getOperandVal(Inst.getOperand(1).getReg()))
Output = *InputVal - Inst.getOperand(2).getImm();
else
return false;
break;
case X86::ADD64ri32:
case X86::ADD64ri8:
if (!Inst.getOperand(2).isImm())
return false;
if (ErrorOr<int64_t> InputVal =
getOperandVal(Inst.getOperand(1).getReg()))
Output = *InputVal + Inst.getOperand(2).getImm();
else
return false;
break;
case X86::ADD64i32:
if (!Inst.getOperand(0).isImm())
return false;
if (ErrorOr<int64_t> InputVal = getOperandVal(X86::RAX))
Output = *InputVal + Inst.getOperand(0).getImm();
else
return false;
break;
case X86::LEA64r: {
unsigned BaseRegNum;
int64_t ScaleValue;
unsigned IndexRegNum;
int64_t DispValue;
unsigned SegRegNum;
const MCExpr *DispExpr = nullptr;
if (!evaluateX86MemoryOperand(Inst, &BaseRegNum, &ScaleValue,
&IndexRegNum, &DispValue, &SegRegNum,
&DispExpr))
return false;
if (BaseRegNum == X86::NoRegister || IndexRegNum != X86::NoRegister ||
SegRegNum != X86::NoRegister || DispExpr)
return false;
if (ErrorOr<int64_t> InputVal = getOperandVal(BaseRegNum))
Output = *InputVal + DispValue;
else
return false;
break;
}
}
return true;
}
bool isRegToRegMove(const MCInst &Inst, MCPhysReg &From,
MCPhysReg &To) const override {
switch (Inst.getOpcode()) {
default:
return false;
case X86::LEAVE:
case X86::LEAVE64:
To = getStackPointer();
From = getFramePointer();
return true;
case X86::MOV64rr:
To = Inst.getOperand(0).getReg();
From = Inst.getOperand(1).getReg();
return true;
}
}
MCPhysReg getStackPointer() const override { return X86::RSP; }
MCPhysReg getFramePointer() const override { return X86::RBP; }
MCPhysReg getFlagsReg() const override { return X86::EFLAGS; }
bool escapesVariable(const MCInst &Inst,
bool HasFramePointer) const override {
int MemOpNo = getMemoryOperandNo(Inst);
const MCInstrDesc &MCII = Info->get(Inst.getOpcode());
const unsigned NumDefs = MCII.getNumDefs();
static BitVector SPBPAliases(BitVector(getAliases(X86::RSP)) |=
getAliases(X86::RBP));
static BitVector SPAliases(getAliases(X86::RSP));
// FIXME: PUSH can be technically a leak, but let's ignore this for now
// because a lot of harmless prologue code will spill SP to the stack.
// Unless push is clearly pushing an object address to the stack as
// demonstrated by having a MemOp.
bool IsPush = isPush(Inst);
if (IsPush && MemOpNo == -1)
return false;
// We use this to detect LEA (has memop but does not access mem)
bool AccessMem = MCII.mayLoad() || MCII.mayStore();
bool DoesLeak = false;
for (int I = 0, E = MCPlus::getNumPrimeOperands(Inst); I != E; ++I) {
// Ignore if SP/BP is used to dereference memory -- that's fine
if (MemOpNo != -1 && !IsPush && AccessMem && I >= MemOpNo &&
I <= MemOpNo + 5)
continue;
// Ignore if someone is writing to SP/BP
if (I < static_cast<int>(NumDefs))
continue;
const MCOperand &Operand = Inst.getOperand(I);
if (HasFramePointer && Operand.isReg() && SPBPAliases[Operand.getReg()]) {
DoesLeak = true;
break;
}
if (!HasFramePointer && Operand.isReg() && SPAliases[Operand.getReg()]) {
DoesLeak = true;
break;
}
}
// If potential leak, check if it is not just writing to itself/sp/bp
if (DoesLeak) {
for (int I = 0, E = NumDefs; I != E; ++I) {
const MCOperand &Operand = Inst.getOperand(I);
if (HasFramePointer && Operand.isReg() &&
SPBPAliases[Operand.getReg()]) {
DoesLeak = false;
break;
}
if (!HasFramePointer && Operand.isReg() &&
SPAliases[Operand.getReg()]) {
DoesLeak = false;
break;
}
}
}
return DoesLeak;
}
bool addToImm(MCInst &Inst, int64_t &Amt, MCContext *Ctx) const override {
unsigned ImmOpNo = -1U;
int MemOpNo = getMemoryOperandNo(Inst);
if (MemOpNo != -1)
ImmOpNo = MemOpNo + X86::AddrDisp;
else
for (unsigned Index = 0; Index < MCPlus::getNumPrimeOperands(Inst);
++Index)
if (Inst.getOperand(Index).isImm())
ImmOpNo = Index;
if (ImmOpNo == -1U)
return false;
MCOperand &Operand = Inst.getOperand(ImmOpNo);
Amt += Operand.getImm();
Operand.setImm(Amt);
// Check for the need for relaxation
if (int64_t(Amt) == int64_t(int8_t(Amt)))
return true;
// Relax instruction
switch (Inst.getOpcode()) {
case X86::SUB64ri8:
Inst.setOpcode(X86::SUB64ri32);
break;
case X86::ADD64ri8:
Inst.setOpcode(X86::ADD64ri32);
break;
default:
// No need for relaxation
break;
}
return true;
}
/// TODO: this implementation currently works for the most common opcodes that
/// load from memory. It can be extended to work with memory store opcodes as
/// well as more memory load opcodes.
bool replaceMemOperandWithImm(MCInst &Inst, StringRef ConstantData,
uint64_t Offset) const override {
enum CheckSignExt : uint8_t {
NOCHECK = 0,
CHECK8,
CHECK32,
};
using CheckList = std::vector<std::pair<CheckSignExt, unsigned>>;
struct InstInfo {
// Size in bytes that Inst loads from memory.
uint8_t DataSize;
// True when the target operand has to be duplicated because the opcode
// expects a LHS operand.
bool HasLHS;
// List of checks and corresponding opcodes to be used. We try to use the
// smallest possible immediate value when various sizes are available,
// hence we may need to check whether a larger constant fits in a smaller
// immediate.
CheckList Checks;
};
InstInfo I;
switch (Inst.getOpcode()) {
default: {
switch (getPopSize(Inst)) {
case 2: I = {2, false, {{NOCHECK, X86::MOV16ri}}}; break;
case 4: I = {4, false, {{NOCHECK, X86::MOV32ri}}}; break;
case 8: I = {8, false, {{CHECK32, X86::MOV64ri32},
{NOCHECK, X86::MOV64rm}}}; break;
default: return false;
}
break;
}
// MOV
case X86::MOV8rm: I = {1, false, {{NOCHECK, X86::MOV8ri}}}; break;
case X86::MOV16rm: I = {2, false, {{NOCHECK, X86::MOV16ri}}}; break;
case X86::MOV32rm: I = {4, false, {{NOCHECK, X86::MOV32ri}}}; break;
case X86::MOV64rm: I = {8, false, {{CHECK32, X86::MOV64ri32},
{NOCHECK, X86::MOV64rm}}}; break;
// MOVZX
case X86::MOVZX16rm8: I = {1, false, {{NOCHECK, X86::MOV16ri}}}; break;
case X86::MOVZX32rm8: I = {1, false, {{NOCHECK, X86::MOV32ri}}}; break;
case X86::MOVZX32rm16: I = {2, false, {{NOCHECK, X86::MOV32ri}}}; break;
// CMP
case X86::CMP8rm: I = {1, false, {{NOCHECK, X86::CMP8ri}}}; break;
case X86::CMP16rm: I = {2, false, {{CHECK8, X86::CMP16ri8},
{NOCHECK, X86::CMP16ri}}}; break;
case X86::CMP32rm: I = {4, false, {{CHECK8, X86::CMP32ri8},
{NOCHECK, X86::CMP32ri}}}; break;
case X86::CMP64rm: I = {8, false, {{CHECK8, X86::CMP64ri8},
{CHECK32, X86::CMP64ri32},
{NOCHECK, X86::CMP64rm}}}; break;
// TEST
case X86::TEST8mr: I = {1, false, {{NOCHECK, X86::TEST8ri}}}; break;
case X86::TEST16mr: I = {2, false, {{NOCHECK, X86::TEST16ri}}}; break;
case X86::TEST32mr: I = {4, false, {{NOCHECK, X86::TEST32ri}}}; break;
case X86::TEST64mr: I = {8, false, {{CHECK32, X86::TEST64ri32},
{NOCHECK, X86::TEST64mr}}}; break;
// ADD
case X86::ADD8rm: I = {1, true, {{NOCHECK, X86::ADD8ri}}}; break;
case X86::ADD16rm: I = {2, true, {{CHECK8, X86::ADD16ri8},
{NOCHECK, X86::ADD16ri}}}; break;
case X86::ADD32rm: I = {4, true, {{CHECK8, X86::ADD32ri8},
{NOCHECK, X86::ADD32ri}}}; break;
case X86::ADD64rm: I = {8, true, {{CHECK8, X86::ADD64ri8},
{CHECK32, X86::ADD64ri32},
{NOCHECK, X86::ADD64rm}}}; break;
// SUB
case X86::SUB8rm: I = {1, true, {{NOCHECK, X86::SUB8ri}}}; break;
case X86::SUB16rm: I = {2, true, {{CHECK8, X86::SUB16ri8},
{NOCHECK, X86::SUB16ri}}}; break;
case X86::SUB32rm: I = {4, true, {{CHECK8, X86::SUB32ri8},
{NOCHECK, X86::SUB32ri}}}; break;
case X86::SUB64rm: I = {8, true, {{CHECK8, X86::SUB64ri8},
{CHECK32, X86::SUB64ri32},
{NOCHECK, X86::SUB64rm}}}; break;
// AND
case X86::AND8rm: I = {1, true, {{NOCHECK, X86::AND8ri}}}; break;
case X86::AND16rm: I = {2, true, {{CHECK8, X86::AND16ri8},
{NOCHECK, X86::AND16ri}}}; break;
case X86::AND32rm: I = {4, true, {{CHECK8, X86::AND32ri8},
{NOCHECK, X86::AND32ri}}}; break;
case X86::AND64rm: I = {8, true, {{CHECK8, X86::AND64ri8},
{CHECK32, X86::AND64ri32},
{NOCHECK, X86::AND64rm}}}; break;
// OR
case X86::OR8rm: I = {1, true, {{NOCHECK, X86::OR8ri}}}; break;
case X86::OR16rm: I = {2, true, {{CHECK8, X86::OR16ri8},
{NOCHECK, X86::OR16ri}}}; break;
case X86::OR32rm: I = {4, true, {{CHECK8, X86::OR32ri8},
{NOCHECK, X86::OR32ri}}}; break;
case X86::OR64rm: I = {8, true, {{CHECK8, X86::OR64ri8},
{CHECK32, X86::OR64ri32},
{NOCHECK, X86::OR64rm}}}; break;
// XOR
case X86::XOR8rm: I = {1, true, {{NOCHECK, X86::XOR8ri}}}; break;
case X86::XOR16rm: I = {2, true, {{CHECK8, X86::XOR16ri8},
{NOCHECK, X86::XOR16ri}}}; break;
case X86::XOR32rm: I = {4, true, {{CHECK8, X86::XOR32ri8},
{NOCHECK, X86::XOR32ri}}}; break;
case X86::XOR64rm: I = {8, true, {{CHECK8, X86::XOR64ri8},
{CHECK32, X86::XOR64ri32},
{NOCHECK, X86::XOR64rm}}}; break;
}
// Compute the immediate value.
assert(Offset + I.DataSize <= ConstantData.size() &&
"invalid offset for given constant data");
int64_t ImmVal =
DataExtractor(ConstantData, true, 8).getSigned(&Offset, I.DataSize);
// Compute the new opcode.
unsigned NewOpcode = 0;
for (const std::pair<CheckSignExt, unsigned> &Check : I.Checks) {
NewOpcode = Check.second;
if (Check.first == NOCHECK)
break;
if (Check.first == CHECK8 && isInt<8>(ImmVal))
break;
if (Check.first == CHECK32 && isInt<32>(ImmVal))
break;
}
if (NewOpcode == Inst.getOpcode())
return false;
// Modify the instruction.
MCOperand ImmOp = MCOperand::createImm(ImmVal);
uint32_t TargetOpNum = 0;
// Test instruction does not follow the regular pattern of putting the
// memory reference of a load (5 MCOperands) last in the list of operands.
// Since it is not modifying the register operand, it is not treated as
// a destination operand and it is not the first operand as it is in the
// other instructions we treat here.
if (NewOpcode == X86::TEST8ri || NewOpcode == X86::TEST16ri ||
NewOpcode == X86::TEST32ri || NewOpcode == X86::TEST64ri32)
TargetOpNum = getMemoryOperandNo(Inst) + X86::AddrNumOperands;
MCOperand TargetOp = Inst.getOperand(TargetOpNum);
Inst.clear();
Inst.setOpcode(NewOpcode);
Inst.addOperand(TargetOp);
if (I.HasLHS)
Inst.addOperand(TargetOp);
Inst.addOperand(ImmOp);
return true;
}
/// TODO: this implementation currently works for the most common opcodes that
/// load from memory. It can be extended to work with memory store opcodes as
/// well as more memory load opcodes.
bool replaceMemOperandWithReg(MCInst &Inst, MCPhysReg RegNum) const override {
unsigned NewOpcode;
switch (Inst.getOpcode()) {
default: {
switch (getPopSize(Inst)) {
case 2: NewOpcode = X86::MOV16rr; break;
case 4: NewOpcode = X86::MOV32rr; break;
case 8: NewOpcode = X86::MOV64rr; break;
default: return false;
}
break;
}
// MOV
case X86::MOV8rm: NewOpcode = X86::MOV8rr; break;
case X86::MOV16rm: NewOpcode = X86::MOV16rr; break;
case X86::MOV32rm: NewOpcode = X86::MOV32rr; break;
case X86::MOV64rm: NewOpcode = X86::MOV64rr; break;
}
// Modify the instruction.
MCOperand RegOp = MCOperand::createReg(RegNum);
MCOperand TargetOp = Inst.getOperand(0);
Inst.clear();
Inst.setOpcode(NewOpcode);
Inst.addOperand(TargetOp);
Inst.addOperand(RegOp);
return true;
}
bool isRedundantMove(const MCInst &Inst) const override {
switch (Inst.getOpcode()) {
default:
return false;
// MOV
case X86::MOV8rr:
case X86::MOV16rr:
case X86::MOV32rr:
case X86::MOV64rr:
break;
}
assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg());
return Inst.getOperand(0).getReg() == Inst.getOperand(1).getReg();
}
bool requiresAlignedAddress(const MCInst &Inst) const override {
const MCInstrDesc &Desc = Info->get(Inst.getOpcode());
for (unsigned int I = 0; I < Desc.getNumOperands(); ++I) {
const MCOperandInfo &Op = Desc.OpInfo[I];
if (Op.OperandType != MCOI::OPERAND_REGISTER)
continue;
if (Op.RegClass == X86::VR128RegClassID)
return true;
}
return false;
}
bool convertJmpToTailCall(MCInst &Inst) override {
if (isTailCall(Inst))
return false;
int NewOpcode;
switch (Inst.getOpcode()) {
default:
return false;
case X86::JMP_1:
case X86::JMP_2:
case X86::JMP_4:
NewOpcode = X86::JMP_4;
break;
case X86::JMP16m:
case X86::JMP32m:
case X86::JMP64m:
NewOpcode = X86::JMP32m;
break;
case X86::JMP16r:
case X86::JMP32r:
case X86::JMP64r:
NewOpcode = X86::JMP32r;
break;
}
Inst.setOpcode(NewOpcode);
setTailCall(Inst);
return true;
}
bool convertTailCallToJmp(MCInst &Inst) override {
int NewOpcode;
switch (Inst.getOpcode()) {
default:
return false;
case X86::JMP_4:
NewOpcode = X86::JMP_1;
break;
case X86::JMP32m:
NewOpcode = X86::JMP64m;
break;
case X86::JMP32r:
NewOpcode = X86::JMP64r;
break;
}
Inst.setOpcode(NewOpcode);
removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
clearOffset(Inst);
return true;
}
bool convertTailCallToCall(MCInst &Inst) override {
int NewOpcode;
switch (Inst.getOpcode()) {
default:
return false;
case X86::JMP_4:
NewOpcode = X86::CALL64pcrel32;
break;
case X86::JMP32m:
NewOpcode = X86::CALL64m;
break;
case X86::JMP32r:
NewOpcode = X86::CALL64r;
break;
}
Inst.setOpcode(NewOpcode);
removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
return true;
}
bool convertCallToIndirectCall(MCInst &Inst, const MCSymbol *TargetLocation,
MCContext *Ctx) override {
assert((Inst.getOpcode() == X86::CALL64pcrel32 ||
(Inst.getOpcode() == X86::JMP_4 && isTailCall(Inst))) &&
"64-bit direct (tail) call instruction expected");
const auto NewOpcode =
(Inst.getOpcode() == X86::CALL64pcrel32) ? X86::CALL64m : X86::JMP32m;
Inst.setOpcode(NewOpcode);
// Replace the first operand and preserve auxiliary operands of
// the instruction.
Inst.erase(Inst.begin());
Inst.insert(Inst.begin(),
MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
Inst.insert(Inst.begin(),
MCOperand::createExpr( // Displacement
MCSymbolRefExpr::create(TargetLocation,
MCSymbolRefExpr::VK_None, *Ctx)));
Inst.insert(Inst.begin(),
MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.insert(Inst.begin(),
MCOperand::createImm(1)); // ScaleAmt
Inst.insert(Inst.begin(),
MCOperand::createReg(X86::RIP)); // BaseReg
return true;
}
void convertIndirectCallToLoad(MCInst &Inst, MCPhysReg Reg) override {
bool IsTailCall = isTailCall(Inst);
if (IsTailCall)
removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
if (Inst.getOpcode() == X86::CALL64m ||
(Inst.getOpcode() == X86::JMP32m && IsTailCall)) {
Inst.setOpcode(X86::MOV64rm);
Inst.insert(Inst.begin(), MCOperand::createReg(Reg));
return;
}
if (Inst.getOpcode() == X86::CALL64r ||
(Inst.getOpcode() == X86::JMP32r && IsTailCall)) {
Inst.setOpcode(X86::MOV64rr);
Inst.insert(Inst.begin(), MCOperand::createReg(Reg));
return;
}
LLVM_DEBUG(Inst.dump());
llvm_unreachable("not implemented");
}
bool shortenInstruction(MCInst &Inst,
const MCSubtargetInfo &STI) const override {
unsigned OldOpcode = Inst.getOpcode();
unsigned NewOpcode = OldOpcode;
int MemOpNo = getMemoryOperandNo(Inst);
// Check and remove redundant Address-Size override prefix.
if (opts::X86StripRedundantAddressSize) {
uint64_t TSFlags = Info->get(OldOpcode).TSFlags;
unsigned Flags = Inst.getFlags();
if (!X86_MC::needsAddressSizeOverride(Inst, STI, MemOpNo, TSFlags) &&
Flags & X86::IP_HAS_AD_SIZE)
Inst.setFlags(Flags ^ X86::IP_HAS_AD_SIZE);
}
// Check and remove EIZ/RIZ. These cases represent ambiguous cases where
// SIB byte is present, but no index is used and modrm alone should have
// been enough. Converting to NoRegister effectively removes the SIB byte.
if (MemOpNo >= 0) {
MCOperand &IndexOp =
Inst.getOperand(static_cast<unsigned>(MemOpNo) + X86::AddrIndexReg);
if (IndexOp.getReg() == X86::EIZ || IndexOp.getReg() == X86::RIZ)
IndexOp = MCOperand::createReg(X86::NoRegister);
}
if (isBranch(Inst)) {
NewOpcode = getShortBranchOpcode(OldOpcode);
} else if (OldOpcode == X86::MOV64ri) {
if (Inst.getOperand(MCPlus::getNumPrimeOperands(Inst) - 1).isImm()) {
const int64_t Imm =
Inst.getOperand(MCPlus::getNumPrimeOperands(Inst) - 1).getImm();
if (int64_t(Imm) == int64_t(int32_t(Imm)))
NewOpcode = X86::MOV64ri32;
}
} else {
// If it's arithmetic instruction check if signed operand fits in 1 byte.
const unsigned ShortOpcode = getShortArithOpcode(OldOpcode);
if (ShortOpcode != OldOpcode &&
Inst.getOperand(MCPlus::getNumPrimeOperands(Inst) - 1).isImm()) {
int64_t Imm =
Inst.getOperand(MCPlus::getNumPrimeOperands(Inst) - 1).getImm();
if (int64_t(Imm) == int64_t(int8_t(Imm)))
NewOpcode = ShortOpcode;
}
}
if (NewOpcode == OldOpcode)
return false;
Inst.setOpcode(NewOpcode);
return true;
}
bool
convertMoveToConditionalMove(MCInst &Inst, unsigned CC, bool AllowStackMemOp,
bool AllowBasePtrStackMemOp) const override {
// - Register-register moves are OK
// - Stores are filtered out by opcode (no store CMOV)
// - Non-stack loads are prohibited (generally unsafe)
// - Stack loads are OK if AllowStackMemOp is true
// - Stack loads with RBP are OK if AllowBasePtrStackMemOp is true
if (isLoad(Inst)) {
// If stack memory operands are not allowed, no loads are allowed
if (!AllowStackMemOp)
return false;
// If stack memory operands are allowed, check if it's a load from stack
bool IsLoad, IsStore, IsStoreFromReg, IsSimple, IsIndexed;
MCPhysReg Reg;
int32_t SrcImm;
uint16_t StackPtrReg;
int64_t StackOffset;
uint8_t Size;
bool IsStackAccess =
isStackAccess(Inst, IsLoad, IsStore, IsStoreFromReg, Reg, SrcImm,
StackPtrReg, StackOffset, Size, IsSimple, IsIndexed);
// Prohibit non-stack-based loads
if (!IsStackAccess)
return false;
// If stack memory operands are allowed, check if it's RBP-based
if (!AllowBasePtrStackMemOp &&
RegInfo->isSubRegisterEq(X86::RBP, StackPtrReg))
return false;
}
unsigned NewOpcode = 0;
switch (Inst.getOpcode()) {
case X86::MOV16rr:
NewOpcode = X86::CMOV16rr;
break;
case X86::MOV16rm:
NewOpcode = X86::CMOV16rm;
break;
case X86::MOV32rr:
NewOpcode = X86::CMOV32rr;
break;
case X86::MOV32rm:
NewOpcode = X86::CMOV32rm;
break;
case X86::MOV64rr:
NewOpcode = X86::CMOV64rr;
break;
case X86::MOV64rm:
NewOpcode = X86::CMOV64rm;
break;
default:
return false;
}
Inst.setOpcode(NewOpcode);
// Insert CC at the end of prime operands, before annotations
Inst.insert(Inst.begin() + MCPlus::getNumPrimeOperands(Inst),
MCOperand::createImm(CC));
// CMOV is a 3-operand MCInst, so duplicate the destination as src1
Inst.insert(Inst.begin(), Inst.getOperand(0));
return true;
}
bool lowerTailCall(MCInst &Inst) override {
if (Inst.getOpcode() == X86::JMP_4 && isTailCall(Inst)) {
Inst.setOpcode(X86::JMP_1);
removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
return true;
}
return false;
}
const MCSymbol *getTargetSymbol(const MCInst &Inst,
unsigned OpNum = 0) const override {
if (OpNum >= MCPlus::getNumPrimeOperands(Inst))
return nullptr;
const MCOperand &Op = Inst.getOperand(OpNum);
if (!Op.isExpr())
return nullptr;
auto *SymExpr = dyn_cast<MCSymbolRefExpr>(Op.getExpr());
if (!SymExpr || SymExpr->getKind() != MCSymbolRefExpr::VK_None)
return nullptr;
return &SymExpr->getSymbol();
}
// This is the same as the base class, but since we are overriding one of
// getTargetSymbol's signatures above, we need to override all of them.
const MCSymbol *getTargetSymbol(const MCExpr *Expr) const override {
return &cast<const MCSymbolRefExpr>(Expr)->getSymbol();
}
bool analyzeBranch(InstructionIterator Begin, InstructionIterator End,
const MCSymbol *&TBB, const MCSymbol *&FBB,
MCInst *&CondBranch,
MCInst *&UncondBranch) const override {
auto I = End;
// Bottom-up analysis
while (I != Begin) {
--I;
// Ignore nops and CFIs
if (isPseudo(*I))
continue;
// Stop when we find the first non-terminator
if (!isTerminator(*I))
break;
if (!isBranch(*I))
break;
// Handle unconditional branches.
if ((I->getOpcode() == X86::JMP_1 || I->getOpcode() == X86::JMP_2 ||
I->getOpcode() == X86::JMP_4) &&
!isTailCall(*I)) {
// If any code was seen after this unconditional branch, we've seen
// unreachable code. Ignore them.
CondBranch = nullptr;
UncondBranch = &*I;
const MCSymbol *Sym = getTargetSymbol(*I);
assert(Sym != nullptr &&
"Couldn't extract BB symbol from jump operand");
TBB = Sym;
continue;
}
// Handle conditional branches and ignore indirect branches
if (!isUnsupportedBranch(I->getOpcode()) &&
getCondCode(*I) == X86::COND_INVALID) {
// Indirect branch
return false;
}
if (CondBranch == nullptr) {
const MCSymbol *TargetBB = getTargetSymbol(*I);
if (TargetBB == nullptr) {
// Unrecognized branch target
return false;
}
FBB = TBB;
TBB = TargetBB;
CondBranch = &*I;
continue;
}
llvm_unreachable("multiple conditional branches in one BB");
}
return true;
}
template <typename Itr>
std::pair<IndirectBranchType, MCInst *>
analyzePICJumpTable(Itr II, Itr IE, MCPhysReg R1, MCPhysReg R2) const {
// Analyze PIC-style jump table code template:
//
// lea PIC_JUMP_TABLE(%rip), {%r1|%r2} <- MemLocInstr
// mov ({%r1|%r2}, %index, 4), {%r2|%r1}
// add %r2, %r1
// jmp *%r1
//
// (with any irrelevant instructions in-between)
//
// When we call this helper we've already determined %r1 and %r2, and
// reverse instruction iterator \p II is pointing to the ADD instruction.
//
// PIC jump table looks like following:
//
// JT: ----------
// E1:| L1 - JT |
// |----------|
// E2:| L2 - JT |
// |----------|
// | |
// ......
// En:| Ln - JT |
// ----------
//
// Where L1, L2, ..., Ln represent labels in the function.
//
// The actual relocations in the table will be of the form:
//
// Ln - JT
// = (Ln - En) + (En - JT)
// = R_X86_64_PC32(Ln) + En - JT
// = R_X86_64_PC32(Ln + offsetof(En))
//
LLVM_DEBUG(dbgs() << "Checking for PIC jump table\n");
MCInst *MemLocInstr = nullptr;
const MCInst *MovInstr = nullptr;
while (++II != IE) {
MCInst &Instr = *II;
const MCInstrDesc &InstrDesc = Info->get(Instr.getOpcode());
if (!InstrDesc.hasDefOfPhysReg(Instr, R1, *RegInfo) &&
!InstrDesc.hasDefOfPhysReg(Instr, R2, *RegInfo)) {
// Ignore instructions that don't affect R1, R2 registers.
continue;
}
if (!MovInstr) {
// Expect to see MOV instruction.
if (!isMOVSX64rm32(Instr)) {
LLVM_DEBUG(dbgs() << "MOV instruction expected.\n");
break;
}
// Check if it's setting %r1 or %r2. In canonical form it sets %r2.
// If it sets %r1 - rename the registers so we have to only check
// a single form.
unsigned MovDestReg = Instr.getOperand(0).getReg();
if (MovDestReg != R2)
std::swap(R1, R2);
if (MovDestReg != R2) {
LLVM_DEBUG(dbgs() << "MOV instruction expected to set %r2\n");
break;
}
// Verify operands for MOV.
unsigned BaseRegNum;
int64_t ScaleValue;
unsigned IndexRegNum;
int64_t DispValue;
unsigned SegRegNum;
if (!evaluateX86MemoryOperand(Instr, &BaseRegNum, &ScaleValue,
&IndexRegNum, &DispValue, &SegRegNum))
break;
if (BaseRegNum != R1 || ScaleValue != 4 ||
IndexRegNum == X86::NoRegister || DispValue != 0 ||
SegRegNum != X86::NoRegister)
break;
MovInstr = &Instr;
} else {
if (!InstrDesc.hasDefOfPhysReg(Instr, R1, *RegInfo))
continue;
if (!isLEA64r(Instr)) {
LLVM_DEBUG(dbgs() << "LEA instruction expected\n");
break;
}
if (Instr.getOperand(0).getReg() != R1) {
LLVM_DEBUG(dbgs() << "LEA instruction expected to set %r1\n");
break;
}
// Verify operands for LEA.
unsigned BaseRegNum;
int64_t ScaleValue;
unsigned IndexRegNum;
const MCExpr *DispExpr = nullptr;
int64_t DispValue;
unsigned SegRegNum;
if (!evaluateX86MemoryOperand(Instr, &BaseRegNum, &ScaleValue,
&IndexRegNum, &DispValue, &SegRegNum,
&DispExpr))
break;
if (BaseRegNum != RegInfo->getProgramCounter() ||
IndexRegNum != X86::NoRegister || SegRegNum != X86::NoRegister ||
DispExpr == nullptr)
break;
MemLocInstr = &Instr;
break;
}
}
if (!MemLocInstr)
return std::make_pair(IndirectBranchType::UNKNOWN, nullptr);
LLVM_DEBUG(dbgs() << "checking potential PIC jump table\n");
return std::make_pair(IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE,
MemLocInstr);
}
IndirectBranchType analyzeIndirectBranch(
MCInst &Instruction, InstructionIterator Begin, InstructionIterator End,
const unsigned PtrSize, MCInst *&MemLocInstrOut, unsigned &BaseRegNumOut,
unsigned &IndexRegNumOut, int64_t &DispValueOut,
const MCExpr *&DispExprOut, MCInst *&PCRelBaseOut) const override {
// Try to find a (base) memory location from where the address for
// the indirect branch is loaded. For X86-64 the memory will be specified
// in the following format:
//
// {%rip}/{%basereg} + Imm + IndexReg * Scale
//
// We are interested in the cases where Scale == sizeof(uintptr_t) and
// the contents of the memory are presumably an array of pointers to code.
//
// Normal jump table:
//
// jmp *(JUMP_TABLE, %index, Scale) <- MemLocInstr
//
// or
//
// mov (JUMP_TABLE, %index, Scale), %r1 <- MemLocInstr
// ...
// jmp %r1
//
// We handle PIC-style jump tables separately.
//
MemLocInstrOut = nullptr;
BaseRegNumOut = X86::NoRegister;
IndexRegNumOut = X86::NoRegister;
DispValueOut = 0;
DispExprOut = nullptr;
std::reverse_iterator<InstructionIterator> II(End);
std::reverse_iterator<InstructionIterator> IE(Begin);
IndirectBranchType Type = IndirectBranchType::UNKNOWN;
// An instruction referencing memory used by jump instruction (directly or
// via register). This location could be an array of function pointers
// in case of indirect tail call, or a jump table.
MCInst *MemLocInstr = nullptr;
if (MCPlus::getNumPrimeOperands(Instruction) == 1) {
// If the indirect jump is on register - try to detect if the
// register value is loaded from a memory location.
assert(Instruction.getOperand(0).isReg() && "register operand expected");
const unsigned R1 = Instruction.getOperand(0).getReg();
// Check if one of the previous instructions defines the jump-on register.
for (auto PrevII = II; PrevII != IE; ++PrevII) {
MCInst &PrevInstr = *PrevII;
const MCInstrDesc &PrevInstrDesc = Info->get(PrevInstr.getOpcode());
if (!PrevInstrDesc.hasDefOfPhysReg(PrevInstr, R1, *RegInfo))
continue;
if (isMoveMem2Reg(PrevInstr)) {
MemLocInstr = &PrevInstr;
break;
}
if (isADD64rr(PrevInstr)) {
unsigned R2 = PrevInstr.getOperand(2).getReg();
if (R1 == R2)
return IndirectBranchType::UNKNOWN;
std::tie(Type, MemLocInstr) = analyzePICJumpTable(PrevII, IE, R1, R2);
break;
}
return IndirectBranchType::UNKNOWN;
}
if (!MemLocInstr) {
// No definition seen for the register in this function so far. Could be
// an input parameter - which means it is an external code reference.
// It also could be that the definition happens to be in the code that
// we haven't processed yet. Since we have to be conservative, return
// as UNKNOWN case.
return IndirectBranchType::UNKNOWN;
}
} else {
MemLocInstr = &Instruction;
}
const MCRegister RIPRegister = RegInfo->getProgramCounter();
// Analyze the memory location.
unsigned BaseRegNum, IndexRegNum, SegRegNum;
int64_t ScaleValue, DispValue;
const MCExpr *DispExpr;
if (!evaluateX86MemoryOperand(*MemLocInstr, &BaseRegNum, &ScaleValue,
&IndexRegNum, &DispValue, &SegRegNum,
&DispExpr))
return IndirectBranchType::UNKNOWN;
BaseRegNumOut = BaseRegNum;
IndexRegNumOut = IndexRegNum;
DispValueOut = DispValue;
DispExprOut = DispExpr;
if ((BaseRegNum != X86::NoRegister && BaseRegNum != RIPRegister) ||
SegRegNum != X86::NoRegister)
return IndirectBranchType::UNKNOWN;
if (MemLocInstr == &Instruction &&
(!ScaleValue || IndexRegNum == X86::NoRegister)) {
MemLocInstrOut = MemLocInstr;
return IndirectBranchType::POSSIBLE_FIXED_BRANCH;
}
if (Type == IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE &&
(ScaleValue != 1 || BaseRegNum != RIPRegister))
return IndirectBranchType::UNKNOWN;
if (Type != IndirectBranchType::POSSIBLE_PIC_JUMP_TABLE &&
ScaleValue != PtrSize)
return IndirectBranchType::UNKNOWN;
MemLocInstrOut = MemLocInstr;
return Type;
}
/// Analyze a callsite to see if it could be a virtual method call. This only
/// checks to see if the overall pattern is satisfied, it does not guarantee
/// that the callsite is a true virtual method call.
/// The format of virtual method calls that are recognized is one of the
/// following:
///
/// Form 1: (found in debug code)
/// add METHOD_OFFSET, %VtableReg
/// mov (%VtableReg), %MethodReg
/// ...
/// call or jmp *%MethodReg
///
/// Form 2:
/// mov METHOD_OFFSET(%VtableReg), %MethodReg
/// ...
/// call or jmp *%MethodReg
///
/// Form 3:
/// ...
/// call or jmp *METHOD_OFFSET(%VtableReg)
///
bool analyzeVirtualMethodCall(InstructionIterator ForwardBegin,
InstructionIterator ForwardEnd,
std::vector<MCInst *> &MethodFetchInsns,
unsigned &VtableRegNum, unsigned &MethodRegNum,
uint64_t &MethodOffset) const override {
VtableRegNum = X86::NoRegister;
MethodRegNum = X86::NoRegister;
MethodOffset = 0;
std::reverse_iterator<InstructionIterator> Itr(ForwardEnd);
std::reverse_iterator<InstructionIterator> End(ForwardBegin);
MCInst &CallInst = *Itr++;
assert(isIndirectBranch(CallInst) || isCall(CallInst));
unsigned BaseReg, IndexReg, SegmentReg;
int64_t Scale, Disp;
const MCExpr *DispExpr;
// The call can just be jmp offset(reg)
if (evaluateX86MemoryOperand(CallInst, &BaseReg, &Scale, &IndexReg, &Disp,
&SegmentReg, &DispExpr)) {
if (!DispExpr && BaseReg != X86::RIP && BaseReg != X86::RBP &&
BaseReg != X86::NoRegister) {
MethodRegNum = BaseReg;
if (Scale == 1 && IndexReg == X86::NoRegister &&
SegmentReg == X86::NoRegister) {
VtableRegNum = MethodRegNum;
MethodOffset = Disp;
MethodFetchInsns.push_back(&CallInst);
return true;
}
}
return false;
}
if (CallInst.getOperand(0).isReg())
MethodRegNum = CallInst.getOperand(0).getReg();
else
return false;
if (MethodRegNum == X86::RIP || MethodRegNum == X86::RBP) {
VtableRegNum = X86::NoRegister;
MethodRegNum = X86::NoRegister;
return false;
}
// find load from vtable, this may or may not include the method offset
while (Itr != End) {
MCInst &CurInst = *Itr++;
const MCInstrDesc &Desc = Info->get(CurInst.getOpcode());
if (Desc.hasDefOfPhysReg(CurInst, MethodRegNum, *RegInfo)) {
if (isLoad(CurInst) &&
evaluateX86MemoryOperand(CurInst, &BaseReg, &Scale, &IndexReg,
&Disp, &SegmentReg, &DispExpr)) {
if (!DispExpr && Scale == 1 && BaseReg != X86::RIP &&
BaseReg != X86::RBP && BaseReg != X86::NoRegister &&
IndexReg == X86::NoRegister && SegmentReg == X86::NoRegister &&
BaseReg != X86::RIP) {
VtableRegNum = BaseReg;
MethodOffset = Disp;
MethodFetchInsns.push_back(&CurInst);
if (MethodOffset != 0)
return true;
break;
}
}
return false;
}
}
if (!VtableRegNum)
return false;
// look for any adds affecting the method register.
while (Itr != End) {
MCInst &CurInst = *Itr++;
const MCInstrDesc &Desc = Info->get(CurInst.getOpcode());
if (Desc.hasDefOfPhysReg(CurInst, VtableRegNum, *RegInfo)) {
if (isADDri(CurInst)) {
assert(!MethodOffset);
MethodOffset = CurInst.getOperand(2).getImm();
MethodFetchInsns.insert(MethodFetchInsns.begin(), &CurInst);
break;
}
}
}
return true;
}
bool createStackPointerIncrement(MCInst &Inst, int Size,
bool NoFlagsClobber) const override {
if (NoFlagsClobber) {
Inst.setOpcode(X86::LEA64r);
Inst.clear();
Inst.addOperand(MCOperand::createReg(X86::RSP));
Inst.addOperand(MCOperand::createReg(X86::RSP)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createImm(-Size)); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
return true;
}
Inst.setOpcode(X86::SUB64ri8);
Inst.clear();
Inst.addOperand(MCOperand::createReg(X86::RSP));
Inst.addOperand(MCOperand::createReg(X86::RSP));
Inst.addOperand(MCOperand::createImm(Size));
return true;
}
bool createStackPointerDecrement(MCInst &Inst, int Size,
bool NoFlagsClobber) const override {
if (NoFlagsClobber) {
Inst.setOpcode(X86::LEA64r);
Inst.clear();
Inst.addOperand(MCOperand::createReg(X86::RSP));
Inst.addOperand(MCOperand::createReg(X86::RSP)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createImm(Size)); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
return true;
}
Inst.setOpcode(X86::ADD64ri8);
Inst.clear();
Inst.addOperand(MCOperand::createReg(X86::RSP));
Inst.addOperand(MCOperand::createReg(X86::RSP));
Inst.addOperand(MCOperand::createImm(Size));
return true;
}
bool createSaveToStack(MCInst &Inst, const MCPhysReg &StackReg, int Offset,
const MCPhysReg &SrcReg, int Size) const override {
unsigned NewOpcode;
switch (Size) {
default:
return false;
case 2: NewOpcode = X86::MOV16mr; break;
case 4: NewOpcode = X86::MOV32mr; break;
case 8: NewOpcode = X86::MOV64mr; break;
}
Inst.setOpcode(NewOpcode);
Inst.clear();
Inst.addOperand(MCOperand::createReg(StackReg)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createImm(Offset)); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
Inst.addOperand(MCOperand::createReg(SrcReg));
return true;
}
bool createRestoreFromStack(MCInst &Inst, const MCPhysReg &StackReg,
int Offset, const MCPhysReg &DstReg,
int Size) const override {
return createLoad(Inst, StackReg, /*Scale=*/1, /*IndexReg=*/X86::NoRegister,
Offset, nullptr, /*AddrSegmentReg=*/X86::NoRegister,
DstReg, Size);
}
bool createLoad(MCInst &Inst, const MCPhysReg &BaseReg, int64_t Scale,
const MCPhysReg &IndexReg, int64_t Offset,
const MCExpr *OffsetExpr, const MCPhysReg &AddrSegmentReg,
const MCPhysReg &DstReg, int Size) const override {
unsigned NewOpcode;
switch (Size) {
default:
return false;
case 2: NewOpcode = X86::MOV16rm; break;
case 4: NewOpcode = X86::MOV32rm; break;
case 8: NewOpcode = X86::MOV64rm; break;
}
Inst.setOpcode(NewOpcode);
Inst.clear();
Inst.addOperand(MCOperand::createReg(DstReg));
Inst.addOperand(MCOperand::createReg(BaseReg));
Inst.addOperand(MCOperand::createImm(Scale));
Inst.addOperand(MCOperand::createReg(IndexReg));
if (OffsetExpr)
Inst.addOperand(MCOperand::createExpr(OffsetExpr)); // Displacement
else
Inst.addOperand(MCOperand::createImm(Offset)); // Displacement
Inst.addOperand(MCOperand::createReg(AddrSegmentReg)); // AddrSegmentReg
return true;
}
void createLoadImmediate(MCInst &Inst, const MCPhysReg Dest,
uint32_t Imm) const override {
Inst.setOpcode(X86::MOV64ri32);
Inst.clear();
Inst.addOperand(MCOperand::createReg(Dest));
Inst.addOperand(MCOperand::createImm(Imm));
}
bool createIncMemory(MCInst &Inst, const MCSymbol *Target,
MCContext *Ctx) const override {
Inst.setOpcode(X86::LOCK_INC64m);
Inst.clear();
Inst.addOperand(MCOperand::createReg(X86::RIP)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None,
*Ctx))); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
return true;
}
bool createIJmp32Frag(SmallVectorImpl<MCInst> &Insts,
const MCOperand &BaseReg, const MCOperand &Scale,
const MCOperand &IndexReg, const MCOperand &Offset,
const MCOperand &TmpReg) const override {
// The code fragment we emit here is:
//
// mov32 (%base, %index, scale), %tmpreg
// ijmp *(%tmpreg)
//
MCInst IJmp;
IJmp.setOpcode(X86::JMP64r);
IJmp.addOperand(TmpReg);
MCInst Load;
Load.setOpcode(X86::MOV32rm);
Load.addOperand(TmpReg);
Load.addOperand(BaseReg);
Load.addOperand(Scale);
Load.addOperand(IndexReg);
Load.addOperand(Offset);
Load.addOperand(MCOperand::createReg(X86::NoRegister));
Insts.push_back(Load);
Insts.push_back(IJmp);
return true;
}
bool createNoop(MCInst &Inst) const override {
Inst.setOpcode(X86::NOOP);
return true;
}
bool createReturn(MCInst &Inst) const override {
Inst.setOpcode(X86::RET64);
return true;
}
InstructionListType createInlineMemcpy(bool ReturnEnd) const override {
InstructionListType Code;
if (ReturnEnd)
Code.emplace_back(MCInstBuilder(X86::LEA64r)
.addReg(X86::RAX)
.addReg(X86::RDI)
.addImm(1)
.addReg(X86::RDX)
.addImm(0)
.addReg(X86::NoRegister));
else
Code.emplace_back(MCInstBuilder(X86::MOV64rr)
.addReg(X86::RAX)
.addReg(X86::RDI));
Code.emplace_back(MCInstBuilder(X86::MOV32rr)
.addReg(X86::ECX)
.addReg(X86::EDX));
Code.emplace_back(MCInstBuilder(X86::REP_MOVSB_64));
return Code;
}
InstructionListType createOneByteMemcpy() const override {
InstructionListType Code;
Code.emplace_back(MCInstBuilder(X86::MOV8rm)
.addReg(X86::CL)
.addReg(X86::RSI)
.addImm(0)
.addReg(X86::NoRegister)
.addImm(0)
.addReg(X86::NoRegister));
Code.emplace_back(MCInstBuilder(X86::MOV8mr)
.addReg(X86::RDI)
.addImm(0)
.addReg(X86::NoRegister)
.addImm(0)
.addReg(X86::NoRegister)
.addReg(X86::CL));
Code.emplace_back(MCInstBuilder(X86::MOV64rr)
.addReg(X86::RAX)
.addReg(X86::RDI));
return Code;
}
InstructionListType createCmpJE(MCPhysReg RegNo, int64_t Imm,
const MCSymbol *Target,
MCContext *Ctx) const override {
InstructionListType Code;
Code.emplace_back(MCInstBuilder(X86::CMP64ri8)
.addReg(RegNo)
.addImm(Imm));
Code.emplace_back(MCInstBuilder(X86::JCC_1)
.addExpr(MCSymbolRefExpr::create(
Target, MCSymbolRefExpr::VK_None, *Ctx))
.addImm(X86::COND_E));
return Code;
}
std::optional<Relocation>
createRelocation(const MCFixup &Fixup,
const MCAsmBackend &MAB) const override {
const MCFixupKindInfo &FKI = MAB.getFixupKindInfo(Fixup.getKind());
assert(FKI.TargetOffset == 0 && "0-bit relocation offset expected");
const uint64_t RelOffset = Fixup.getOffset();
uint64_t RelType;
if (FKI.Flags & MCFixupKindInfo::FKF_IsPCRel) {
switch (FKI.TargetSize) {
default:
return std::nullopt;
case 8: RelType = ELF::R_X86_64_PC8; break;
case 16: RelType = ELF::R_X86_64_PC16; break;
case 32: RelType = ELF::R_X86_64_PC32; break;
case 64: RelType = ELF::R_X86_64_PC64; break;
}
} else {
switch (FKI.TargetSize) {
default:
return std::nullopt;
case 8: RelType = ELF::R_X86_64_8; break;
case 16: RelType = ELF::R_X86_64_16; break;
case 32: RelType = ELF::R_X86_64_32; break;
case 64: RelType = ELF::R_X86_64_64; break;
}
}
// Extract a symbol and an addend out of the fixup value expression.
//
// Only the following limited expression types are supported:
// Symbol + Addend
// Symbol
uint64_t Addend = 0;
MCSymbol *Symbol = nullptr;
const MCExpr *ValueExpr = Fixup.getValue();
if (ValueExpr->getKind() == MCExpr::Binary) {
const auto *BinaryExpr = cast<MCBinaryExpr>(ValueExpr);
assert(BinaryExpr->getOpcode() == MCBinaryExpr::Add &&
"unexpected binary expression");
const MCExpr *LHS = BinaryExpr->getLHS();
assert(LHS->getKind() == MCExpr::SymbolRef && "unexpected LHS");
Symbol = const_cast<MCSymbol *>(this->getTargetSymbol(LHS));
const MCExpr *RHS = BinaryExpr->getRHS();
assert(RHS->getKind() == MCExpr::Constant && "unexpected RHS");
Addend = cast<MCConstantExpr>(RHS)->getValue();
} else {
assert(ValueExpr->getKind() == MCExpr::SymbolRef && "unexpected value");
Symbol = const_cast<MCSymbol *>(this->getTargetSymbol(ValueExpr));
}
return Relocation({RelOffset, Symbol, RelType, Addend, 0});
}
bool replaceImmWithSymbolRef(MCInst &Inst, const MCSymbol *Symbol,
int64_t Addend, MCContext *Ctx, int64_t &Value,
uint64_t RelType) const override {
unsigned ImmOpNo = -1U;
for (unsigned Index = 0; Index < MCPlus::getNumPrimeOperands(Inst);
++Index) {
if (Inst.getOperand(Index).isImm()) {
ImmOpNo = Index;
// TODO: this is a bit hacky. It finds the correct operand by
// searching for a specific immediate value. If no value is
// provided it defaults to the last immediate operand found.
// This could lead to unexpected results if the instruction
// has more than one immediate with the same value.
if (Inst.getOperand(ImmOpNo).getImm() == Value)
break;
}
}
if (ImmOpNo == -1U)
return false;
Value = Inst.getOperand(ImmOpNo).getImm();
setOperandToSymbolRef(Inst, ImmOpNo, Symbol, Addend, Ctx, RelType);
return true;
}
bool replaceRegWithImm(MCInst &Inst, unsigned Register,
int64_t Imm) const override {
enum CheckSignExt : uint8_t {
NOCHECK = 0,
CHECK8,
CHECK32,
};
using CheckList = std::vector<std::pair<CheckSignExt, unsigned>>;
struct InstInfo {
// Size in bytes that Inst loads from memory.
uint8_t DataSize;
// True when the target operand has to be duplicated because the opcode
// expects a LHS operand.
bool HasLHS;
// List of checks and corresponding opcodes to be used. We try to use the
// smallest possible immediate value when various sizes are available,
// hence we may need to check whether a larger constant fits in a smaller
// immediate.
CheckList Checks;
};
InstInfo I;
switch (Inst.getOpcode()) {
default: {
switch (getPushSize(Inst)) {
case 2: I = {2, false, {{CHECK8, X86::PUSH16i8}, {NOCHECK, X86::PUSHi16}}}; break;
case 4: I = {4, false, {{CHECK8, X86::PUSH32i8}, {NOCHECK, X86::PUSHi32}}}; break;
case 8: I = {8, false, {{CHECK8, X86::PUSH64i8},
{CHECK32, X86::PUSH64i32},
{NOCHECK, Inst.getOpcode()}}}; break;
default: return false;
}
break;
}
// MOV
case X86::MOV8rr: I = {1, false, {{NOCHECK, X86::MOV8ri}}}; break;
case X86::MOV16rr: I = {2, false, {{NOCHECK, X86::MOV16ri}}}; break;
case X86::MOV32rr: I = {4, false, {{NOCHECK, X86::MOV32ri}}}; break;
case X86::MOV64rr: I = {8, false, {{CHECK32, X86::MOV64ri32},
{NOCHECK, X86::MOV64ri}}}; break;
case X86::MOV8mr: I = {1, false, {{NOCHECK, X86::MOV8mi}}}; break;
case X86::MOV16mr: I = {2, false, {{NOCHECK, X86::MOV16mi}}}; break;
case X86::MOV32mr: I = {4, false, {{NOCHECK, X86::MOV32mi}}}; break;
case X86::MOV64mr: I = {8, false, {{CHECK32, X86::MOV64mi32},
{NOCHECK, X86::MOV64mr}}}; break;
// MOVZX
case X86::MOVZX16rr8: I = {1, false, {{NOCHECK, X86::MOV16ri}}}; break;
case X86::MOVZX32rr8: I = {1, false, {{NOCHECK, X86::MOV32ri}}}; break;
case X86::MOVZX32rr16: I = {2, false, {{NOCHECK, X86::MOV32ri}}}; break;
// CMP
case X86::CMP8rr: I = {1, false, {{NOCHECK, X86::CMP8ri}}}; break;
case X86::CMP16rr: I = {2, false, {{CHECK8, X86::CMP16ri8},
{NOCHECK, X86::CMP16ri}}}; break;
case X86::CMP32rr: I = {4, false, {{CHECK8, X86::CMP32ri8},
{NOCHECK, X86::CMP32ri}}}; break;
case X86::CMP64rr: I = {8, false, {{CHECK8, X86::CMP64ri8},
{CHECK32, X86::CMP64ri32},
{NOCHECK, X86::CMP64rr}}}; break;
// TEST
case X86::TEST8rr: I = {1, false, {{NOCHECK, X86::TEST8ri}}}; break;
case X86::TEST16rr: I = {2, false, {{NOCHECK, X86::TEST16ri}}}; break;
case X86::TEST32rr: I = {4, false, {{NOCHECK, X86::TEST32ri}}}; break;
case X86::TEST64rr: I = {8, false, {{CHECK32, X86::TEST64ri32},
{NOCHECK, X86::TEST64rr}}}; break;
// ADD
case X86::ADD8rr: I = {1, true, {{NOCHECK, X86::ADD8ri}}}; break;
case X86::ADD16rr: I = {2, true, {{CHECK8, X86::ADD16ri8},
{NOCHECK, X86::ADD16ri}}}; break;
case X86::ADD32rr: I = {4, true, {{CHECK8, X86::ADD32ri8},
{NOCHECK, X86::ADD32ri}}}; break;
case X86::ADD64rr: I = {8, true, {{CHECK8, X86::ADD64ri8},
{CHECK32, X86::ADD64ri32},
{NOCHECK, X86::ADD64rr}}}; break;
// SUB
case X86::SUB8rr: I = {1, true, {{NOCHECK, X86::SUB8ri}}}; break;
case X86::SUB16rr: I = {2, true, {{CHECK8, X86::SUB16ri8},
{NOCHECK, X86::SUB16ri}}}; break;
case X86::SUB32rr: I = {4, true, {{CHECK8, X86::SUB32ri8},
{NOCHECK, X86::SUB32ri}}}; break;
case X86::SUB64rr: I = {8, true, {{CHECK8, X86::SUB64ri8},
{CHECK32, X86::SUB64ri32},
{NOCHECK, X86::SUB64rr}}}; break;
// AND
case X86::AND8rr: I = {1, true, {{NOCHECK, X86::AND8ri}}}; break;
case X86::AND16rr: I = {2, true, {{CHECK8, X86::AND16ri8},
{NOCHECK, X86::AND16ri}}}; break;
case X86::AND32rr: I = {4, true, {{CHECK8, X86::AND32ri8},
{NOCHECK, X86::AND32ri}}}; break;
case X86::AND64rr: I = {8, true, {{CHECK8, X86::AND64ri8},
{CHECK32, X86::AND64ri32},
{NOCHECK, X86::AND64rr}}}; break;
// OR
case X86::OR8rr: I = {1, true, {{NOCHECK, X86::OR8ri}}}; break;
case X86::OR16rr: I = {2, true, {{CHECK8, X86::OR16ri8},
{NOCHECK, X86::OR16ri}}}; break;
case X86::OR32rr: I = {4, true, {{CHECK8, X86::OR32ri8},
{NOCHECK, X86::OR32ri}}}; break;
case X86::OR64rr: I = {8, true, {{CHECK8, X86::OR64ri8},
{CHECK32, X86::OR64ri32},
{NOCHECK, X86::OR64rr}}}; break;
// XOR
case X86::XOR8rr: I = {1, true, {{NOCHECK, X86::XOR8ri}}}; break;
case X86::XOR16rr: I = {2, true, {{CHECK8, X86::XOR16ri8},
{NOCHECK, X86::XOR16ri}}}; break;
case X86::XOR32rr: I = {4, true, {{CHECK8, X86::XOR32ri8},
{NOCHECK, X86::XOR32ri}}}; break;
case X86::XOR64rr: I = {8, true, {{CHECK8, X86::XOR64ri8},
{CHECK32, X86::XOR64ri32},
{NOCHECK, X86::XOR64rr}}}; break;
}
// Compute the new opcode.
unsigned NewOpcode = 0;
for (const std::pair<CheckSignExt, unsigned> &Check : I.Checks) {
NewOpcode = Check.second;
if (Check.first == NOCHECK)
break;
if (Check.first == CHECK8 && isInt<8>(Imm))
break;
if (Check.first == CHECK32 && isInt<32>(Imm))
break;
}
if (NewOpcode == Inst.getOpcode())
return false;
const MCInstrDesc &InstDesc = Info->get(Inst.getOpcode());
unsigned NumFound = 0;
for (unsigned Index = InstDesc.getNumDefs() + (I.HasLHS ? 1 : 0),
E = InstDesc.getNumOperands();
Index != E; ++Index)
if (Inst.getOperand(Index).isReg() &&
Inst.getOperand(Index).getReg() == Register)
NumFound++;
if (NumFound != 1)
return false;
MCOperand TargetOp = Inst.getOperand(0);
Inst.clear();
Inst.setOpcode(NewOpcode);
Inst.addOperand(TargetOp);
if (I.HasLHS)
Inst.addOperand(TargetOp);
Inst.addOperand(MCOperand::createImm(Imm));
return true;
}
bool replaceRegWithReg(MCInst &Inst, unsigned ToReplace,
unsigned ReplaceWith) const override {
// Get the HasLHS value so that iteration can be done
bool HasLHS;
if (X86::isAND(Inst.getOpcode()) || X86::isADD(Inst.getOpcode()) ||
X86::isSUB(Inst.getOpcode())) {
HasLHS = true;
} else if (isPop(Inst) || isPush(Inst) || X86::isCMP(Inst.getOpcode()) ||
X86::isTEST(Inst.getOpcode())) {
HasLHS = false;
} else {
switch (Inst.getOpcode()) {
case X86::MOV8rr:
case X86::MOV8rm:
case X86::MOV8mr:
case X86::MOV8ri:
case X86::MOV16rr:
case X86::MOV16rm:
case X86::MOV16mr:
case X86::MOV16ri:
case X86::MOV32rr:
case X86::MOV32rm:
case X86::MOV32mr:
case X86::MOV32ri:
case X86::MOV64rr:
case X86::MOV64rm:
case X86::MOV64mr:
case X86::MOV64ri:
case X86::MOVZX16rr8:
case X86::MOVZX32rr8:
case X86::MOVZX32rr16:
case X86::MOVSX32rm8:
case X86::MOVSX32rr8:
case X86::MOVSX64rm32:
case X86::LEA64r:
HasLHS = false;
break;
default:
return false;
}
}
const MCInstrDesc &InstDesc = Info->get(Inst.getOpcode());
bool FoundOne = false;
// Iterate only through src operands that arent also dest operands
for (unsigned Index = InstDesc.getNumDefs() + (HasLHS ? 1 : 0),
E = InstDesc.getNumOperands();
Index != E; ++Index) {
BitVector RegAliases = getAliases(ToReplace, true);
if (!Inst.getOperand(Index).isReg() ||
!RegAliases.test(Inst.getOperand(Index).getReg()))
continue;
// Resize register if needed
unsigned SizedReplaceWith = getAliasSized(
ReplaceWith, getRegSize(Inst.getOperand(Index).getReg()));
MCOperand NewOperand = MCOperand::createReg(SizedReplaceWith);
Inst.getOperand(Index) = NewOperand;
FoundOne = true;
}
// Return true if at least one operand was replaced
return FoundOne;
}
bool createUncondBranch(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
Inst.setOpcode(X86::JMP_1);
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx)));
return true;
}
bool createCall(MCInst &Inst, const MCSymbol *Target,
MCContext *Ctx) override {
Inst.setOpcode(X86::CALL64pcrel32);
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx)));
return true;
}
bool createTailCall(MCInst &Inst, const MCSymbol *Target,
MCContext *Ctx) override {
return createDirectCall(Inst, Target, Ctx, /*IsTailCall*/ true);
}
void createLongTailCall(InstructionListType &Seq, const MCSymbol *Target,
MCContext *Ctx) override {
Seq.clear();
Seq.emplace_back();
createDirectCall(Seq.back(), Target, Ctx, /*IsTailCall*/ true);
}
bool createTrap(MCInst &Inst) const override {
Inst.clear();
Inst.setOpcode(X86::TRAP);
return true;
}
bool reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
unsigned InvCC = getInvertedCondCode(getCondCode(Inst));
assert(InvCC != X86::COND_INVALID && "invalid branch instruction");
Inst.getOperand(Info->get(Inst.getOpcode()).NumOperands - 1).setImm(InvCC);
Inst.getOperand(0) = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}
bool replaceBranchCondition(MCInst &Inst, const MCSymbol *TBB, MCContext *Ctx,
unsigned CC) const override {
if (CC == X86::COND_INVALID)
return false;
Inst.getOperand(Info->get(Inst.getOpcode()).NumOperands - 1).setImm(CC);
Inst.getOperand(0) = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}
unsigned getCanonicalBranchCondCode(unsigned CC) const override {
switch (CC) {
default: return X86::COND_INVALID;
case X86::COND_E: return X86::COND_E;
case X86::COND_NE: return X86::COND_E;
case X86::COND_L: return X86::COND_L;
case X86::COND_GE: return X86::COND_L;
case X86::COND_LE: return X86::COND_G;
case X86::COND_G: return X86::COND_G;
case X86::COND_B: return X86::COND_B;
case X86::COND_AE: return X86::COND_B;
case X86::COND_BE: return X86::COND_A;
case X86::COND_A: return X86::COND_A;
case X86::COND_S: return X86::COND_S;
case X86::COND_NS: return X86::COND_S;
case X86::COND_P: return X86::COND_P;
case X86::COND_NP: return X86::COND_P;
case X86::COND_O: return X86::COND_O;
case X86::COND_NO: return X86::COND_O;
}
}
bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
MCContext *Ctx) const override {
assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) &&
"Invalid instruction");
Inst.getOperand(0) = MCOperand::createExpr(
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
return true;
}
MCPhysReg getX86R11() const override { return X86::R11; }
MCPhysReg getIntArgRegister(unsigned ArgNo) const override {
// FIXME: this should depend on the calling convention.
switch (ArgNo) {
case 0: return X86::RDI;
case 1: return X86::RSI;
case 2: return X86::RDX;
case 3: return X86::RCX;
case 4: return X86::R8;
case 5: return X86::R9;
default: return getNoRegister();
}
}
void createPause(MCInst &Inst) const override {
Inst.clear();
Inst.setOpcode(X86::PAUSE);
}
void createLfence(MCInst &Inst) const override {
Inst.clear();
Inst.setOpcode(X86::LFENCE);
}
bool createDirectCall(MCInst &Inst, const MCSymbol *Target, MCContext *Ctx,
bool IsTailCall) override {
Inst.clear();
Inst.setOpcode(IsTailCall ? X86::JMP_4 : X86::CALL64pcrel32);
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx)));
if (IsTailCall)
setTailCall(Inst);
return true;
}
void createShortJmp(InstructionListType &Seq, const MCSymbol *Target,
MCContext *Ctx, bool IsTailCall) override {
Seq.clear();
MCInst Inst;
Inst.setOpcode(X86::JMP_1);
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx)));
if (IsTailCall)
setTailCall(Inst);
Seq.emplace_back(Inst);
}
bool isConditionalMove(const MCInst &Inst) const override {
unsigned OpCode = Inst.getOpcode();
return (OpCode == X86::CMOV16rr || OpCode == X86::CMOV32rr ||
OpCode == X86::CMOV64rr);
}
bool isBranchOnMem(const MCInst &Inst) const override {
unsigned OpCode = Inst.getOpcode();
if (OpCode == X86::CALL64m || (OpCode == X86::JMP32m && isTailCall(Inst)) ||
OpCode == X86::JMP64m)
return true;
return false;
}
bool isBranchOnReg(const MCInst &Inst) const override {
unsigned OpCode = Inst.getOpcode();
if (OpCode == X86::CALL64r || (OpCode == X86::JMP32r && isTailCall(Inst)) ||
OpCode == X86::JMP64r)
return true;
return false;
}
void createPushRegister(MCInst &Inst, MCPhysReg Reg,
unsigned Size) const override {
Inst.clear();
unsigned NewOpcode = 0;
if (Reg == X86::EFLAGS) {
switch (Size) {
case 2: NewOpcode = X86::PUSHF16; break;
case 4: NewOpcode = X86::PUSHF32; break;
case 8: NewOpcode = X86::PUSHF64; break;
default:
llvm_unreachable("Unexpected size");
}
Inst.setOpcode(NewOpcode);
return;
}
switch (Size) {
case 2: NewOpcode = X86::PUSH16r; break;
case 4: NewOpcode = X86::PUSH32r; break;
case 8: NewOpcode = X86::PUSH64r; break;
default:
llvm_unreachable("Unexpected size");
}
Inst.setOpcode(NewOpcode);
Inst.addOperand(MCOperand::createReg(Reg));
}
void createPopRegister(MCInst &Inst, MCPhysReg Reg,
unsigned Size) const override {
Inst.clear();
unsigned NewOpcode = 0;
if (Reg == X86::EFLAGS) {
switch (Size) {
case 2: NewOpcode = X86::POPF16; break;
case 4: NewOpcode = X86::POPF32; break;
case 8: NewOpcode = X86::POPF64; break;
default:
llvm_unreachable("Unexpected size");
}
Inst.setOpcode(NewOpcode);
return;
}
switch (Size) {
case 2: NewOpcode = X86::POP16r; break;
case 4: NewOpcode = X86::POP32r; break;
case 8: NewOpcode = X86::POP64r; break;
default:
llvm_unreachable("Unexpected size");
}
Inst.setOpcode(NewOpcode);
Inst.addOperand(MCOperand::createReg(Reg));
}
void createPushFlags(MCInst &Inst, unsigned Size) const override {
return createPushRegister(Inst, X86::EFLAGS, Size);
}
void createPopFlags(MCInst &Inst, unsigned Size) const override {
return createPopRegister(Inst, X86::EFLAGS, Size);
}
void createAddRegImm(MCInst &Inst, MCPhysReg Reg, int64_t Value,
unsigned Size) const {
unsigned int Opcode;
switch (Size) {
case 1: Opcode = X86::ADD8ri; break;
case 2: Opcode = X86::ADD16ri; break;
case 4: Opcode = X86::ADD32ri; break;
default:
llvm_unreachable("Unexpected size");
}
Inst.setOpcode(Opcode);
Inst.clear();
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createImm(Value));
}
void createClearRegWithNoEFlagsUpdate(MCInst &Inst, MCPhysReg Reg,
unsigned Size) const {
unsigned int Opcode;
switch (Size) {
case 1: Opcode = X86::MOV8ri; break;
case 2: Opcode = X86::MOV16ri; break;
case 4: Opcode = X86::MOV32ri; break;
// Writing to a 32-bit register always zeros the upper 32 bits of the
// full-width register
case 8:
Opcode = X86::MOV32ri;
Reg = getAliasSized(Reg, 4);
break;
default:
llvm_unreachable("Unexpected size");
}
Inst.setOpcode(Opcode);
Inst.clear();
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createImm(0));
}
void createX86SaveOVFlagToRegister(MCInst &Inst, MCPhysReg Reg) const {
Inst.setOpcode(X86::SETCCr);
Inst.clear();
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createImm(X86::COND_O));
}
void createX86Lahf(MCInst &Inst) const {
Inst.setOpcode(X86::LAHF);
Inst.clear();
}
void createX86Sahf(MCInst &Inst) const {
Inst.setOpcode(X86::SAHF);
Inst.clear();
}
void createInstrIncMemory(InstructionListType &Instrs, const MCSymbol *Target,
MCContext *Ctx, bool IsLeaf) const override {
unsigned int I = 0;
Instrs.resize(IsLeaf ? 13 : 11);
// Don't clobber application red zone (ABI dependent)
if (IsLeaf)
createStackPointerIncrement(Instrs[I++], 128,
/*NoFlagsClobber=*/true);
// Performance improvements based on the optimization discussed at
// https://reviews.llvm.org/D6629
// LAHF/SAHF are used instead of PUSHF/POPF
// PUSHF
createPushRegister(Instrs[I++], X86::RAX, 8);
createClearRegWithNoEFlagsUpdate(Instrs[I++], X86::RAX, 8);
createX86Lahf(Instrs[I++]);
createPushRegister(Instrs[I++], X86::RAX, 8);
createClearRegWithNoEFlagsUpdate(Instrs[I++], X86::RAX, 8);
createX86SaveOVFlagToRegister(Instrs[I++], X86::AL);
// LOCK INC
createIncMemory(Instrs[I++], Target, Ctx);
// POPF
createAddRegImm(Instrs[I++], X86::AL, 127, 1);
createPopRegister(Instrs[I++], X86::RAX, 8);
createX86Sahf(Instrs[I++]);
createPopRegister(Instrs[I++], X86::RAX, 8);
if (IsLeaf)
createStackPointerDecrement(Instrs[I], 128,
/*NoFlagsClobber=*/true);
}
void createSwap(MCInst &Inst, MCPhysReg Source, MCPhysReg MemBaseReg,
int64_t Disp) const {
Inst.setOpcode(X86::XCHG64rm);
Inst.addOperand(MCOperand::createReg(Source));
Inst.addOperand(MCOperand::createReg(Source));
Inst.addOperand(MCOperand::createReg(MemBaseReg)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createImm(Disp)); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
}
void createIndirectBranch(MCInst &Inst, MCPhysReg MemBaseReg,
int64_t Disp) const {
Inst.setOpcode(X86::JMP64m);
Inst.addOperand(MCOperand::createReg(MemBaseReg)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createImm(Disp)); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
}
InstructionListType createInstrumentedIndirectCall(const MCInst &CallInst,
bool TailCall,
MCSymbol *HandlerFuncAddr,
int CallSiteID,
MCContext *Ctx) override {
// Check if the target address expression used in the original indirect call
// uses the stack pointer, which we are going to clobber.
static BitVector SPAliases(getAliases(X86::RSP));
bool UsesSP = false;
// Skip defs.
for (unsigned I = Info->get(CallInst.getOpcode()).getNumDefs(),
E = MCPlus::getNumPrimeOperands(CallInst);
I != E; ++I) {
const MCOperand &Operand = CallInst.getOperand(I);
if (Operand.isReg() && SPAliases[Operand.getReg()]) {
UsesSP = true;
break;
}
}
InstructionListType Insts;
MCPhysReg TempReg = getIntArgRegister(0);
// Code sequence used to enter indirect call instrumentation helper:
// push %rdi
// add $8, %rsp ;; $rsp may be used in target, so fix it to prev val
// movq target, %rdi ;; via convertIndirectCallTargetToLoad
// sub $8, %rsp ;; restore correct stack value
// push %rdi
// movq $CallSiteID, %rdi
// push %rdi
// callq/jmp HandlerFuncAddr
Insts.emplace_back();
createPushRegister(Insts.back(), TempReg, 8);
if (UsesSP) { // Only adjust SP if we really need to
Insts.emplace_back();
createStackPointerDecrement(Insts.back(), 8, /*NoFlagsClobber=*/false);
}
Insts.emplace_back(CallInst);
// Insts.back() and CallInst now share the same annotation instruction.
// Strip it from Insts.back(), only preserving tail call annotation.
stripAnnotations(Insts.back(), /*KeepTC=*/true);
convertIndirectCallToLoad(Insts.back(), TempReg);
if (UsesSP) {
Insts.emplace_back();
createStackPointerIncrement(Insts.back(), 8, /*NoFlagsClobber=*/false);
}
Insts.emplace_back();
createPushRegister(Insts.back(), TempReg, 8);
Insts.emplace_back();
createLoadImmediate(Insts.back(), TempReg, CallSiteID);
Insts.emplace_back();
createPushRegister(Insts.back(), TempReg, 8);
Insts.emplace_back();
createDirectCall(Insts.back(), HandlerFuncAddr, Ctx,
/*TailCall=*/TailCall);
// Carry over metadata
for (int I = MCPlus::getNumPrimeOperands(CallInst),
E = CallInst.getNumOperands();
I != E; ++I)
Insts.back().addOperand(CallInst.getOperand(I));
return Insts;
}
InstructionListType createInstrumentedIndCallHandlerExitBB() const override {
const MCPhysReg TempReg = getIntArgRegister(0);
// We just need to undo the sequence created for every ind call in
// instrumentIndirectTarget(), which can be accomplished minimally with:
// popfq
// pop %rdi
// add $16, %rsp
// xchg (%rsp), %rdi
// jmp *-8(%rsp)
InstructionListType Insts(5);
createPopFlags(Insts[0], 8);
createPopRegister(Insts[1], TempReg, 8);
createStackPointerDecrement(Insts[2], 16, /*NoFlagsClobber=*/false);
createSwap(Insts[3], TempReg, X86::RSP, 0);
createIndirectBranch(Insts[4], X86::RSP, -8);
return Insts;
}
InstructionListType
createInstrumentedIndTailCallHandlerExitBB() const override {
const MCPhysReg TempReg = getIntArgRegister(0);
// Same thing as above, but for tail calls
// popfq
// add $16, %rsp
// pop %rdi
// jmp *-16(%rsp)
InstructionListType Insts(4);
createPopFlags(Insts[0], 8);
createStackPointerDecrement(Insts[1], 16, /*NoFlagsClobber=*/false);
createPopRegister(Insts[2], TempReg, 8);
createIndirectBranch(Insts[3], X86::RSP, -16);
return Insts;
}
InstructionListType
createInstrumentedIndCallHandlerEntryBB(const MCSymbol *InstrTrampoline,
const MCSymbol *IndCallHandler,
MCContext *Ctx) override {
const MCPhysReg TempReg = getIntArgRegister(0);
// Code sequence used to check whether InstrTampoline was initialized
// and call it if so, returns via IndCallHandler.
// pushfq
// mov InstrTrampoline,%rdi
// cmp $0x0,%rdi
// je IndCallHandler
// callq *%rdi
// jmpq IndCallHandler
InstructionListType Insts;
Insts.emplace_back();
createPushFlags(Insts.back(), 8);
Insts.emplace_back();
createMove(Insts.back(), InstrTrampoline, TempReg, Ctx);
InstructionListType cmpJmp = createCmpJE(TempReg, 0, IndCallHandler, Ctx);
Insts.insert(Insts.end(), cmpJmp.begin(), cmpJmp.end());
Insts.emplace_back();
Insts.back().setOpcode(X86::CALL64r);
Insts.back().addOperand(MCOperand::createReg(TempReg));
Insts.emplace_back();
createDirectCall(Insts.back(), IndCallHandler, Ctx, /*IsTailCall*/ true);
return Insts;
}
InstructionListType createNumCountersGetter(MCContext *Ctx) const override {
InstructionListType Insts(2);
MCSymbol *NumLocs = Ctx->getOrCreateSymbol("__bolt_num_counters");
createMove(Insts[0], NumLocs, X86::EAX, Ctx);
createReturn(Insts[1]);
return Insts;
}
InstructionListType
createInstrLocationsGetter(MCContext *Ctx) const override {
InstructionListType Insts(2);
MCSymbol *Locs = Ctx->getOrCreateSymbol("__bolt_instr_locations");
createLea(Insts[0], Locs, X86::EAX, Ctx);
createReturn(Insts[1]);
return Insts;
}
InstructionListType createInstrTablesGetter(MCContext *Ctx) const override {
InstructionListType Insts(2);
MCSymbol *Locs = Ctx->getOrCreateSymbol("__bolt_instr_tables");
createLea(Insts[0], Locs, X86::EAX, Ctx);
createReturn(Insts[1]);
return Insts;
}
InstructionListType createInstrNumFuncsGetter(MCContext *Ctx) const override {
InstructionListType Insts(2);
MCSymbol *NumFuncs = Ctx->getOrCreateSymbol("__bolt_instr_num_funcs");
createMove(Insts[0], NumFuncs, X86::EAX, Ctx);
createReturn(Insts[1]);
return Insts;
}
InstructionListType createSymbolTrampoline(const MCSymbol *TgtSym,
MCContext *Ctx) const override {
InstructionListType Insts(1);
createUncondBranch(Insts[0], TgtSym, Ctx);
return Insts;
}
InstructionListType createDummyReturnFunction(MCContext *Ctx) const override {
InstructionListType Insts(1);
createReturn(Insts[0]);
return Insts;
}
BlocksVectorTy indirectCallPromotion(
const MCInst &CallInst,
const std::vector<std::pair<MCSymbol *, uint64_t>> &Targets,
const std::vector<std::pair<MCSymbol *, uint64_t>> &VtableSyms,
const std::vector<MCInst *> &MethodFetchInsns,
const bool MinimizeCodeSize, MCContext *Ctx) override {
const bool IsTailCall = isTailCall(CallInst);
const bool IsJumpTable = getJumpTable(CallInst) != 0;
BlocksVectorTy Results;
// Label for the current code block.
MCSymbol *NextTarget = nullptr;
// The join block which contains all the instructions following CallInst.
// MergeBlock remains null if CallInst is a tail call.
MCSymbol *MergeBlock = nullptr;
unsigned FuncAddrReg = X86::R10;
const bool LoadElim = !VtableSyms.empty();
assert((!LoadElim || VtableSyms.size() == Targets.size()) &&
"There must be a vtable entry for every method "
"in the targets vector.");
if (MinimizeCodeSize && !LoadElim) {
std::set<unsigned> UsedRegs;
for (unsigned int I = 0; I < MCPlus::getNumPrimeOperands(CallInst); ++I) {
const MCOperand &Op = CallInst.getOperand(I);
if (Op.isReg())
UsedRegs.insert(Op.getReg());
}
if (UsedRegs.count(X86::R10) == 0)
FuncAddrReg = X86::R10;
else if (UsedRegs.count(X86::R11) == 0)
FuncAddrReg = X86::R11;
else
return Results;
}
const auto jumpToMergeBlock = [&](InstructionListType &NewCall) {
assert(MergeBlock);
NewCall.push_back(CallInst);
MCInst &Merge = NewCall.back();
Merge.clear();
createUncondBranch(Merge, MergeBlock, Ctx);
};
for (unsigned int i = 0; i < Targets.size(); ++i) {
Results.emplace_back(NextTarget, InstructionListType());
InstructionListType *NewCall = &Results.back().second;
if (MinimizeCodeSize && !LoadElim) {
// Load the call target into FuncAddrReg.
NewCall->push_back(CallInst); // Copy CallInst in order to get SMLoc
MCInst &Target = NewCall->back();
Target.clear();
Target.setOpcode(X86::MOV64ri32);
Target.addOperand(MCOperand::createReg(FuncAddrReg));
if (Targets[i].first) {
// Is this OK?
Target.addOperand(MCOperand::createExpr(MCSymbolRefExpr::create(
Targets[i].first, MCSymbolRefExpr::VK_None, *Ctx)));
} else {
const uint64_t Addr = Targets[i].second;
// Immediate address is out of sign extended 32 bit range.
if (int64_t(Addr) != int64_t(int32_t(Addr)))
return BlocksVectorTy();
Target.addOperand(MCOperand::createImm(Addr));
}
// Compare current call target to a specific address.
NewCall->push_back(CallInst);
MCInst &Compare = NewCall->back();
Compare.clear();
if (isBranchOnReg(CallInst))
Compare.setOpcode(X86::CMP64rr);
else if (CallInst.getOpcode() == X86::CALL64pcrel32)
Compare.setOpcode(X86::CMP64ri32);
else
Compare.setOpcode(X86::CMP64rm);
Compare.addOperand(MCOperand::createReg(FuncAddrReg));
// TODO: Would be preferable to only load this value once.
for (unsigned i = 0;
i < Info->get(CallInst.getOpcode()).getNumOperands(); ++i)
if (!CallInst.getOperand(i).isInst())
Compare.addOperand(CallInst.getOperand(i));
} else {
// Compare current call target to a specific address.
NewCall->push_back(CallInst);
MCInst &Compare = NewCall->back();
Compare.clear();
if (isBranchOnReg(CallInst))
Compare.setOpcode(X86::CMP64ri32);
else
Compare.setOpcode(X86::CMP64mi32);
// Original call address.
for (unsigned i = 0;
i < Info->get(CallInst.getOpcode()).getNumOperands(); ++i)
if (!CallInst.getOperand(i).isInst())
Compare.addOperand(CallInst.getOperand(i));
// Target address.
if (Targets[i].first || LoadElim) {
const MCSymbol *Sym =
LoadElim ? VtableSyms[i].first : Targets[i].first;
const uint64_t Addend = LoadElim ? VtableSyms[i].second : 0;
const MCExpr *Expr = MCSymbolRefExpr::create(Sym, *Ctx);
if (Addend)
Expr = MCBinaryExpr::createAdd(
Expr, MCConstantExpr::create(Addend, *Ctx), *Ctx);
Compare.addOperand(MCOperand::createExpr(Expr));
} else {
const uint64_t Addr = Targets[i].second;
// Immediate address is out of sign extended 32 bit range.
if (int64_t(Addr) != int64_t(int32_t(Addr)))
return BlocksVectorTy();
Compare.addOperand(MCOperand::createImm(Addr));
}
}
// jump to next target compare.
NextTarget =
Ctx->createNamedTempSymbol(); // generate label for the next block
NewCall->push_back(CallInst);
if (IsJumpTable) {
MCInst &Je = NewCall->back();
// Jump to next compare if target addresses don't match.
Je.clear();
Je.setOpcode(X86::JCC_1);
if (Targets[i].first)
Je.addOperand(MCOperand::createExpr(MCSymbolRefExpr::create(
Targets[i].first, MCSymbolRefExpr::VK_None, *Ctx)));
else
Je.addOperand(MCOperand::createImm(Targets[i].second));
Je.addOperand(MCOperand::createImm(X86::COND_E));
assert(!isInvoke(CallInst));
} else {
MCInst &Jne = NewCall->back();
// Jump to next compare if target addresses don't match.
Jne.clear();
Jne.setOpcode(X86::JCC_1);
Jne.addOperand(MCOperand::createExpr(MCSymbolRefExpr::create(
NextTarget, MCSymbolRefExpr::VK_None, *Ctx)));
Jne.addOperand(MCOperand::createImm(X86::COND_NE));
// Call specific target directly.
Results.emplace_back(Ctx->createNamedTempSymbol(),
InstructionListType());
NewCall = &Results.back().second;
NewCall->push_back(CallInst);
MCInst &CallOrJmp = NewCall->back();
CallOrJmp.clear();
if (MinimizeCodeSize && !LoadElim) {
CallOrJmp.setOpcode(IsTailCall ? X86::JMP32r : X86::CALL64r);
CallOrJmp.addOperand(MCOperand::createReg(FuncAddrReg));
} else {
CallOrJmp.setOpcode(IsTailCall ? X86::JMP_4 : X86::CALL64pcrel32);
if (Targets[i].first)
CallOrJmp.addOperand(MCOperand::createExpr(MCSymbolRefExpr::create(
Targets[i].first, MCSymbolRefExpr::VK_None, *Ctx)));
else
CallOrJmp.addOperand(MCOperand::createImm(Targets[i].second));
}
if (IsTailCall)
setTailCall(CallOrJmp);
if (CallOrJmp.getOpcode() == X86::CALL64r ||
CallOrJmp.getOpcode() == X86::CALL64pcrel32) {
if (std::optional<uint32_t> Offset = getOffset(CallInst))
// Annotated as duplicated call
setOffset(CallOrJmp, *Offset);
}
if (isInvoke(CallInst) && !isInvoke(CallOrJmp)) {
// Copy over any EH or GNU args size information from the original
// call.
std::optional<MCPlus::MCLandingPad> EHInfo = getEHInfo(CallInst);
if (EHInfo)
addEHInfo(CallOrJmp, *EHInfo);
int64_t GnuArgsSize = getGnuArgsSize(CallInst);
if (GnuArgsSize >= 0)
addGnuArgsSize(CallOrJmp, GnuArgsSize);
}
if (!IsTailCall) {
// The fallthrough block for the most common target should be
// the merge block.
if (i == 0) {
// Fallthrough to merge block.
MergeBlock = Ctx->createNamedTempSymbol();
} else {
// Insert jump to the merge block if we are not doing a fallthrough.
jumpToMergeBlock(*NewCall);
}
}
}
}
// Cold call block.
Results.emplace_back(NextTarget, InstructionListType());
InstructionListType &NewCall = Results.back().second;
for (const MCInst *Inst : MethodFetchInsns)
if (Inst != &CallInst)
NewCall.push_back(*Inst);
NewCall.push_back(CallInst);
// Jump to merge block from cold call block
if (!IsTailCall && !IsJumpTable) {
jumpToMergeBlock(NewCall);
// Record merge block
Results.emplace_back(MergeBlock, InstructionListType());
}
return Results;
}
BlocksVectorTy jumpTablePromotion(
const MCInst &IJmpInst,
const std::vector<std::pair<MCSymbol *, uint64_t>> &Targets,
const std::vector<MCInst *> &TargetFetchInsns,
MCContext *Ctx) const override {
assert(getJumpTable(IJmpInst) != 0);
uint16_t IndexReg = getAnnotationAs<uint16_t>(IJmpInst, "JTIndexReg");
if (IndexReg == 0)
return BlocksVectorTy();
BlocksVectorTy Results;
// Label for the current code block.
MCSymbol *NextTarget = nullptr;
for (unsigned int i = 0; i < Targets.size(); ++i) {
Results.emplace_back(NextTarget, InstructionListType());
InstructionListType *CurBB = &Results.back().second;
// Compare current index to a specific index.
CurBB->emplace_back(MCInst());
MCInst &CompareInst = CurBB->back();
CompareInst.setLoc(IJmpInst.getLoc());
CompareInst.setOpcode(X86::CMP64ri32);
CompareInst.addOperand(MCOperand::createReg(IndexReg));
const uint64_t CaseIdx = Targets[i].second;
// Immediate address is out of sign extended 32 bit range.
if (int64_t(CaseIdx) != int64_t(int32_t(CaseIdx)))
return BlocksVectorTy();
CompareInst.addOperand(MCOperand::createImm(CaseIdx));
shortenInstruction(CompareInst, *Ctx->getSubtargetInfo());
// jump to next target compare.
NextTarget =
Ctx->createNamedTempSymbol(); // generate label for the next block
CurBB->push_back(MCInst());
MCInst &JEInst = CurBB->back();
JEInst.setLoc(IJmpInst.getLoc());
// Jump to target if indices match
JEInst.setOpcode(X86::JCC_1);
JEInst.addOperand(MCOperand::createExpr(MCSymbolRefExpr::create(
Targets[i].first, MCSymbolRefExpr::VK_None, *Ctx)));
JEInst.addOperand(MCOperand::createImm(X86::COND_E));
}
// Cold call block.
Results.emplace_back(NextTarget, InstructionListType());
InstructionListType &CurBB = Results.back().second;
for (const MCInst *Inst : TargetFetchInsns)
if (Inst != &IJmpInst)
CurBB.push_back(*Inst);
CurBB.push_back(IJmpInst);
return Results;
}
private:
bool createMove(MCInst &Inst, const MCSymbol *Src, unsigned Reg,
MCContext *Ctx) const {
Inst.setOpcode(X86::MOV64rm);
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createReg(X86::RIP)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Src, MCSymbolRefExpr::VK_None,
*Ctx))); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
return true;
}
bool createLea(MCInst &Inst, const MCSymbol *Src, unsigned Reg,
MCContext *Ctx) const {
Inst.setOpcode(X86::LEA64r);
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createReg(X86::RIP)); // BaseReg
Inst.addOperand(MCOperand::createImm(1)); // ScaleAmt
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // IndexReg
Inst.addOperand(MCOperand::createExpr(
MCSymbolRefExpr::create(Src, MCSymbolRefExpr::VK_None,
*Ctx))); // Displacement
Inst.addOperand(MCOperand::createReg(X86::NoRegister)); // AddrSegmentReg
return true;
}
};
} // namespace
namespace llvm {
namespace bolt {
MCPlusBuilder *createX86MCPlusBuilder(const MCInstrAnalysis *Analysis,
const MCInstrInfo *Info,
const MCRegisterInfo *RegInfo) {
return new X86MCPlusBuilder(Analysis, Info, RegInfo);
}
} // namespace bolt
} // namespace llvm