Simplification of loads from read-only data sections.
Summary: Instructions that load data from the a read-only data section and their target address can be computed statically (e.g. RIP-relative addressing) are modified to corresponding instructions that use immediate operands. We apply the transformation only when the resulting instruction will have smaller or equal size. (cherry picked from FBD3397112)
This commit is contained in:
parent
17b846586c
commit
156a55209c
|
@ -242,5 +242,13 @@ ErrorOr<SectionRef> BinaryContext::getSectionForAddress(uint64_t Address) const{
|
|||
return std::make_error_code(std::errc::bad_address);
|
||||
}
|
||||
|
||||
uint64_t BinaryContext::getInstructionSize(const MCInst &Instr) const {
|
||||
SmallString<256> Code;
|
||||
SmallVector<MCFixup, 4> Fixups;
|
||||
raw_svector_ostream VecOS(Code);
|
||||
MCE->encodeInstruction(Instr, VecOS, Fixups, *STI);
|
||||
return Code.size();
|
||||
}
|
||||
|
||||
} // namespace bolt
|
||||
} // namespace llvm
|
||||
|
|
|
@ -177,6 +177,12 @@ public:
|
|||
/// disassembled functions.
|
||||
void preprocessFunctionDebugInfo(
|
||||
std::map<uint64_t, BinaryFunction> &BinaryFunctions);
|
||||
|
||||
/// Calculate the size of the given instruction.
|
||||
/// Note: this can be imprecise wrt the final binary since happening prior to
|
||||
/// relaxation, as well as wrt the original binary because of opcode
|
||||
/// shortening.
|
||||
uint64_t getInstructionSize(const MCInst &Instr) const;
|
||||
};
|
||||
|
||||
} // namespace bolt
|
||||
|
|
|
@ -327,14 +327,7 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation,
|
|||
|
||||
for (auto &Instr : *BB) {
|
||||
printInstruction(Instr);
|
||||
|
||||
// Calculate the size of the instruction.
|
||||
// Note: this is imprecise since happening prior to relaxation.
|
||||
SmallString<256> Code;
|
||||
SmallVector<MCFixup, 4> Fixups;
|
||||
raw_svector_ostream VecOS(Code);
|
||||
BC.MCE->encodeInstruction(Instr, VecOS, Fixups, *BC.STI);
|
||||
Offset += Code.size();
|
||||
Offset += BC.getInstructionSize(Instr);
|
||||
}
|
||||
|
||||
if (!BB->Successors.empty()) {
|
||||
|
|
|
@ -42,6 +42,14 @@ Peepholes("peepholes",
|
|||
llvm::cl::init(true),
|
||||
llvm::cl::Optional);
|
||||
|
||||
static llvm::cl::opt<bool>
|
||||
SimplifyRODataLoads("simplify-rodata-loads",
|
||||
llvm::cl::desc("simplify loads from read-only sections by "
|
||||
"replacing the memory operand with the "
|
||||
"constant found in the corresponding "
|
||||
"section"),
|
||||
llvm::cl::Optional);
|
||||
|
||||
} // namespace opts
|
||||
|
||||
namespace llvm {
|
||||
|
@ -69,6 +77,9 @@ void BinaryFunctionPassManager::runAllPasses(
|
|||
std::move(llvm::make_unique<EliminateUnreachableBlocks>(Manager.NagUser)),
|
||||
opts::EliminateUnreachable);
|
||||
|
||||
Manager.registerPass(llvm::make_unique<SimplifyRODataLoads>(),
|
||||
opts::SimplifyRODataLoads);
|
||||
|
||||
Manager.registerPass(std::move(llvm::make_unique<ReorderBasicBlocks>()));
|
||||
|
||||
Manager.registerPass(llvm::make_unique<SimplifyConditionalTailCalls>(),
|
||||
|
|
|
@ -22,6 +22,7 @@ extern llvm::cl::opt<bool> PrintReordered;
|
|||
extern llvm::cl::opt<bool> PrintEHRanges;
|
||||
extern llvm::cl::opt<bool> PrintUCE;
|
||||
extern llvm::cl::opt<bool> PrintPeepholes;
|
||||
extern llvm::cl::opt<bool> PrintSimplifyROLoads;
|
||||
extern llvm::cl::opt<llvm::bolt::BinaryFunction::SplittingType> SplitFunctions;
|
||||
extern bool shouldProcess(const llvm::bolt::BinaryFunction &Function);
|
||||
|
||||
|
@ -567,5 +568,119 @@ void Peepholes::runOnFunctions(BinaryContext &BC,
|
|||
}
|
||||
}
|
||||
|
||||
bool SimplifyRODataLoads::simplifyRODataLoads(
|
||||
BinaryContext &BC, BinaryFunction &BF) {
|
||||
auto &MIA = BC.MIA;
|
||||
|
||||
uint64_t NumLocalLoadsSimplified = 0;
|
||||
uint64_t NumDynamicLocalLoadsSimplified = 0;
|
||||
uint64_t NumLocalLoadsFound = 0;
|
||||
uint64_t NumDynamicLocalLoadsFound = 0;
|
||||
|
||||
for (auto *BB : BF.layout()) {
|
||||
for (auto &Inst : *BB) {
|
||||
unsigned Opcode = Inst.getOpcode();
|
||||
const MCInstrDesc &Desc = BC.MII->get(Opcode);
|
||||
|
||||
// Skip instructions that do not load from memory.
|
||||
if (!Desc.mayLoad())
|
||||
continue;
|
||||
|
||||
// Try to statically evaluate the target memory address;
|
||||
uint64_t TargetAddress;
|
||||
|
||||
if (MIA->hasRIPOperand(Inst)) {
|
||||
// Try to find the symbol that corresponds to the rip-relative operand.
|
||||
MCOperand DisplOp;
|
||||
if (!MIA->getRIPOperandDisp(Inst, DisplOp))
|
||||
continue;
|
||||
|
||||
assert(DisplOp.isExpr() &&
|
||||
"found rip-relative with non-symbolic displacement");
|
||||
|
||||
// Get displacement symbol.
|
||||
const MCSymbolRefExpr *DisplExpr;
|
||||
if (!(DisplExpr = dyn_cast<MCSymbolRefExpr>(DisplOp.getExpr())))
|
||||
continue;
|
||||
const MCSymbol &DisplSymbol = DisplExpr->getSymbol();
|
||||
|
||||
// Look up the symbol address in the global symbols map of the binary
|
||||
// context object.
|
||||
auto GI = BC.GlobalSymbols.find(DisplSymbol.getName().str());
|
||||
if (GI == BC.GlobalSymbols.end())
|
||||
continue;
|
||||
TargetAddress = GI->second;
|
||||
} else if (!MIA->evaluateMemOperand(Inst, TargetAddress)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the contents of the section containing the target addresss of the
|
||||
// memory operand. We are only interested in read-only sections.
|
||||
ErrorOr<SectionRef> DataSectionOrErr =
|
||||
BC.getSectionForAddress(TargetAddress);
|
||||
if (!DataSectionOrErr)
|
||||
continue;
|
||||
SectionRef DataSection = DataSectionOrErr.get();
|
||||
if (!DataSection.isReadOnly())
|
||||
continue;
|
||||
uint32_t Offset = TargetAddress - DataSection.getAddress();
|
||||
StringRef ConstantData;
|
||||
if (std::error_code EC = DataSection.getContents(ConstantData)) {
|
||||
errs() << "BOLT-ERROR: 'cannot get section contents': "
|
||||
<< EC.message() << ".\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
++NumLocalLoadsFound;
|
||||
if (BB->getExecutionCount() != BinaryBasicBlock::COUNT_NO_PROFILE)
|
||||
NumDynamicLocalLoadsFound += BB->getExecutionCount();
|
||||
|
||||
if (MIA->replaceMemOperandWithImm(Inst, ConstantData, Offset)) {
|
||||
++NumLocalLoadsSimplified;
|
||||
if (BB->getExecutionCount() != BinaryBasicBlock::COUNT_NO_PROFILE)
|
||||
NumDynamicLocalLoadsSimplified += BB->getExecutionCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NumLoadsFound += NumLocalLoadsFound;
|
||||
NumDynamicLoadsFound += NumDynamicLocalLoadsFound;
|
||||
NumLoadsSimplified += NumLocalLoadsSimplified;
|
||||
NumDynamicLoadsSimplified += NumDynamicLocalLoadsSimplified;
|
||||
|
||||
return NumLocalLoadsSimplified > 0;
|
||||
}
|
||||
|
||||
void SimplifyRODataLoads::runOnFunctions(
|
||||
BinaryContext &BC,
|
||||
std::map<uint64_t, BinaryFunction> &BFs,
|
||||
std::set<uint64_t> &
|
||||
) {
|
||||
|
||||
for (auto &It : BFs) {
|
||||
auto &Function = It.second;
|
||||
|
||||
if (!Function.isSimple())
|
||||
continue;
|
||||
|
||||
if (simplifyRODataLoads(BC, Function)) {
|
||||
if (opts::PrintAll || opts::PrintSimplifyROLoads) {
|
||||
Function.print(errs(),
|
||||
"after simplifying read-only section loads",
|
||||
true);
|
||||
}
|
||||
if (opts::DumpDotAll) {
|
||||
Function.dumpGraphForPass("simplify-rodata-loads");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outs() << "BOLT: simplified " << NumLoadsSimplified << " out of ";
|
||||
outs() << NumLoadsFound << " loads from a statically computed address.\n";
|
||||
outs() << "BOLT: dynamic loads simplified: " << NumDynamicLoadsSimplified;
|
||||
outs() << "\n";
|
||||
outs() << "BOLT: dynamic loads found: " << NumDynamicLoadsFound << "\n";
|
||||
}
|
||||
|
||||
} // namespace bolt
|
||||
} // namespace llvm
|
||||
|
|
|
@ -157,6 +157,31 @@ class Peepholes : public BinaryFunctionPass {
|
|||
std::set<uint64_t> &LargeFunctions) override;
|
||||
};
|
||||
|
||||
/// An optimization to simplify loads from read-only sections.The pass converts
|
||||
/// load instructions with statically computed target address such as:
|
||||
///
|
||||
/// mov 0x12f(%rip), %eax
|
||||
///
|
||||
/// to their counterparts that use immediate opreands instead of memory loads:
|
||||
///
|
||||
/// mov $0x4007dc, %eax
|
||||
///
|
||||
/// when the target address points somewhere inside a read-only section.
|
||||
///
|
||||
class SimplifyRODataLoads : public BinaryFunctionPass {
|
||||
uint64_t NumLoadsSimplified{0};
|
||||
uint64_t NumDynamicLoadsSimplified{0};
|
||||
uint64_t NumLoadsFound{0};
|
||||
uint64_t NumDynamicLoadsFound{0};
|
||||
|
||||
bool simplifyRODataLoads(BinaryContext &BC, BinaryFunction &BF);
|
||||
|
||||
public:
|
||||
void runOnFunctions(BinaryContext &BC,
|
||||
std::map<uint64_t, BinaryFunction> &BFs,
|
||||
std::set<uint64_t> &LargeFunctions) override;
|
||||
};
|
||||
|
||||
} // namespace bolt
|
||||
} // namespace llvm
|
||||
|
||||
|
|
|
@ -175,6 +175,12 @@ PrintEHRanges("print-eh-ranges",
|
|||
cl::desc("print function with updated exception ranges"),
|
||||
cl::Hidden);
|
||||
|
||||
cl::opt<bool>
|
||||
PrintSimplifyROLoads("print-simplify-rodata-loads",
|
||||
cl::desc("print functions after simplification of RO data"
|
||||
" loads"),
|
||||
cl::Hidden);
|
||||
|
||||
cl::opt<bool>
|
||||
PrintReordered("print-reordered",
|
||||
cl::desc("print functions after layout optimization"),
|
||||
|
|
Loading…
Reference in a new issue