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 e6acc7b commit d1f5254
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
124 changes: 124 additions & 0 deletions bolt/BinaryPass.cpp
@@ -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
71 changes: 71 additions & 0 deletions bolt/BinaryPass.h
@@ -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
2 changes: 2 additions & 0 deletions bolt/RewriteInstance.cpp
Expand Up @@ -1069,6 +1069,8 @@ void RewriteInstance::runOptimizationPasses() {
if (opts::PrintAll || opts::PrintEHRanges)
Function.print(errs(), "after updating EH ranges", true);
}

BinaryFunctionPassManager::runAllPasses(*BC, BinaryFunctions);
}

namespace {
Expand Down

0 comments on commit d1f5254

Please sign in to comment.