Skip to content

Commit

Permalink
[CodeGen] Port GlobalMerge to new pass manager (llvm#77474)
Browse files Browse the repository at this point in the history
  • Loading branch information
paperchalice authored Jan 18, 2024
1 parent d06fb0b commit bd9e145
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 83 deletions.
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/CodeGenPassBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "llvm/CodeGen/ExpandMemCmp.h"
#include "llvm/CodeGen/ExpandReductions.h"
#include "llvm/CodeGen/GCMetadata.h"
#include "llvm/CodeGen/GlobalMerge.h"
#include "llvm/CodeGen/IndirectBrExpand.h"
#include "llvm/CodeGen/InterleavedAccess.h"
#include "llvm/CodeGen/InterleavedLoadCombine.h"
Expand Down Expand Up @@ -280,6 +281,9 @@ template <typename DerivedT> class CodeGenPassBuilder {
inconvertibleErrorCode());
}

/// Target can override this to add GlobalMergePass before all IR passes.
void addGlobalMergePass(AddIRPass &) const {}

/// Add passes that optimize instruction level parallelism for out-of-order
/// targets. These passes are run while the machine code is still in SSA
/// form, so they can use MachineTraceMetrics to control their heuristics.
Expand Down Expand Up @@ -593,6 +597,7 @@ CodeGenPassBuilder<Derived>::getPassNameFromLegacyName(StringRef Name) const {

template <typename Derived>
void CodeGenPassBuilder<Derived>::addISelPasses(AddIRPass &addPass) const {
derived().addGlobalMergePass(addPass);
if (TM.useEmulatedTLS())
addPass(LowerEmuTLSPass());

Expand Down
50 changes: 50 additions & 0 deletions llvm/include/llvm/CodeGen/GlobalMerge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===- llvm/CodeGen/GlobalMerge.h -------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CODEGEN_GLOBALMERGE_H
#define LLVM_CODEGEN_GLOBALMERGE_H

#include "llvm/IR/PassManager.h"

namespace llvm {

class TargetMachine;

struct GlobalMergeOptions {
// FIXME: Infer the maximum possible offset depending on the actual users
// (these max offsets are different for the users inside Thumb or ARM
// functions), see the code that passes in the offset in the ARM backend
// for more information.
unsigned MaxOffset = 0;
bool GroupByUse = true;
bool IgnoreSingleUse = true;
bool MergeConst = false;
/// Whether we should merge global variables that have external linkage.
bool MergeExternal = true;
/// Whether we should try to optimize for size only.
/// Currently, this applies a dead simple heuristic: only consider globals
/// used in minsize functions for merging.
/// FIXME: This could learn about optsize, and be used in the cost model.
bool SizeOnly = false;
};

// FIXME: This pass must run before AsmPrinterPass::doInitialization!
class GlobalMergePass : public PassInfoMixin<GlobalMergePass> {
const TargetMachine *TM;
GlobalMergeOptions Options;

public:
GlobalMergePass(const TargetMachine *TM, GlobalMergeOptions Options)
: TM(TM), Options(Options) {}

PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
};

} // namespace llvm

#endif // LLVM_CODEGEN_GLOBALMERGE_H
3 changes: 2 additions & 1 deletion llvm/include/llvm/CodeGen/MachinePassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ MODULE_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis, (PIC))
#ifndef MODULE_PASS
#define MODULE_PASS(NAME, PASS_NAME, CONSTRUCTOR)
#endif
MODULE_PASS("pre-isel-intrinsic-lowering", PreISelIntrinsicLoweringPass, ())
MODULE_PASS("global-merge", GlobalMergePass, (TM, GlobalMergeOptions()))
MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass, ())
MODULE_PASS("lower-emutls", LowerEmuTLSPass, ())
MODULE_PASS("pre-isel-intrinsic-lowering", PreISelIntrinsicLoweringPass, ())
MODULE_PASS("shadow-stack-gc-lowering", ShadowStackGCLoweringPass, ())
#undef MODULE_PASS

Expand Down
167 changes: 85 additions & 82 deletions llvm/lib/CodeGen/GlobalMerge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
//
// ===---------------------------------------------------------------------===//

