mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-24 03:33:20 +01:00
Teach GlobalDCE how to remove empty global_ctor entries.
This moves most of GlobalOpt's constructor optimization code out of GlobalOpt into Transforms/Utils/CDtorUtils.{h,cpp}. The public interface is a single function OptimizeGlobalCtorsList() that takes a predicate returning which constructors to remove. GlobalOpt calls this with a function that statically evaluates all constructors, just like it did before. This part of the change is behavior-preserving. Also add a call to this from GlobalDCE with a filter that removes global constructors that contain a "ret" instruction and nothing else – this fixes PR19590. llvm-svn: 207856
This commit is contained in:
parent
f64b00f1d7
commit
fdced47a40
35
include/llvm/Transforms/Utils/CtorUtils.h
Normal file
35
include/llvm/Transforms/Utils/CtorUtils.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//===- CtorUtils.h - Helpers for working with global_ctors ------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file defines functions that are used to process llvm.global_ctors.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_TRANSFORMS_UTILS_CTOR_UTILS_H
|
||||||
|
#define LLVM_TRANSFORMS_UTILS_CTOR_UTILS_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
|
||||||
|
class GlobalVariable;
|
||||||
|
class Function;
|
||||||
|
class Module;
|
||||||
|
|
||||||
|
typedef bool (*ShouldRemoveCtor)(void *, Function *);
|
||||||
|
|
||||||
|
/// 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, ShouldRemoveCtor ShouldRemove,
|
||||||
|
void *Context);
|
||||||
|
|
||||||
|
} // End llvm namespace
|
||||||
|
|
||||||
|
#endif
|
@ -19,7 +19,9 @@
|
|||||||
#include "llvm/ADT/SmallPtrSet.h"
|
#include "llvm/ADT/SmallPtrSet.h"
|
||||||
#include "llvm/ADT/Statistic.h"
|
#include "llvm/ADT/Statistic.h"
|
||||||
#include "llvm/IR/Constants.h"
|
#include "llvm/IR/Constants.h"
|
||||||
|
#include "llvm/IR/Instructions.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/Transforms/Utils/CtorUtils.h"
|
||||||
#include "llvm/Pass.h"
|
#include "llvm/Pass.h"
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
@ -52,6 +54,15 @@ namespace {
|
|||||||
|
|
||||||
bool RemoveUnusedGlobalValue(GlobalValue &GV);
|
bool RemoveUnusedGlobalValue(GlobalValue &GV);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Returns true if F contains only a single "ret" instruction.
|
||||||
|
bool isEmptyFunction(void *Context, Function *F) {
|
||||||
|
BasicBlock &Entry = F->getEntryBlock();
|
||||||
|
if (Entry.size() != 1 || !isa<ReturnInst>(Entry.front()))
|
||||||
|
return false;
|
||||||
|
ReturnInst &RI = cast<ReturnInst>(Entry.front());
|
||||||
|
return RI.getReturnValue() == NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char GlobalDCE::ID = 0;
|
char GlobalDCE::ID = 0;
|
||||||
@ -63,6 +74,9 @@ ModulePass *llvm::createGlobalDCEPass() { return new GlobalDCE(); }
|
|||||||
bool GlobalDCE::runOnModule(Module &M) {
|
bool GlobalDCE::runOnModule(Module &M) {
|
||||||
bool Changed = false;
|
bool Changed = false;
|
||||||
|
|
||||||
|
// Remove empty functions from the global ctors list.
|
||||||
|
Changed |= optimizeGlobalCtorsList(M, isEmptyFunction, nullptr);
|
||||||
|
|
||||||
// Loop over the module, adding globals which are obviously necessary.
|
// Loop over the module, adding globals which are obviously necessary.
|
||||||
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
|
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
|
||||||
Changed |= RemoveUnusedGlobalValue(*I);
|
Changed |= RemoveUnusedGlobalValue(*I);
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "llvm/Support/MathExtras.h"
|
#include "llvm/Support/MathExtras.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include "llvm/Target/TargetLibraryInfo.h"
|
#include "llvm/Target/TargetLibraryInfo.h"
|
||||||
|
#include "llvm/Transforms/Utils/CtorUtils.h"
|
||||||
#include "llvm/Transforms/Utils/GlobalStatus.h"
|
#include "llvm/Transforms/Utils/GlobalStatus.h"
|
||||||
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -75,11 +76,9 @@ namespace {
|
|||||||
bool runOnModule(Module &M) override;
|
bool runOnModule(Module &M) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GlobalVariable *FindGlobalCtors(Module &M);
|
|
||||||
bool OptimizeFunctions(Module &M);
|
bool OptimizeFunctions(Module &M);
|
||||||
bool OptimizeGlobalVars(Module &M);
|
bool OptimizeGlobalVars(Module &M);
|
||||||
bool OptimizeGlobalAliases(Module &M);
|
bool OptimizeGlobalAliases(Module &M);
|
||||||
bool OptimizeGlobalCtorsList(GlobalVariable *&GCL);
|
|
||||||
bool ProcessGlobal(GlobalVariable *GV,Module::global_iterator &GVI);
|
bool ProcessGlobal(GlobalVariable *GV,Module::global_iterator &GVI);
|
||||||
bool ProcessInternalGlobal(GlobalVariable *GV,Module::global_iterator &GVI,
|
bool ProcessInternalGlobal(GlobalVariable *GV,Module::global_iterator &GVI,
|
||||||
const GlobalStatus &GS);
|
const GlobalStatus &GS);
|
||||||
@ -1963,116 +1962,6 @@ bool GlobalOpt::OptimizeGlobalVars(Module &M) {
|
|||||||
return Changed;
|
return Changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FindGlobalCtors - Find the llvm.global_ctors list, verifying that all
|
|
||||||
/// initializers have an init priority of 65535.
|
|
||||||
GlobalVariable *GlobalOpt::FindGlobalCtors(Module &M) {
|
|
||||||
GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
|
|
||||||
if (!GV) return nullptr;
|
|
||||||
|
|
||||||
// Verify that the initializer is simple enough for us to handle. We are
|
|
||||||
// only allowed to optimize the initializer if it is unique.
|
|
||||||
if (!GV->hasUniqueInitializer()) return nullptr;
|
|
||||||
|
|
||||||
if (isa<ConstantAggregateZero>(GV->getInitializer()))
|
|
||||||
return GV;
|
|
||||||
ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
|
|
||||||
|
|
||||||
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) {
|
|
||||||
if (isa<ConstantAggregateZero>(*i))
|
|
||||||
continue;
|
|
||||||
ConstantStruct *CS = cast<ConstantStruct>(*i);
|
|
||||||
if (isa<ConstantPointerNull>(CS->getOperand(1)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Must have a function or null ptr.
|
|
||||||
if (!isa<Function>(CS->getOperand(1)))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Init priority must be standard.
|
|
||||||
ConstantInt *CI = cast<ConstantInt>(CS->getOperand(0));
|
|
||||||
if (CI->getZExtValue() != 65535)
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ParseGlobalCtors - 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) {
|
|
||||||
if (GV->getInitializer()->isNullValue())
|
|
||||||
return std::vector<Function*>();
|
|
||||||
ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
|
|
||||||
std::vector<Function*> Result;
|
|
||||||
Result.reserve(CA->getNumOperands());
|
|
||||||
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) {
|
|
||||||
ConstantStruct *CS = cast<ConstantStruct>(*i);
|
|
||||||
Result.push_back(dyn_cast<Function>(CS->getOperand(1)));
|
|
||||||
}
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// InstallGlobalCtors - Given a specified llvm.global_ctors list, install the
|
|
||||||
/// specified array, returning the new global to use.
|
|
||||||
static GlobalVariable *InstallGlobalCtors(GlobalVariable *GCL,
|
|
||||||
const std::vector<Function*> &Ctors) {
|
|
||||||
// If we made a change, reassemble the initializer list.
|
|
||||||
Constant *CSVals[2];
|
|
||||||
CSVals[0] = ConstantInt::get(Type::getInt32Ty(GCL->getContext()), 65535);
|
|
||||||
CSVals[1] = nullptr;
|
|
||||||
|
|
||||||
StructType *StructTy =
|
|
||||||
cast<StructType>(GCL->getType()->getElementType()->getArrayElementType());
|
|
||||||
|
|
||||||
// Create the new init list.
|
|
||||||
std::vector<Constant*> CAList;
|
|
||||||
for (unsigned i = 0, e = Ctors.size(); i != e; ++i) {
|
|
||||||
if (Ctors[i]) {
|
|
||||||
CSVals[1] = Ctors[i];
|
|
||||||
} else {
|
|
||||||
Type *FTy = FunctionType::get(Type::getVoidTy(GCL->getContext()),
|
|
||||||
false);
|
|
||||||
PointerType *PFTy = PointerType::getUnqual(FTy);
|
|
||||||
CSVals[1] = Constant::getNullValue(PFTy);
|
|
||||||
CSVals[0] = ConstantInt::get(Type::getInt32Ty(GCL->getContext()),
|
|
||||||
0x7fffffff);
|
|
||||||
}
|
|
||||||
CAList.push_back(ConstantStruct::get(StructTy, CSVals));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the array initializer.
|
|
||||||
Constant *CA = ConstantArray::get(ArrayType::get(StructTy,
|
|
||||||
CAList.size()), CAList);
|
|
||||||
|
|
||||||
// If we didn't change the number of elements, don't create a new GV.
|
|
||||||
if (CA->getType() == GCL->getInitializer()->getType()) {
|
|
||||||
GCL->setInitializer(CA);
|
|
||||||
return GCL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the new global and insert it next to the existing list.
|
|
||||||
GlobalVariable *NGV = new GlobalVariable(CA->getType(), GCL->isConstant(),
|
|
||||||
GCL->getLinkage(), CA, "",
|
|
||||||
GCL->getThreadLocalMode());
|
|
||||||
GCL->getParent()->getGlobalList().insert(GCL, NGV);
|
|
||||||
NGV->takeName(GCL);
|
|
||||||
|
|
||||||
// Nuke the old list, replacing any uses with the new one.
|
|
||||||
if (!GCL->use_empty()) {
|
|
||||||
Constant *V = NGV;
|
|
||||||
if (V->getType() != GCL->getType())
|
|
||||||
V = ConstantExpr::getBitCast(V, GCL->getType());
|
|
||||||
GCL->replaceAllUsesWith(V);
|
|
||||||
}
|
|
||||||
GCL->eraseFromParent();
|
|
||||||
|
|
||||||
if (Ctors.size())
|
|
||||||
return NGV;
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
isSimpleEnoughValueToCommit(Constant *C,
|
isSimpleEnoughValueToCommit(Constant *C,
|
||||||
SmallPtrSet<Constant*, 8> &SimpleConstants,
|
SmallPtrSet<Constant*, 8> &SimpleConstants,
|
||||||
@ -2788,6 +2677,8 @@ static bool EvaluateStaticConstructor(Function *F, const DataLayout *DL,
|
|||||||
SmallVector<Constant*, 0>());
|
SmallVector<Constant*, 0>());
|
||||||
|
|
||||||
if (EvalSuccess) {
|
if (EvalSuccess) {
|
||||||
|
++NumCtorsEvaluated;
|
||||||
|
|
||||||
// We succeeded at evaluation: commit the result.
|
// We succeeded at evaluation: commit the result.
|
||||||
DEBUG(dbgs() << "FULLY EVALUATED GLOBAL CTOR FUNCTION '"
|
DEBUG(dbgs() << "FULLY EVALUATED GLOBAL CTOR FUNCTION '"
|
||||||
<< F->getName() << "' to " << Eval.getMutatedMemory().size()
|
<< F->getName() << "' to " << Eval.getMutatedMemory().size()
|
||||||
@ -2805,46 +2696,6 @@ static bool EvaluateStaticConstructor(Function *F, const DataLayout *DL,
|
|||||||
return EvalSuccess;
|
return EvalSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OptimizeGlobalCtorsList - Simplify and evaluation global ctors if possible.
|
|
||||||
/// Return true if anything changed.
|
|
||||||
bool GlobalOpt::OptimizeGlobalCtorsList(GlobalVariable *&GCL) {
|
|
||||||
std::vector<Function*> Ctors = ParseGlobalCtors(GCL);
|
|
||||||
bool MadeChange = false;
|
|
||||||
if (Ctors.empty()) return false;
|
|
||||||
|
|
||||||
// Loop over global ctors, optimizing them when we can.
|
|
||||||
for (unsigned i = 0; i != Ctors.size(); ++i) {
|
|
||||||
Function *F = Ctors[i];
|
|
||||||
// Found a null terminator in the middle of the list, prune off the rest of
|
|
||||||
// the list.
|
|
||||||
if (!F) {
|
|
||||||
if (i != Ctors.size()-1) {
|
|
||||||
Ctors.resize(i+1);
|
|
||||||
MadeChange = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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 (EvaluateStaticConstructor(F, DL, TLI)) {
|
|
||||||
Ctors.erase(Ctors.begin()+i);
|
|
||||||
MadeChange = true;
|
|
||||||
--i;
|
|
||||||
++NumCtorsEvaluated;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MadeChange) return false;
|
|
||||||
|
|
||||||
GCL = InstallGlobalCtors(GCL, Ctors);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compareNames(Constant *const *A, Constant *const *B) {
|
static int compareNames(Constant *const *A, Constant *const *B) {
|
||||||
return (*A)->getName().compare((*B)->getName());
|
return (*A)->getName().compare((*B)->getName());
|
||||||
}
|
}
|
||||||
@ -3162,9 +3013,6 @@ bool GlobalOpt::runOnModule(Module &M) {
|
|||||||
DL = DLP ? &DLP->getDataLayout() : nullptr;
|
DL = DLP ? &DLP->getDataLayout() : nullptr;
|
||||||
TLI = &getAnalysis<TargetLibraryInfo>();
|
TLI = &getAnalysis<TargetLibraryInfo>();
|
||||||
|
|
||||||
// Try to find the llvm.globalctors list.
|
|
||||||
GlobalVariable *GlobalCtors = FindGlobalCtors(M);
|
|
||||||
|
|
||||||
bool LocalChange = true;
|
bool LocalChange = true;
|
||||||
while (LocalChange) {
|
while (LocalChange) {
|
||||||
LocalChange = false;
|
LocalChange = false;
|
||||||
@ -3173,8 +3021,10 @@ bool GlobalOpt::runOnModule(Module &M) {
|
|||||||
LocalChange |= OptimizeFunctions(M);
|
LocalChange |= OptimizeFunctions(M);
|
||||||
|
|
||||||
// Optimize global_ctors list.
|
// Optimize global_ctors list.
|
||||||
if (GlobalCtors)
|
LocalChange |= optimizeGlobalCtorsList(M, [](void *C, Function *F) -> bool {
|
||||||
LocalChange |= OptimizeGlobalCtorsList(GlobalCtors);
|
GlobalOpt *self = static_cast<GlobalOpt *>(C);
|
||||||
|
return EvaluateStaticConstructor(F, self->DL, self->TLI);
|
||||||
|
}, this);
|
||||||
|
|
||||||
// Optimize non-address-taken globals.
|
// Optimize non-address-taken globals.
|
||||||
LocalChange |= OptimizeGlobalVars(M);
|
LocalChange |= OptimizeGlobalVars(M);
|
||||||
|
@ -5,6 +5,7 @@ add_llvm_library(LLVMTransformUtils
|
|||||||
BreakCriticalEdges.cpp
|
BreakCriticalEdges.cpp
|
||||||
BuildLibCalls.cpp
|
BuildLibCalls.cpp
|
||||||
BypassSlowDivision.cpp
|
BypassSlowDivision.cpp
|
||||||
|
CtorUtils.cpp
|
||||||
CloneFunction.cpp
|
CloneFunction.cpp
|
||||||
CloneModule.cpp
|
CloneModule.cpp
|
||||||
CmpInstAnalysis.cpp
|
CmpInstAnalysis.cpp
|
||||||
|
181
lib/Transforms/Utils/CtorUtils.cpp
Normal file
181
lib/Transforms/Utils/CtorUtils.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
//===- CtorUtils.cpp - Helpers for working with global_ctors ----*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file defines functions that are used to process llvm.global_ctors.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "llvm/Transforms/Utils/CtorUtils.h"
|
||||||
|
#include "llvm/IR/Constants.h"
|
||||||
|
#include "llvm/IR/Function.h"
|
||||||
|
#include "llvm/IR/GlobalVariable.h"
|
||||||
|
#include "llvm/IR/Instructions.h"
|
||||||
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/Support/Debug.h"
|
||||||
|
|
||||||
|
#define DEBUG_TYPE "ctor_utils"
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// Given a specified llvm.global_ctors list, install the
|
||||||
|
/// specified array.
|
||||||
|
void installGlobalCtors(GlobalVariable *GCL,
|
||||||
|
const std::vector<Function *> &Ctors) {
|
||||||
|
// If we made a change, reassemble the initializer list.
|
||||||
|
Constant *CSVals[2];
|
||||||
|
CSVals[0] = ConstantInt::get(Type::getInt32Ty(GCL->getContext()), 65535);
|
||||||
|
CSVals[1] = nullptr;
|
||||||
|
|
||||||
|
StructType *StructTy =
|
||||||
|
cast<StructType>(GCL->getType()->getElementType()->getArrayElementType());
|
||||||
|
|
||||||
|
// Create the new init list.
|
||||||
|
std::vector<Constant *> CAList;
|
||||||
|
for (unsigned i = 0, e = Ctors.size(); i != e; ++i) {
|
||||||
|
if (Ctors[i]) {
|
||||||
|
CSVals[1] = Ctors[i];
|
||||||
|
} else {
|
||||||
|
Type *FTy = FunctionType::get(Type::getVoidTy(GCL->getContext()), false);
|
||||||
|
PointerType *PFTy = PointerType::getUnqual(FTy);
|
||||||
|
CSVals[1] = Constant::getNullValue(PFTy);
|
||||||
|
CSVals[0] =
|
||||||
|
ConstantInt::get(Type::getInt32Ty(GCL->getContext()), 0x7fffffff);
|
||||||
|
}
|
||||||
|
CAList.push_back(ConstantStruct::get(StructTy, CSVals));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the array initializer.
|
||||||
|
Constant *CA =
|
||||||
|
ConstantArray::get(ArrayType::get(StructTy, CAList.size()), CAList);
|
||||||
|
|
||||||
|
// If we didn't change the number of elements, don't create a new GV.
|
||||||
|
if (CA->getType() == GCL->getInitializer()->getType()) {
|
||||||
|
GCL->setInitializer(CA);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new global and insert it next to the existing list.
|
||||||
|
GlobalVariable *NGV =
|
||||||
|
new GlobalVariable(CA->getType(), GCL->isConstant(), GCL->getLinkage(),
|
||||||
|
CA, "", GCL->getThreadLocalMode());
|
||||||
|
GCL->getParent()->getGlobalList().insert(GCL, NGV);
|
||||||
|
NGV->takeName(GCL);
|
||||||
|
|
||||||
|
// Nuke the old list, replacing any uses with the new one.
|
||||||
|
if (!GCL->use_empty()) {
|
||||||
|
Constant *V = NGV;
|
||||||
|
if (V->getType() != GCL->getType())
|
||||||
|
V = ConstantExpr::getBitCast(V, GCL->getType());
|
||||||
|
GCL->replaceAllUsesWith(V);
|
||||||
|
}
|
||||||
|
GCL->eraseFromParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a llvm.global_ctors list that we can understand,
|
||||||
|
/// return a list of the functions and null terminator as a vector.
|
||||||
|
std::vector<Function*> parseGlobalCtors(GlobalVariable *GV) {
|
||||||
|
if (GV->getInitializer()->isNullValue())
|
||||||
|
return std::vector<Function *>();
|
||||||
|
ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
|
||||||
|
std::vector<Function *> Result;
|
||||||
|
Result.reserve(CA->getNumOperands());
|
||||||
|
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) {
|
||||||
|
ConstantStruct *CS = cast<ConstantStruct>(*i);
|
||||||
|
Result.push_back(dyn_cast<Function>(CS->getOperand(1)));
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the llvm.global_ctors list, verifying that all initializers have an
|
||||||
|
/// init priority of 65535.
|
||||||
|
GlobalVariable *findGlobalCtors(Module &M) {
|
||||||
|
GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
|
||||||
|
if (!GV)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Verify that the initializer is simple enough for us to handle. We are
|
||||||
|
// only allowed to optimize the initializer if it is unique.
|
||||||
|
if (!GV->hasUniqueInitializer())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (isa<ConstantAggregateZero>(GV->getInitializer()))
|
||||||
|
return GV;
|
||||||
|
ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
|
||||||
|
|
||||||
|
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) {
|
||||||
|
if (isa<ConstantAggregateZero>(*i))
|
||||||
|
continue;
|
||||||
|
ConstantStruct *CS = cast<ConstantStruct>(*i);
|
||||||
|
if (isa<ConstantPointerNull>(CS->getOperand(1)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Must have a function or null ptr.
|
||||||
|
if (!isa<Function>(CS->getOperand(1)))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Init priority must be standard.
|
||||||
|
ConstantInt *CI = cast<ConstantInt>(CS->getOperand(0));
|
||||||
|
if (CI->getZExtValue() != 65535)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GV;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
/// 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, ShouldRemoveCtor ShouldRemove,
|
||||||
|
void *Context) {
|
||||||
|
GlobalVariable *GlobalCtors = findGlobalCtors(M);
|
||||||
|
if (!GlobalCtors)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<Function *> Ctors = parseGlobalCtors(GlobalCtors);
|
||||||
|
if (Ctors.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool MadeChange = false;
|
||||||
|
|
||||||
|
// Loop over global ctors, optimizing them when we can.
|
||||||
|
for (unsigned i = 0; i != Ctors.size(); ++i) {
|
||||||
|
Function *F = Ctors[i];
|
||||||
|
// Found a null terminator in the middle of the list, prune off the rest of
|
||||||
|
// the list.
|
||||||
|
if (!F) {
|
||||||
|
if (i != Ctors.size() - 1) {
|
||||||
|
Ctors.resize(i + 1);
|
||||||
|
MadeChange = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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(Context, F)) {
|
||||||
|
Ctors.erase(Ctors.begin() + i);
|
||||||
|
MadeChange = true;
|
||||||
|
--i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MadeChange)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
installGlobalCtors(GlobalCtors, Ctors);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End llvm namespace
|
14
test/Transforms/GlobalDCE/global_ctors.ll
Normal file
14
test/Transforms/GlobalDCE/global_ctors.ll
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
; RUN: opt -S -globaldce < %s | FileCheck %s
|
||||||
|
|
||||||
|
; CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @_notremovable }]
|
||||||
|
; CHECK-NOT: @_GLOBAL__I_a
|
||||||
|
|
||||||
|
declare void @_notremovable()
|
||||||
|
|
||||||
|
@llvm.global_ctors = appending global [2 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @_GLOBAL__I_a }, { i32, void ()* } { i32 65535, void ()* @_notremovable }]
|
||||||
|
|
||||||
|
; Function Attrs: nounwind readnone
|
||||||
|
define internal void @_GLOBAL__I_a() #1 section "__TEXT,__StaticInit,regular,pure_instructions" {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
45
test/Transforms/GlobalDCE/global_ctors_integration.ll
Normal file
45
test/Transforms/GlobalDCE/global_ctors_integration.ll
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
; RUN: opt -S -O2 < %s | FileCheck %s
|
||||||
|
|
||||||
|
; This test checks that -O2 is able to delete constructors that become empty
|
||||||
|
; only after some optimization passes have run, even if the pass structure
|
||||||
|
; changes.
|
||||||
|
; CHECK-NOT: @_GLOBAL__I_a
|
||||||
|
|
||||||
|
%class.Foo = type { i32 }
|
||||||
|
|
||||||
|
@foo = global %class.Foo zeroinitializer, align 4
|
||||||
|
@_ZN3Bar18LINKER_INITIALIZEDE = external constant i32
|
||||||
|
@llvm.global_ctors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @_GLOBAL__I_a }]
|
||||||
|
|
||||||
|
define internal void @__cxx_global_var_init() section "__TEXT,__StaticInit,regular,pure_instructions" {
|
||||||
|
%1 = load i32* @_ZN3Bar18LINKER_INITIALIZEDE, align 4
|
||||||
|
call void @_ZN3FooC1E17LinkerInitialized(%class.Foo* @foo, i32 %1)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: ssp uwtable
|
||||||
|
define linkonce_odr void @_ZN3FooC1E17LinkerInitialized(%class.Foo* %this, i32) unnamed_addr #0 align 2 {
|
||||||
|
%2 = alloca %class.Foo*, align 8
|
||||||
|
%3 = alloca i32, align 4
|
||||||
|
store %class.Foo* %this, %class.Foo** %2, align 8
|
||||||
|
store i32 %0, i32* %3, align 4
|
||||||
|
%4 = load %class.Foo** %2
|
||||||
|
%5 = load i32* %3, align 4
|
||||||
|
call void @_ZN3FooC2E17LinkerInitialized(%class.Foo* %4, i32 %5)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind ssp uwtable
|
||||||
|
define linkonce_odr void @_ZN3FooC2E17LinkerInitialized(%class.Foo* %this, i32) unnamed_addr #1 align 2 {
|
||||||
|
%2 = alloca %class.Foo*, align 8
|
||||||
|
%3 = alloca i32, align 4
|
||||||
|
store %class.Foo* %this, %class.Foo** %2, align 8
|
||||||
|
store i32 %0, i32* %3, align 4
|
||||||
|
%4 = load %class.Foo** %2
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @_GLOBAL__I_a() section "__TEXT,__StaticInit,regular,pure_instructions" {
|
||||||
|
call void @__cxx_global_var_init()
|
||||||
|
ret void
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user