forked from OSchip/llvm-project
[BOLT] Added Constant and Copy Propagation to tail duplicated blocks
Summary: Added a function in TailDuplication that will do Constant and Copy Propagation for blocks that we duplicated as a part of tail duplication. Added supporting functions to MCPlusBuilder to find src registers and replace registers (cherry picked from FBD30231907)
This commit is contained in:
parent
2a5790b670
commit
ef6186c822
|
@ -369,6 +369,42 @@ void MCPlusBuilder::getUsedRegs(const MCInst &Inst, BitVector &Regs) const {
|
|||
}
|
||||
}
|
||||
|
||||
void MCPlusBuilder::getSrcRegs(const MCInst &Inst, BitVector &Regs) const {
|
||||
if (isPrefix(Inst) || isCFI(Inst))
|
||||
return;
|
||||
|
||||
if (isCall(Inst)) {
|
||||
BitVector CallRegs = BitVector(Regs.size(), false);
|
||||
getCalleeSavedRegs(CallRegs);
|
||||
CallRegs.flip();
|
||||
Regs |= CallRegs;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReturn(Inst)) {
|
||||
getDefaultLiveOut(Regs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRep(Inst)) {
|
||||
getRepRegs(Regs);
|
||||
}
|
||||
|
||||
const MCInstrDesc &InstInfo = Info->get(Inst.getOpcode());
|
||||
|
||||
const MCPhysReg *ImplicitUses = InstInfo.getImplicitUses();
|
||||
for (unsigned I = 0, E = InstInfo.getNumImplicitUses(); I != E; ++I) {
|
||||
Regs |= getAliases(ImplicitUses[I], /*OnlySmaller=*/true);
|
||||
}
|
||||
|
||||
for (unsigned I = InstInfo.getNumDefs(), E = InstInfo.getNumOperands();
|
||||
I != E; ++I) {
|
||||
if (!Inst.getOperand(I).isReg())
|
||||
continue;
|
||||
Regs |= getAliases(Inst.getOperand(I).getReg(), /*OnlySmaller=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
bool MCPlusBuilder::hasDefOfPhysReg(const MCInst &MI, unsigned Reg) const {
|
||||
const MCInstrDesc &InstInfo = Info->get(MI.getOpcode());
|
||||
return InstInfo.hasDefOfPhysReg(MI, Reg, *RegInfo);
|
||||
|
|
|
@ -355,6 +355,12 @@ public:
|
|||
return Analysis->isConditionalBranch(Inst);
|
||||
}
|
||||
|
||||
/// Returns true if Inst is a condtional move instruction
|
||||
virtual bool isConditionalMove(const MCInst &Inst) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool isUnconditionalBranch(const MCInst &Inst) const {
|
||||
return Analysis->isUnconditionalBranch(Inst);
|
||||
}
|
||||
|
@ -469,6 +475,11 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual bool isRep(const MCInst &Inst) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool deleteREPPrefix(MCInst &Inst) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return false;
|
||||
|
@ -972,6 +983,20 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
// Replace Register in Inst with Imm. Returns true if successful
|
||||
virtual bool replaceRegWithImm(MCInst &Inst, unsigned Register,
|
||||
int64_t Imm) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Replace ToReplace in Inst with ReplaceWith. Returns true if successful
|
||||
virtual bool replaceRegWithReg(MCInst &Inst, unsigned ToReplace,
|
||||
unsigned ReplaceWith) const {
|
||||
llvm_unreachable("not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Add \p NewImm to the current immediate operand of \p Inst. If it is a
|
||||
/// memory accessing instruction, this immediate is the memory address
|
||||
/// displacement. Otherwise, the target operand is the first immediate
|
||||
|
@ -1139,6 +1164,11 @@ public:
|
|||
llvm_unreachable("not implemented");
|
||||
}
|
||||
|
||||
/// Set of Registers used by the Rep instruction
|
||||
virtual void getRepRegs(BitVector &Regs) const {
|
||||
llvm_unreachable("not implemented");
|
||||
}
|
||||
|
||||
/// Return the register width in bytes (1, 2, 4 or 8)
|
||||
virtual uint8_t getRegSize(MCPhysReg Reg) const;
|
||||
|
||||
|
@ -1182,6 +1212,10 @@ public:
|
|||
/// but only if they are strictly smaller than the actual reg
|
||||
virtual void getUsedRegs(const MCInst &Inst, BitVector &Regs) const;
|
||||
|
||||
/// Set of all src registers -- includes aliases but
|
||||
/// only if they are strictly smaller than the actual reg
|
||||
virtual void getSrcRegs(const MCInst &Inst, BitVector &Regs) const;
|
||||
|
||||
/// Return true if this instruction defines the specified physical
|
||||
/// register either explicitly or implicitly.
|
||||
virtual bool hasDefOfPhysReg(const MCInst &MI, unsigned Reg) const;
|
||||
|
|
|
@ -41,6 +41,179 @@ static cl::opt<unsigned> TailDuplicationMaximumDuplication(
|
|||
|
||||
namespace llvm {
|
||||
namespace bolt {
|
||||
void TailDuplication::getCallerSavedRegs(const MCInst &Inst, BitVector &Regs,
|
||||
BinaryContext &BC) const {
|
||||
if (!BC.MIB->isCall(Inst))
|
||||
return;
|
||||
BitVector CallRegs = BitVector(BC.MRI->getNumRegs(), false);
|
||||
BC.MIB->getCalleeSavedRegs(CallRegs);
|
||||
CallRegs.flip();
|
||||
Regs |= CallRegs;
|
||||
}
|
||||
|
||||
bool TailDuplication::regIsPossiblyOverwritten(const MCInst &Inst, unsigned Reg,
|
||||
BinaryContext &BC) const {
|
||||
BitVector WrittenRegs = BitVector(BC.MRI->getNumRegs(), false);
|
||||
BC.MIB->getWrittenRegs(Inst, WrittenRegs);
|
||||
getCallerSavedRegs(Inst, WrittenRegs, BC);
|
||||
if (BC.MIB->isRep(Inst))
|
||||
BC.MIB->getRepRegs(WrittenRegs);
|
||||
WrittenRegs &= BC.MIB->getAliases(Reg, false);
|
||||
return WrittenRegs.any();
|
||||
}
|
||||
|
||||
bool TailDuplication::regIsDefinitelyOverwritten(const MCInst &Inst,
|
||||
unsigned Reg,
|
||||
BinaryContext &BC) const {
|
||||
BitVector WrittenRegs = BitVector(BC.MRI->getNumRegs(), false);
|
||||
BC.MIB->getWrittenRegs(Inst, WrittenRegs);
|
||||
getCallerSavedRegs(Inst, WrittenRegs, BC);
|
||||
if (BC.MIB->isRep(Inst))
|
||||
BC.MIB->getRepRegs(WrittenRegs);
|
||||
return (!regIsUsed(Inst, Reg, BC) && WrittenRegs.test(Reg) &&
|
||||
!BC.MIB->isConditionalMove(Inst));
|
||||
}
|
||||
|
||||
bool TailDuplication::regIsUsed(const MCInst &Inst, unsigned Reg,
|
||||
BinaryContext &BC) const {
|
||||
BitVector SrcRegs = BitVector(BC.MRI->getNumRegs(), false);
|
||||
BC.MIB->getSrcRegs(Inst, SrcRegs);
|
||||
SrcRegs &= BC.MIB->getAliases(Reg, true);
|
||||
return SrcRegs.any();
|
||||
}
|
||||
|
||||
bool TailDuplication::isOverwrittenBeforeUsed(BinaryBasicBlock &StartBB,
|
||||
unsigned Reg) const {
|
||||
BinaryFunction *BF = StartBB.getFunction();
|
||||
BinaryContext &BC = BF->getBinaryContext();
|
||||
std::queue<BinaryBasicBlock *> Q;
|
||||
for (auto Itr = StartBB.succ_begin(); Itr != StartBB.succ_end(); ++Itr) {
|
||||
BinaryBasicBlock *NextBB = *Itr;
|
||||
Q.push(NextBB);
|
||||
}
|
||||
std::set<BinaryBasicBlock *> Visited;
|
||||
// Breadth first search through successive blocks and see if Reg is ever used
|
||||
// before its overwritten
|
||||
while (Q.size() > 0) {
|
||||
BinaryBasicBlock *CurrBB = Q.front();
|
||||
Q.pop();
|
||||
if (Visited.count(CurrBB))
|
||||
continue;
|
||||
Visited.insert(CurrBB);
|
||||
bool Overwritten = false;
|
||||
for (auto Itr = CurrBB->begin(); Itr != CurrBB->end(); ++Itr) {
|
||||
MCInst &Inst = *Itr;
|
||||
if (regIsUsed(Inst, Reg, BC))
|
||||
return false;
|
||||
if (regIsDefinitelyOverwritten(Inst, Reg, BC)) {
|
||||
Overwritten = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Overwritten)
|
||||
continue;
|
||||
for (auto Itr = CurrBB->succ_begin(); Itr != CurrBB->succ_end(); ++Itr) {
|
||||
BinaryBasicBlock *NextBB = *Itr;
|
||||
Q.push(NextBB);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TailDuplication::constantAndCopyPropagate(
|
||||
BinaryBasicBlock &OriginalBB,
|
||||
std::vector<BinaryBasicBlock *> &BlocksToPropagate) {
|
||||
BinaryFunction *BF = OriginalBB.getFunction();
|
||||
BinaryContext &BC = BF->getBinaryContext();
|
||||
|
||||
BlocksToPropagate.insert(BlocksToPropagate.begin(), &OriginalBB);
|
||||
// Iterate through the original instructions to find one to propagate
|
||||
for (auto Itr = OriginalBB.begin(); Itr != OriginalBB.end(); ++Itr) {
|
||||
MCInst &OriginalInst = *Itr;
|
||||
// It must be a non conditional
|
||||
if (BC.MIB->isConditionalMove(OriginalInst))
|
||||
continue;
|
||||
|
||||
// Move immediate or move register
|
||||
if ((!BC.MII->get(OriginalInst.getOpcode()).isMoveImmediate() ||
|
||||
!OriginalInst.getOperand(1).isImm()) &&
|
||||
(!BC.MII->get(OriginalInst.getOpcode()).isMoveReg() ||
|
||||
!OriginalInst.getOperand(1).isReg()))
|
||||
continue;
|
||||
|
||||
// True if this is constant propagation and not copy propagation
|
||||
bool ConstantProp = BC.MII->get(OriginalInst.getOpcode()).isMoveImmediate();
|
||||
// The Register to replaced
|
||||
unsigned Reg = OriginalInst.getOperand(0).getReg();
|
||||
// True if the register to replace was replaced everywhere it was used
|
||||
bool ReplacedEverywhere = true;
|
||||
// True if the register was definitely overwritten
|
||||
bool Overwritten = false;
|
||||
// True if the register to replace and the register to replace with (for
|
||||
// copy propagation) has not been overwritten and is still usable
|
||||
bool RegsActive = true;
|
||||
|
||||
// Iterate through successor blocks and through their instructions
|
||||
for (BinaryBasicBlock *NextBB : BlocksToPropagate) {
|
||||
for (auto PropagateItr =
|
||||
((NextBB == &OriginalBB) ? Itr + 1 : NextBB->begin());
|
||||
PropagateItr < NextBB->end(); ++PropagateItr) {
|
||||
MCInst &PropagateInst = *PropagateItr;
|
||||
if (regIsUsed(PropagateInst, Reg, BC)) {
|
||||
bool Replaced = false;
|
||||
// If both registers are active for copy propagation or the register
|
||||
// to replace is active for constant propagation
|
||||
if (RegsActive) {
|
||||
// Set Replaced and so ReplacedEverwhere to false if it cannot be
|
||||
// replaced (no replacing that opcode, Register is src and dest)
|
||||
if (ConstantProp) {
|
||||
Replaced = BC.MIB->replaceRegWithImm(
|
||||
PropagateInst, Reg, OriginalInst.getOperand(1).getImm());
|
||||
} else {
|
||||
Replaced = BC.MIB->replaceRegWithReg(
|
||||
PropagateInst, Reg, OriginalInst.getOperand(1).getReg());
|
||||
}
|
||||
}
|
||||
ReplacedEverywhere = ReplacedEverywhere && Replaced;
|
||||
}
|
||||
// For copy propagation, make sure no propagation happens after the
|
||||
// register to replace with is overwritten
|
||||
if (!ConstantProp &&
|
||||
regIsPossiblyOverwritten(PropagateInst,
|
||||
OriginalInst.getOperand(1).getReg(), BC)) {
|
||||
RegsActive = false;
|
||||
}
|
||||
// Make sure no propagation happens after the register to replace is
|
||||
// overwritten
|
||||
if (regIsPossiblyOverwritten(PropagateInst, Reg, BC)) {
|
||||
RegsActive = false;
|
||||
}
|
||||
// Record if the register to replace is overwritten
|
||||
if (regIsDefinitelyOverwritten(PropagateInst, Reg, BC)) {
|
||||
Overwritten = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Overwritten)
|
||||
break;
|
||||
}
|
||||
|
||||
// If the register was replaced everwhere and it was overwritten in either
|
||||
// one of the iterated through blocks or one of the successor blocks, delete
|
||||
// the original move instruction
|
||||
if (ReplacedEverywhere &&
|
||||
(Overwritten ||
|
||||
isOverwrittenBeforeUsed(
|
||||
*BlocksToPropagate[BlocksToPropagate.size() - 1], Reg))) {
|
||||
// If both registers are active for copy propagation or the register
|
||||
// to replace is active for constant propagation
|
||||
StaticInstructionDeletionCount++;
|
||||
DynamicInstructionDeletionCount += OriginalBB.getExecutionCount();
|
||||
Itr = std::prev(OriginalBB.eraseInstruction(Itr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TailDuplication::isInCacheLine(const BinaryBasicBlock &BB,
|
||||
const BinaryBasicBlock &Succ) const {
|
||||
if (&BB == &Succ)
|
||||
|
@ -123,7 +296,7 @@ TailDuplication::aggressiveCodeToDuplicate(BinaryBasicBlock &BB) const {
|
|||
return BlocksToDuplicate;
|
||||
}
|
||||
|
||||
void TailDuplication::tailDuplicate(
|
||||
std::vector<BinaryBasicBlock *> TailDuplication::tailDuplicate(
|
||||
BinaryBasicBlock &BB,
|
||||
const std::vector<BinaryBasicBlock *> &BlocksToDuplicate) const {
|
||||
BinaryFunction *BF = BB.getFunction();
|
||||
|
@ -149,6 +322,7 @@ void TailDuplication::tailDuplicate(
|
|||
LastDuplicatedBB->removeSuccessor(LastDuplicatedBB->getSuccessor());
|
||||
|
||||
std::vector<std::unique_ptr<BinaryBasicBlock>> DuplicatedBlocks;
|
||||
std::vector<BinaryBasicBlock *> DuplicatedBlocksToReturn;
|
||||
|
||||
for (BinaryBasicBlock *CurrBB : BlocksToDuplicate) {
|
||||
DuplicatedBlocks.emplace_back(
|
||||
|
@ -161,6 +335,8 @@ void TailDuplication::tailDuplicate(
|
|||
std::max((uint64_t)1, CurrBB->getExecutionCount()));
|
||||
LastDuplicatedBB->addSuccessor(NewBB, LastBI);
|
||||
|
||||
DuplicatedBlocksToReturn.push_back(NewBB);
|
||||
|
||||
// As long as its not the first block, adjust both original and duplicated
|
||||
// to what they should be
|
||||
if (LastDuplicatedBB != &BB) {
|
||||
|
@ -183,6 +359,8 @@ void TailDuplication::tailDuplicate(
|
|||
LastDuplicatedBB->adjustExecutionCount(ExecutionCountRatio);
|
||||
|
||||
BF->insertBasicBlocks(&BB, std::move(DuplicatedBlocks));
|
||||
|
||||
return DuplicatedBlocksToReturn;
|
||||
}
|
||||
|
||||
void TailDuplication::runOnFunction(BinaryFunction &Function) {
|
||||
|
@ -222,7 +400,15 @@ void TailDuplication::runOnFunction(BinaryFunction &Function) {
|
|||
if (BlocksToDuplicate.size() > 0) {
|
||||
PossibleDuplications++;
|
||||
PossibleDuplicationsDynamicCount += BB->getExecutionCount();
|
||||
tailDuplicate(*BB, BlocksToDuplicate);
|
||||
std::vector<BinaryBasicBlock *> DuplicatedBlocks =
|
||||
tailDuplicate(*BB, BlocksToDuplicate);
|
||||
constantAndCopyPropagate(*BB, DuplicatedBlocks);
|
||||
BinaryBasicBlock *FirstBB = BlocksToDuplicate[0];
|
||||
if (FirstBB->pred_size() == 1) {
|
||||
BinaryBasicBlock *PredBB = *FirstBB->pred_begin();
|
||||
if (PredBB->succ_size() == 1)
|
||||
constantAndCopyPropagate(*PredBB, BlocksToDuplicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +416,8 @@ void TailDuplication::runOnFunction(BinaryFunction &Function) {
|
|||
void TailDuplication::runOnFunctions(BinaryContext &BC) {
|
||||
for (auto &It : BC.getBinaryFunctions()) {
|
||||
BinaryFunction &Function = It.second;
|
||||
if (Function.isIgnored())
|
||||
continue;
|
||||
runOnFunction(Function);
|
||||
}
|
||||
|
||||
|
@ -247,6 +435,10 @@ void TailDuplication::runOnFunctions(BinaryContext &BC) {
|
|||
<< format("%.1f", ((float)PossibleDuplicationsDynamicCount * 100.0f) /
|
||||
AllBlocksDynamicCount)
|
||||
<< "%\n";
|
||||
outs() << "BOLT-INFO: tail duplication static propagation deletions: "
|
||||
<< StaticInstructionDeletionCount << "\n";
|
||||
outs() << "BOLT-INFO: tail duplication dynamic propagation deletions: "
|
||||
<< DynamicInstructionDeletionCount << "\n"; //
|
||||
}
|
||||
|
||||
} // end namespace bolt
|
||||
|
|
|
@ -57,12 +57,42 @@ class TailDuplication : public BinaryFunctionPass {
|
|||
/// Record the execution count of all blocks.
|
||||
uint64_t AllBlocksDynamicCount = 0;
|
||||
|
||||
/// Record the number of instructions deleted because of propagation
|
||||
uint64_t StaticInstructionDeletionCount = 0;
|
||||
|
||||
/// Record the number of instructions deleted because of propagation
|
||||
uint64_t DynamicInstructionDeletionCount = 0;
|
||||
|
||||
/// Sets Regs with the caller saved registers
|
||||
void getCallerSavedRegs(const MCInst &Inst, BitVector &Regs,
|
||||
BinaryContext &BC) const;
|
||||
|
||||
/// Returns true if Reg is possibly overwritten by Inst
|
||||
bool regIsPossiblyOverwritten(const MCInst &Inst, unsigned Reg,
|
||||
BinaryContext &BC) const;
|
||||
|
||||
/// Returns true if Reg is definitely overwritten by Inst
|
||||
bool regIsDefinitelyOverwritten(const MCInst &Inst, unsigned Reg,
|
||||
BinaryContext &BC) const;
|
||||
|
||||
/// Returns true if Reg is used by Inst
|
||||
bool regIsUsed(const MCInst &Inst, unsigned Reg, BinaryContext &BC) const;
|
||||
|
||||
/// Returns true if Reg is overwritten before its used by StartBB's sucessors
|
||||
bool isOverwrittenBeforeUsed(BinaryBasicBlock &StartBB, unsigned Reg) const;
|
||||
|
||||
/// Constant and Copy Propagate for the block formed by OriginalBB and
|
||||
/// BlocksToPropagate
|
||||
void
|
||||
constantAndCopyPropagate(BinaryBasicBlock &OriginalBB,
|
||||
std::vector<BinaryBasicBlock *> &BlocksToPropagate);
|
||||
|
||||
/// True if Succ is in the same cache line as BB (approximately)
|
||||
bool isInCacheLine(const BinaryBasicBlock &BB,
|
||||
const BinaryBasicBlock &Succ) const;
|
||||
|
||||
/// Duplicates BlocksToDuplicate and places them after BB.
|
||||
void
|
||||
std::vector<BinaryBasicBlock *>
|
||||
tailDuplicate(BinaryBasicBlock &BB,
|
||||
const std::vector<BinaryBasicBlock *> &BlocksToDuplicate) const;
|
||||
|
||||
|
|
|
@ -436,6 +436,10 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -1169,6 +1173,10 @@ public:
|
|||
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:
|
||||
|
@ -1207,7 +1215,19 @@ public:
|
|||
case 2: return X86::CX; case 1: return X86::CL;
|
||||
default: llvm_unreachable("Unexpected size");
|
||||
}
|
||||
case X86::R8: case X86::R8D: case X86::R8W: case X86::R8B:
|
||||
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;
|
||||
|
@ -2938,6 +2958,248 @@ public:
|
|||
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 && Imm >= std::numeric_limits<int8_t>::min() &&
|
||||
Imm <= std::numeric_limits<int8_t>::max())
|
||||
break;
|
||||
if (Check.first == CHECK32 &&
|
||||
Imm >= std::numeric_limits<int32_t>::min() &&
|
||||
Imm <= std::numeric_limits<int32_t>::max())
|
||||
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;
|
||||
|
||||
// Iterate backwards to replace the src register before the src/dest
|
||||
// register as in AND, ADD, and SUB Only iterate through src operands that
|
||||
// arent also dest operands
|
||||
for (unsigned Index = InstDesc.getNumOperands() - 1,
|
||||
E = InstDesc.getNumDefs() + (I.HasLHS ? 0 : -1);
|
||||
Index != E; --Index) {
|
||||
if (!Inst.getOperand(Index).isReg() ||
|
||||
Inst.getOperand(Index).getReg() != Register)
|
||||
continue;
|
||||
MCOperand NewOperand = MCOperand::createImm(Imm);
|
||||
Inst.getOperand(Index) = NewOperand;
|
||||
break;
|
||||
}
|
||||
|
||||
Inst.setOpcode(NewOpcode);
|
||||
|
||||
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 (isAND(Inst.getOpcode()) || isADD(Inst.getOpcode()) || isSUB(Inst)) {
|
||||
HasLHS = true;
|
||||
} else if (isPop(Inst) || isPush(Inst) || isCMP(Inst.getOpcode()) ||
|
||||
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);
|
||||
|
@ -3081,6 +3343,12 @@ public:
|
|||
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)) ||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# REQUIRES: system-linux
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \
|
||||
# RUN: %s -o %t.o
|
||||
# RUN: link_fdata %s %t.o %t.fdata
|
||||
# RUN: %host_cc %cflags %t.o -o %t.exe -Wl,-q
|
||||
# RUN: llvm-bolt %t.exe -data %t.fdata -reorder-blocks=cache+ -print-finalized \
|
||||
# RUN: -tail-duplication -tail-duplication-minimum-offset 1 -o %t.out | FileCheck %s
|
||||
# RUN: %t.exe; echo $?
|
||||
# RUN: %t.out; echo $?
|
||||
|
||||
# FDATA: 1 main 14 1 main #.BB2# 0 10
|
||||
# FDATA: 1 main 16 1 main #.BB2# 0 20
|
||||
# CHECK: tail duplication possible duplications: 1
|
||||
# CHECK: BB Layout : .LBB00, .Ltail-dup0, .Ltmp0, .Ltmp1
|
||||
# CHECK-NOT: mov $0x2, %rbx
|
||||
|
||||
.text
|
||||
.globl main
|
||||
.type main, %function
|
||||
.size main, .Lend-main
|
||||
main:
|
||||
mov $0x2, %rbx
|
||||
mov $0x1, %rdi
|
||||
inc %rdi
|
||||
mov %rdi, %rsi
|
||||
jmp .BB2
|
||||
.BB1:
|
||||
mov $0x9, %rbx
|
||||
.BB2:
|
||||
mov %rbx, %rax
|
||||
mov $0x5, %rbx
|
||||
add %rsi, %rax
|
||||
jmp .BB4
|
||||
.BB3:
|
||||
mov $0x9, %rbx
|
||||
.BB4:
|
||||
mov $0xa, %rsi
|
||||
add %rbx, %rax
|
||||
add %rsi, %rax
|
||||
.BB5:
|
||||
retq
|
||||
.Lend:
|
Loading…
Reference in New Issue