#include "llvm/CodeGen/GlobalMerge.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SetVector.h"
Expand Down Expand Up @@ -137,88 +138,99 @@ STATISTIC(NumMerged, "Number of globals merged");

namespace {

class GlobalMerge : public FunctionPass {
const TargetMachine *TM = nullptr;

// FIXME: Infer the maximum possible offset depending on the actual users
// (these max offsets are different for the users inside Thumb or ARM
// functions), see the code that passes in the offset in the ARM backend
// for more information.
unsigned MaxOffset;

/// Whether we should try to optimize for size only.
/// Currently, this applies a dead simple heuristic: only consider globals
/// used in minsize functions for merging.
/// FIXME: This could learn about optsize, and be used in the cost model.
bool OnlyOptimizeForSize = false;

/// Whether we should merge global variables that have external linkage.
bool MergeExternalGlobals = false;
class GlobalMergeImpl {
const TargetMachine *TM = nullptr;
GlobalMergeOptions Opt;
bool IsMachO = false;

private:
bool doMerge(SmallVectorImpl<GlobalVariable *> &Globals, Module &M,
bool isConst, unsigned AddrSpace) const;

/// Merge everything in \p Globals for which the corresponding bit
/// in \p GlobalSet is set.
bool doMerge(const SmallVectorImpl<GlobalVariable *> &Globals,
const BitVector &GlobalSet, Module &M, bool isConst,
unsigned AddrSpace) const;

/// Check if the given variable has been identified as must keep
/// \pre setMustKeepGlobalVariables must have been called on the Module that
/// contains GV
bool isMustKeepGlobalVariable(const GlobalVariable *GV) const {
return MustKeepGlobalVariables.count(GV);
}

bool IsMachO = false;
/// Collect every variables marked as "used" or used in a landing pad
/// instruction for this Module.
void setMustKeepGlobalVariables(Module &M);

bool doMerge(SmallVectorImpl<GlobalVariable*> &Globals,
Module &M, bool isConst, unsigned AddrSpace) const;
/// Collect every variables marked as "used"
void collectUsedGlobalVariables(Module &M, StringRef Name);

/// Merge everything in \p Globals for which the corresponding bit
/// in \p GlobalSet is set.
bool doMerge(const SmallVectorImpl<GlobalVariable *> &Globals,
const BitVector &GlobalSet, Module &M, bool isConst,
unsigned AddrSpace) const;
/// Keep track of the GlobalVariable that must not be merged away
SmallSetVector<const GlobalVariable *, 16> MustKeepGlobalVariables;

/// Check if the given variable has been identified as must keep
/// \pre setMustKeepGlobalVariables must have been called on the Module that
/// contains GV
bool isMustKeepGlobalVariable(const GlobalVariable *GV) const {
return MustKeepGlobalVariables.count(GV);
}
public:
GlobalMergeImpl(const TargetMachine *TM, GlobalMergeOptions Opt)
: TM(TM), Opt(Opt) {}
bool run(Module &M);
};

/// Collect every variables marked as "used" or used in a landing pad
/// instruction for this Module.
void setMustKeepGlobalVariables(Module &M);
class GlobalMerge : public FunctionPass {
const TargetMachine *TM = nullptr;
GlobalMergeOptions Opt;

/// Collect every variables marked as "used"
void collectUsedGlobalVariables(Module &M, StringRef Name);
public:
static char ID; // Pass identification, replacement for typeid.

/// Keep track of the GlobalVariable that must not be merged away
SmallSetVector<const GlobalVariable *, 16> MustKeepGlobalVariables;
explicit GlobalMerge() : FunctionPass(ID) {
Opt.MaxOffset = GlobalMergeMaxOffset;
initializeGlobalMergePass(*PassRegistry::getPassRegistry());
}

public:
static char ID; // Pass identification, replacement for typeid.
explicit GlobalMerge(const TargetMachine *TM, unsigned MaximalOffset,
bool OnlyOptimizeForSize, bool MergeExternalGlobals)
: FunctionPass(ID), TM(TM) {
Opt.MaxOffset = MaximalOffset;
Opt.SizeOnly = OnlyOptimizeForSize;
Opt.MergeExternal = MergeExternalGlobals;
initializeGlobalMergePass(*PassRegistry::getPassRegistry());
}

explicit GlobalMerge()
: FunctionPass(ID), MaxOffset(GlobalMergeMaxOffset) {
initializeGlobalMergePass(*PassRegistry::getPassRegistry());
}
bool doInitialization(Module &M) override {
GlobalMergeImpl P(TM, Opt);
return P.run(M);
}
bool runOnFunction(Function &F) override { return false; }

explicit GlobalMerge(const TargetMachine *TM, unsigned MaximalOffset,
bool OnlyOptimizeForSize, bool MergeExternalGlobals)
: FunctionPass(ID), TM(TM), MaxOffset(MaximalOffset),
OnlyOptimizeForSize(OnlyOptimizeForSize),
MergeExternalGlobals(MergeExternalGlobals) {
initializeGlobalMergePass(*PassRegistry::getPassRegistry());
}
StringRef getPassName() const override { return "Merge internal globals"; }

bool doInitialization(Module &M) override;
bool runOnFunction(Function &F) override;
bool doFinalization(Module &M) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
FunctionPass::getAnalysisUsage(AU);
}
};

StringRef getPassName() const override { return "Merge internal globals"; }
} // end anonymous namespace

void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
FunctionPass::getAnalysisUsage(AU);
}
};
PreservedAnalyses GlobalMergePass::run(Module &M, ModuleAnalysisManager &) {
GlobalMergeImpl P(TM, Options);
bool Changed = P.run(M);
if (!Changed)
return PreservedAnalyses::all();

} // end anonymous namespace
PreservedAnalyses PA;
PA.preserveSet<CFGAnalyses>();
return PA;
}

char GlobalMerge::ID = 0;

INITIALIZE_PASS(GlobalMerge, DEBUG_TYPE, "Merge global variables", false, false)

bool GlobalMerge::doMerge(SmallVectorImpl<GlobalVariable*> &Globals,
Module &M, bool isConst, unsigned AddrSpace) const {
bool GlobalMergeImpl::doMerge(SmallVectorImpl<GlobalVariable *> &Globals,
Module &M, bool isConst,
unsigned AddrSpace) const {
auto &DL = M.getDataLayout();
// FIXME: Find better heuristics
llvm::stable_sort(
Expand Down Expand Up @@ -333,7 +345,7 @@ bool GlobalMerge::doMerge(SmallVectorImpl<GlobalVariable*> &Globals,
Function *ParentFn = I->getParent()->getParent();

// If we're only optimizing for size, ignore non-minsize functions.
if (OnlyOptimizeForSize && !ParentFn->hasMinSize())
if (Opt.SizeOnly && !ParentFn->hasMinSize())
continue;

size_t UGSIdx = GlobalUsesByFunction[ParentFn];
Expand Down Expand Up @@ -434,9 +446,9 @@ bool GlobalMerge::doMerge(SmallVectorImpl<GlobalVariable*> &Globals,
return Changed;
}

bool GlobalMerge::doMerge(const SmallVectorImpl<GlobalVariable *> &Globals,
const BitVector &GlobalSet, Module &M, bool isConst,
unsigned AddrSpace) const {
bool GlobalMergeImpl::doMerge(const SmallVectorImpl<GlobalVariable *> &Globals,
const BitVector &GlobalSet, Module &M,
bool isConst, unsigned AddrSpace) const {
assert(Globals.size() > 1);

Type *Int32Ty = Type::getInt32Ty(M.getContext());
Expand Down Expand Up @@ -467,7 +479,7 @@ bool GlobalMerge::doMerge(const SmallVectorImpl<GlobalVariable *> &Globals,
unsigned Padding = alignTo(MergedSize, Alignment) - MergedSize;
MergedSize += Padding;
MergedSize += DL.getTypeAllocSize(Ty);
if (MergedSize > MaxOffset) {
if (MergedSize > Opt.MaxOffset) {
break;
}
if (Padding) {
Expand Down Expand Up @@ -563,7 +575,7 @@ bool GlobalMerge::doMerge(const SmallVectorImpl<GlobalVariable *> &Globals,
return Changed;
}

void GlobalMerge::collectUsedGlobalVariables(Module &M, StringRef Name) {
void GlobalMergeImpl::collectUsedGlobalVariables(Module &M, StringRef Name) {
// Extract global variables from llvm.used array
const GlobalVariable *GV = M.getGlobalVariable(Name);
if (!GV || !GV->hasInitializer()) return;
Expand All @@ -577,7 +589,7 @@ void GlobalMerge::collectUsedGlobalVariables(Module &M, StringRef Name) {
MustKeepGlobalVariables.insert(G);
}

void GlobalMerge::setMustKeepGlobalVariables(Module &M) {
void GlobalMergeImpl::setMustKeepGlobalVariables(Module &M) {
collectUsedGlobalVariables(M, "llvm.used");
collectUsedGlobalVariables(M, "llvm.compiler.used");

Expand All @@ -604,7 +616,7 @@ void GlobalMerge::setMustKeepGlobalVariables(Module &M) {
}
}

bool GlobalMerge::doInitialization(Module &M) {
bool GlobalMergeImpl::run(Module &M) {
if (!EnableGlobalMerge)
return false;

Expand Down Expand Up @@ -632,7 +644,7 @@ bool GlobalMerge::doInitialization(Module &M) {
if (TM && !TM->shouldAssumeDSOLocal(M, &GV))
continue;

if (!(MergeExternalGlobals && GV.hasExternalLinkage()) &&
if (!(Opt.MergeExternal && GV.hasExternalLinkage()) &&
!GV.hasInternalLinkage())
continue;

Expand All @@ -659,7 +671,7 @@ bool GlobalMerge::doInitialization(Module &M) {
continue;

Type *Ty = GV.getValueType();
if (DL.getTypeAllocSize(Ty) < MaxOffset) {
if (DL.getTypeAllocSize(Ty) < Opt.MaxOffset) {
if (TM &&
TargetLoweringObjectFile::getKindForGlobal(&GV, *TM).isBSS())
BSSGlobals[{AddressSpace, Section}].push_back(&GV);
Expand All @@ -686,15 +698,6 @@ bool GlobalMerge::doInitialization(Module &M) {
return Changed;
}

bool GlobalMerge::runOnFunction(Function &F) {
return false;
}

bool GlobalMerge::doFinalization(Module &M) {
MustKeepGlobalVariables.clear();
return false;
}

Pass *llvm::createGlobalMergePass(const TargetMachine *TM, unsigned Offset,
bool OnlyOptimizeForSize,
bool MergeExternalByDefault) {
Expand Down
27 changes: 27 additions & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
#include "llvm/CodeGen/ExpandLargeFpConvert.h"
#include "llvm/CodeGen/ExpandMemCmp.h"
#include "llvm/CodeGen/GCMetadata.h"
#include "llvm/CodeGen/GlobalMerge.h"
#include "llvm/CodeGen/HardwareLoops.h"
#include "llvm/CodeGen/IndirectBrExpand.h"
#include "llvm/CodeGen/InterleavedAccess.h"
Expand Down Expand Up @@ -1082,6 +1083,32 @@ Expected<bool> parseWinEHPrepareOptions(StringRef Params) {
"WinEHPreparePass");
}

Expected<GlobalMergeOptions> parseGlobalMergeOptions(StringRef Params) {
GlobalMergeOptions Result;
while (!Params.empty()) {
StringRef ParamName;
std::tie(ParamName, Params) = Params.split(';');

bool Enable = !ParamName.consume_front("no-");
if (ParamName == "group-by-use")
Result.GroupByUse = Enable;
else if (ParamName == "ignore-single-use")
Result.IgnoreSingleUse = Enable;
else if (ParamName == "merge-const")
Result.MergeConst = Enable;
else if (ParamName == "merge-external")
Result.MergeExternal = Enable;
else if (ParamName.consume_front("max-offset=")) {
if (ParamName.getAsInteger(0, Result.MaxOffset))
return make_error<StringError>(
formatv("invalid GlobalMergePass parameter '{0}' ", ParamName)
.str(),
inconvertibleErrorCode());
}
}
return Result;
}

} // namespace

/// Tests whether a pass name starts with a valid prefix for a default pipeline
Expand Down
Loading

0 comments on commit bd9e145

Please sign in to comment.