[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:
Joey Thaman 2021-08-10 10:02:32 -07:00 committed by Maksim Panchenko
parent 2a5790b670
commit ef6186c822
6 changed files with 607 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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