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
5 changed files
with
246 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,30 @@ | ||
//===--- BinaryPassManager.cpp - 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 "BinaryPassManager.h" | ||
|
||
namespace llvm { | ||
namespace bolt { | ||
|
||
std::unique_ptr<BinaryFunctionPassManager> | ||
BinaryFunctionPassManager::GlobalPassManager; | ||
|
||
void BinaryFunctionPassManager::runAllPasses( | ||
BinaryContext &BC, | ||
std::map<uint64_t, BinaryFunction> &Functions) { | ||
auto &Manager = getGlobalPassManager(); | ||
Manager.BC = &BC; | ||
Manager.BFs = &Functions; | ||
Manager.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,92 @@ | ||
//===--- BinaryPassManager.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_MANAGER_H | ||
#define LLVM_TOOLS_LLVM_BOLT_BINARY_FUNCTION_PASS_MANAGER_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 { | ||
private: | ||
BinaryContext *BC; | ||
std::map<uint64_t, BinaryFunction> *BFs; | ||
std::vector<std::pair<const cl::opt<bool> *, | ||
std::unique_ptr<BinaryFunctionPass>>> Passes; | ||
|
||
/// Manager that contains all implemented passes. | ||
static std::unique_ptr<BinaryFunctionPassManager> GlobalPassManager; | ||
|
||
public: | ||
BinaryFunctionPassManager(BinaryContext *BC = nullptr, | ||
std::map<uint64_t, BinaryFunction> *BFs = nullptr) | ||
: BC(BC), BFs(BFs) {} | ||
|
||
static BinaryFunctionPassManager &getGlobalPassManager() { | ||
if (!GlobalPassManager) { | ||
GlobalPassManager = llvm::make_unique<BinaryFunctionPassManager>(); | ||
} | ||
return *GlobalPassManager.get(); | ||
} | ||
|
||
/// 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) { | ||
Passes.emplace_back(Opt, std::move(Pass)); | ||
} | ||
|
||
/// Run all registered passes in the order they were added. | ||
void runPasses() { | ||
for (const auto &OptPassPair : Passes) { | ||
if (*OptPassPair.first) { | ||
OptPassPair.second->runOnFunctions(*BC, *BFs); | ||
} | ||
} | ||
} | ||
|
||
/// Runs all enabled implemented passes on all functions. | ||
static void runAllPasses(BinaryContext &BC, | ||
std::map<uint64_t, BinaryFunction> &Functions); | ||
|
||
}; | ||
|
||
template <typename T, cl::opt<bool> *Opt> | ||
class RegisterBinaryPass { | ||
public: | ||
RegisterBinaryPass() { | ||
BinaryFunctionPassManager::getGlobalPassManager().registerPass( | ||
std::move(llvm::make_unique<T>()), Opt); | ||
} | ||
}; | ||
|
||
} // 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
//===--- BinaryPasses.cpp - 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 "BinaryPassManager.h" | ||
|
||
#define DEBUG_TYPE "bolt" | ||
|
||
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: | ||
/// 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, | ||
std::map<uint64_t, BinaryFunction> &BFs) { | ||
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 AddressIt = BC.GlobalSymbols.find(Expr->getSymbol().getName()); | ||
if (AddressIt != BC.GlobalSymbols.end()) { | ||
auto CalleeIt = BFs.find(AddressIt->second); | ||
if (CalleeIt != BFs.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(); | ||
auto Target = OriginalTarget; | ||
// Iteratively update target since we could have f1() calling f2() | ||
// calling f3() calling f4() and we want to output f1() directly | ||
// calling f4(). | ||
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) { | ||
analyze(It.second, BC, BFs); | ||
} | ||
for (auto &It : BFs) { | ||
optimizeCalls(It.second, BC); | ||
} | ||
} | ||
}; | ||
|
||
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 { | ||
|
||
RegisterBinaryPass<OptimizeBodylessFunctions, &opts::OptimizeBodylessFunctions> | ||
RegisterOptimizeBodylessFunctions; | ||
|
||
} // namespace | ||
|
||
} // 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
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