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:
Theodoros Kasampalis 2016-06-03 00:58:11 -07:00 committed by Maksim Panchenko
parent 17b846586c
commit 156a55209c
7 changed files with 172 additions and 8 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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