Skip to content

Commit

Permalink
Optimize calls to functions that are a single unconditional jump
Browse files Browse the repository at this point in the history
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
Gabriel Poesia authored and maksfb committed Apr 15, 2016
1 parent 459eb8c commit e6acc7b
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 0 deletions.
30 changes: 30 additions & 0 deletions bolt/BinaryPassManager.cpp
@@ -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
92 changes: 92 additions & 0 deletions bolt/BinaryPassManager.h
@@ -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
120 changes: 120 additions & 0 deletions bolt/BinaryPasses.cpp
@@ -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
2 changes: 2 additions & 0 deletions bolt/CMakeLists.txt
Expand Up @@ -18,6 +18,8 @@ add_llvm_tool(llvm-bolt
BinaryBasicBlock.cpp
BinaryContext.cpp
BinaryFunction.cpp
BinaryPasses.cpp
BinaryPassManager.cpp
DataReader.cpp
DebugData.cpp
Exceptions.cpp
Expand Down
2 changes: 2 additions & 0 deletions bolt/RewriteInstance.cpp
Expand Up @@ -13,6 +13,7 @@
#include "BinaryBasicBlock.h"
#include "BinaryContext.h"
#include "BinaryFunction.h"
#include "BinaryPassManager.h"
#include "DataReader.h"
#include "Exceptions.h"
#include "RewriteInstance.h"
Expand Down Expand Up @@ -1053,6 +1054,7 @@ void RewriteInstance::runOptimizationPasses() {
}

// Post-processing passes.
BinaryFunctionPassManager::runAllPasses(*BC, BinaryFunctions);

// Fix the CFI state.
if (!Function.fixCFIState()) {
Expand Down

0 comments on commit e6acc7b

Please sign in to comment.