Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Optimize calls to functions that are a single unconditional jump
Summary: Many functions (around 600) in the HHVM binary are simply a single unconditional jump instruction to another function. These can be trivially optimized by modifying the call sites to directly call the branch target instead (because it also happens with more than one jump in sequence, we do it iteratively). This diff also adds a very simple analysis/optimization pass system in which this pass is the first one to be implemented. A follow-up to this could be to move the current optimizations to other passes. (cherry picked from FBD3211138)
- Loading branch information
Showing
3 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
//===--- BinaryPass.h - Binary-level analysis/optimization passes ---------===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "BinaryPass.h" | ||
|
||
#define DEBUG_TYPE "bolt" | ||
|
||
namespace opts { | ||
|
||
static llvm::cl::opt<bool> | ||
OptimizeBodylessFunctions( | ||
"optimize-bodyless-functions", | ||
llvm::cl::desc("optimize functions that just do a tail call"), | ||
llvm::cl::Optional); | ||
|
||
} // namespace opts | ||
|
||
namespace llvm { | ||
namespace bolt { | ||
|
||
/// Detects functions that simply do a tail call when they are called and | ||
/// optimizes calls to these functions. | ||
class OptimizeBodylessFunctions : public BinaryFunctionPass { | ||
private: | ||
/// Maps function name to BinaryFunction. | ||
std::map<std::string, const BinaryFunction *> FunctionByName; | ||
|
||
/// EquivalentCallTarget[F] = G ==> function F is simply a tail call to G, | ||
/// thus calls to F can be optimized to calls to G. | ||
std::map<std::string, const BinaryFunction *> EquivalentCallTarget; | ||
|
||
void analyze(BinaryFunction &BF, | ||
BinaryContext &BC) { | ||
if (BF.size() != 1 || BF.begin()->size() == 0) | ||
return; | ||
|
||
auto &BB = *BF.begin(); | ||
const auto &FirstInst = *BB.begin(); | ||
|
||
if (!BC.MIA->isTailCall(FirstInst)) | ||
return; | ||
|
||
auto &Op1 = FirstInst.getOperand(0); | ||
if (!Op1.isExpr()) | ||
return; | ||
|
||
if (auto Expr = dyn_cast<MCSymbolRefExpr>(Op1.getExpr())) { | ||
auto CalleeIt = FunctionByName.find(Expr->getSymbol().getName()); | ||
if (CalleeIt != FunctionByName.end()) { | ||
assert(Expr->getSymbol().getName() == CalleeIt->second->getName()); | ||
EquivalentCallTarget[BF.getName()] = CalleeIt->second; | ||
} | ||
} | ||
} | ||
|
||
void optimizeCalls(BinaryFunction &BF, | ||
BinaryContext &BC) { | ||
for (auto BBIt = BF.begin(), BBEnd = BF.end(); BBIt != BBEnd; ++BBIt) { | ||
for (auto InstIt = BBIt->begin(), InstEnd = BBIt->end(); | ||
InstIt != InstEnd; ++InstIt) { | ||
auto &Inst = *InstIt; | ||
if (BC.MIA->isCall(Inst)) { | ||
auto &Op1 = Inst.getOperand(0); | ||
if (Op1.isExpr()) { | ||
if (auto Expr = dyn_cast<MCSymbolRefExpr>(Op1.getExpr())) { | ||
auto OriginalTarget = Expr->getSymbol().getName(); | ||
errs() << "BOLT-OPT: " << BF.getName() << " calls " << OriginalTarget << "\n"; | ||
auto Target = OriginalTarget; | ||
while (EquivalentCallTarget.count(Target)) { | ||
Target = EquivalentCallTarget.find(Target)->second->getName(); | ||
} | ||
if (Target != OriginalTarget) { | ||
DEBUG(errs() << "BOLT-DEBUG: Optimizing " << BF.getName() | ||
<< ": replacing call to " | ||
<< OriginalTarget | ||
<< " by call to " << Target << "\n"); | ||
Inst.clear(); | ||
Inst.addOperand(MCOperand::createExpr( | ||
MCSymbolRefExpr::create( | ||
BC.Ctx->getOrCreateSymbol(Target), *BC.Ctx))); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
public: | ||
void runOnFunctions(BinaryContext &BC, | ||
std::map<uint64_t, BinaryFunction> &BFs) override { | ||
for (auto &It : BFs) { | ||
FunctionByName[It.second.getName()] = &It.second; | ||
} | ||
for (auto &It : BFs) { | ||
analyze(It.second, BC); | ||
} | ||
for (auto &It : BFs) { | ||
optimizeCalls(It.second, BC); | ||
} | ||
} | ||
}; | ||
|
||
void BinaryFunctionPassManager::runAllPasses( | ||
BinaryContext &BC, | ||
std::map<uint64_t, BinaryFunction> &Functions) { | ||
BinaryFunctionPassManager PassManager(BC, Functions); | ||
|
||
PassManager.registerPass(make_unique<OptimizeBodylessFunctions>(), | ||
opts::OptimizeBodylessFunctions); | ||
|
||
PassManager.runPasses(); | ||
} | ||
|
||
} // namespace bolt | ||
} // namespace llvm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
//===--- BinaryPass.h - Binary-level analysis/optimization passes ---------===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// A very simple binary-level analysis/optimization passes system. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_TOOLS_LLVM_BOLT_BINARY_FUNCTION_PASS_H | ||
#define LLVM_TOOLS_LLVM_BOLT_BINARY_FUNCTION_PASS_H | ||
|
||
#include "BinaryFunction.h" | ||
#include "llvm/Support/Options.h" | ||
#include "llvm/Support/CommandLine.h" | ||
#include <map> | ||
#include <memory> | ||
#include <vector> | ||
|
||
namespace llvm { | ||
namespace bolt { | ||
|
||
/// An optimization/analysis pass that runs on functions. | ||
class BinaryFunctionPass { | ||
public: | ||
virtual ~BinaryFunctionPass() = default; | ||
virtual void runOnFunctions(BinaryContext &BC, | ||
std::map<uint64_t, BinaryFunction> &BFs) = 0; | ||
}; | ||
|
||
/// Simple class for managing analyses and optimizations on BinaryFunctions. | ||
class BinaryFunctionPassManager { | ||
protected: | ||
BinaryContext &BC; | ||
std::map<uint64_t, BinaryFunction> &BFs; | ||
std::vector<std::unique_ptr<BinaryFunctionPass>> Passes; | ||
|
||
public: | ||
BinaryFunctionPassManager(BinaryContext &BC, | ||
std::map<uint64_t, BinaryFunction> &BFs) | ||
: BC(BC), BFs(BFs) {} | ||
|
||
/// Adds a pass to this manager based on the value of its corresponding | ||
/// command-line option. | ||
void registerPass(std::unique_ptr<BinaryFunctionPass> Pass, | ||
const cl::opt<bool> &Opt) { | ||
if (Opt) { | ||
Passes.emplace_back(std::move(Pass)); | ||
} | ||
} | ||
|
||
/// Run all registered passes in the order they were added. | ||
void runPasses() { | ||
for (const auto &Pass : Passes) { | ||
Pass->runOnFunctions(BC, BFs); | ||
} | ||
} | ||
|
||
/// Runs all enabled implemented passes on all functions. | ||
static void runAllPasses(BinaryContext &BC, | ||
std::map<uint64_t, BinaryFunction> &Functions); | ||
}; | ||
|
||
} // namespace bolt | ||
} // namespace llvm | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters