Skip to content

Commit

Permalink
[GlobalOpt] Enable optimization of constructors with different priori…
Browse files Browse the repository at this point in the history
…ties

Adjust `optimizeGlobalCtorsList` to handle the case of different priorities.
This addresses the issue #55083.

Test plan: ninja check-all

Differential revision: https://reviews.llvm.org/D125278
  • Loading branch information
alexander-shaposhnikov committed May 13, 2022
1 parent 4205f4a commit badd088
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 33 deletions.
6 changes: 3 additions & 3 deletions llvm/include/llvm/Transforms/Utils/CtorUtils.h
Expand Up @@ -22,9 +22,9 @@ class Module;

/// Call "ShouldRemove" for every entry in M's global_ctor list and remove the
/// entries for which it returns true. Return true if anything changed.
bool optimizeGlobalCtorsList(Module &M,
function_ref<bool(Function *)> ShouldRemove);
bool optimizeGlobalCtorsList(
Module &M, function_ref<bool(uint32_t, Function *)> ShouldRemove);

} // End llvm namespace
} // namespace llvm

#endif
6 changes: 5 additions & 1 deletion llvm/lib/Transforms/IPO/GlobalDCE.cpp
Expand Up @@ -85,6 +85,9 @@ ModulePass *llvm::createGlobalDCEPass() {

/// Returns true if F is effectively empty.
static bool isEmptyFunction(Function *F) {
// Skip external functions.
if (F->isDeclaration())
return false;
BasicBlock &Entry = F->getEntryBlock();
for (auto &I : Entry) {
if (I.isDebugOrPseudoInst())
Expand Down Expand Up @@ -297,7 +300,8 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
// marked as alive are discarded.

// Remove empty functions from the global ctors list.
Changed |= optimizeGlobalCtorsList(M, isEmptyFunction);
Changed |= optimizeGlobalCtorsList(
M, [](uint32_t, Function *F) { return isEmptyFunction(F); });

// Collect the set of members for each comdat.
for (Function &F : M)
Expand Down
18 changes: 15 additions & 3 deletions llvm/lib/Transforms/IPO/GlobalOpt.cpp
Expand Up @@ -2058,6 +2058,9 @@ OptimizeGlobalVars(Module &M,
/// can, false otherwise.
static bool EvaluateStaticConstructor(Function *F, const DataLayout &DL,
TargetLibraryInfo *TLI) {
// Skip external functions.
if (F->isDeclaration())
return false;
// Call the function.
Evaluator Eval(DL, TLI);
Constant *RetValDummy;
Expand Down Expand Up @@ -2419,6 +2422,8 @@ static bool optimizeGlobalsInModule(
SmallPtrSet<const Comdat *, 8> NotDiscardableComdats;
bool Changed = false;
bool LocalChange = true;
Optional<uint32_t> FirstNotFullyEvaluatedPriority;

while (LocalChange) {
LocalChange = false;

Expand All @@ -2441,9 +2446,16 @@ static bool optimizeGlobalsInModule(
NotDiscardableComdats);

// Optimize global_ctors list.
LocalChange |= optimizeGlobalCtorsList(M, [&](Function *F) {
return EvaluateStaticConstructor(F, DL, &GetTLI(*F));
});
LocalChange |=
optimizeGlobalCtorsList(M, [&](uint32_t Priority, Function *F) {
if (FirstNotFullyEvaluatedPriority &&
*FirstNotFullyEvaluatedPriority != Priority)
return false;
bool Evaluated = EvaluateStaticConstructor(F, DL, &GetTLI(*F));
if (!Evaluated)
FirstNotFullyEvaluatedPriority = Priority;
return Evaluated;
});

// Optimize non-address-taken globals.
LocalChange |= OptimizeGlobalVars(M, GetTTI, GetTLI, LookupDomTree,
Expand Down
46 changes: 21 additions & 25 deletions llvm/lib/Transforms/Utils/CtorUtils.cpp
Expand Up @@ -18,6 +18,7 @@
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <numeric>

#define DEBUG_TYPE "ctor_utils"

Expand Down Expand Up @@ -62,19 +63,20 @@ static void removeGlobalCtors(GlobalVariable *GCL, const BitVector &CtorsToRemov

/// Given a llvm.global_ctors list that we can understand,
/// return a list of the functions and null terminator as a vector.
static std::vector<Function *> parseGlobalCtors(GlobalVariable *GV) {
static std::vector<std::pair<uint32_t, Function *>>
parseGlobalCtors(GlobalVariable *GV) {
ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
std::vector<Function *> Result;
std::vector<std::pair<uint32_t, Function *>> Result;
Result.reserve(CA->getNumOperands());
for (auto &V : CA->operands()) {
ConstantStruct *CS = cast<ConstantStruct>(V);
Result.push_back(dyn_cast<Function>(CS->getOperand(1)));
Result.emplace_back(cast<ConstantInt>(CS->getOperand(0))->getZExtValue(),
dyn_cast<Function>(CS->getOperand(1)));
}
return Result;
}

/// Find the llvm.global_ctors list, verifying that all initializers have an
/// init priority of 65535.
/// Find the llvm.global_ctors list.
static GlobalVariable *findGlobalCtors(Module &M) {
GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
if (!GV)
Expand Down Expand Up @@ -102,49 +104,43 @@ static GlobalVariable *findGlobalCtors(Module &M) {
Function *F = dyn_cast<Function>(CS->getOperand(1));
if (!F || F->arg_size() != 0)
return nullptr;

// Init priority must be standard.
ConstantInt *CI = cast<ConstantInt>(CS->getOperand(0));
if (CI->getZExtValue() != 65535)
return nullptr;
}

return GV;
}

/// Call "ShouldRemove" for every entry in M's global_ctor list and remove the
/// entries for which it returns true. Return true if anything changed.
bool llvm::optimizeGlobalCtorsList(
Module &M, function_ref<bool(Function *)> ShouldRemove) {
Module &M, function_ref<bool(uint32_t, Function *)> ShouldRemove) {
GlobalVariable *GlobalCtors = findGlobalCtors(M);
if (!GlobalCtors)
return false;

std::vector<Function *> Ctors = parseGlobalCtors(GlobalCtors);
std::vector<std::pair<uint32_t, Function *>> Ctors =
parseGlobalCtors(GlobalCtors);
if (Ctors.empty())
return false;

bool MadeChange = false;

// Loop over global ctors, optimizing them when we can.
BitVector CtorsToRemove(Ctors.size());
for (unsigned i = 0, e = Ctors.size(); i != e; ++i) {
Function *F = Ctors[i];
// Found a null terminator in the middle of the list, prune off the rest of
// the list.
std::vector<size_t> CtorsByPriority(Ctors.size());
std::iota(CtorsByPriority.begin(), CtorsByPriority.end(), 0);
stable_sort(CtorsByPriority, [&](size_t LHS, size_t RHS) {
return Ctors[LHS].first < Ctors[RHS].first;
});
for (unsigned CtorIndex : CtorsByPriority) {
const uint32_t Priority = Ctors[CtorIndex].first;
Function *F = Ctors[CtorIndex].second;
if (!F)
continue;

LLVM_DEBUG(dbgs() << "Optimizing Global Constructor: " << *F << "\n");

// We cannot simplify external ctor functions.
if (F->empty())
continue;

// If we can evaluate the ctor at compile time, do.
if (ShouldRemove(F)) {
Ctors[i] = nullptr;
CtorsToRemove.set(i);
if (ShouldRemove(Priority, F)) {
Ctors[CtorIndex].second = nullptr;
CtorsToRemove.set(CtorIndex);
MadeChange = true;
continue;
}
Expand Down
19 changes: 18 additions & 1 deletion llvm/test/Transforms/GlobalOpt/ctor-list-opt.ll
@@ -1,7 +1,8 @@
; RUN: opt < %s -passes=globalopt -S | FileCheck %s
; CHECK-NOT: CTOR
%ini = type { i32, void()*, i8* }
@llvm.global_ctors = appending global [11 x %ini] [
@llvm.global_ctors = appending global [15 x %ini] [
%ini { i32 65534, void ()* @CTOR1, i8* null },
%ini { i32 65535, void ()* @CTOR1, i8* null },
%ini { i32 65535, void ()* @CTOR1, i8* null },
%ini { i32 65535, void ()* @CTOR2, i8* null },
Expand All @@ -12,6 +13,9 @@
%ini { i32 65535, void ()* @CTOR7, i8* null },
%ini { i32 65535, void ()* @CTOR8, i8* null },
%ini { i32 65535, void ()* @CTOR9, i8* null },
%ini { i32 65536, void ()* @CTOR10_EXTERNAL, i8* null },
%ini { i32 65536, void ()* @CTOR11, i8* null },
%ini { i32 65537, void ()* @CTOR12, i8* null },
%ini { i32 2147483647, void ()* null, i8* null }
]

Expand Down Expand Up @@ -113,3 +117,16 @@ entry:
store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @GV2, i64 1, i64 0), i8*** %3
ret void
}

; CHECK: CTOR10_EXTERNAL
declare external void @CTOR10_EXTERNAL();

; CHECK-NOT: CTOR11
define internal void @CTOR11() {
ret void
}

; CHECK: CTOR12
define internal void @CTOR12() {
ret void
}

0 comments on commit badd088

Please sign in to comment.