forked from OSchip/llvm-project
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
4f51a0740a
commit
4b2acde21a
|
@ -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/Statistic.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Transforms/Utils/CtorUtils.h"
|
||||
#include "llvm/Pass.h"
|
||||
using namespace llvm;
|
||||
|
||||
|
@ -52,6 +54,15 @@ namespace {
|
|||
|
||||
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;
|
||||
|
@ -62,7 +73,10 @@ ModulePass *llvm::createGlobalDCEPass() { return new GlobalDCE(); }
|
|||
|
||||
bool GlobalDCE::runOnModule(Module &M) {
|
||||
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.
|
||||
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
|
||||
Changed |= RemoveUnusedGlobalValue(*I);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetLibraryInfo.h"
|
||||
#include "llvm/Transforms/Utils/CtorUtils.h"
|
||||
#include "llvm/Transforms/Utils/GlobalStatus.h"
|
||||
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||
#include <algorithm>
|
||||
|
@ -75,11 +76,9 @@ namespace {
|
|||
bool runOnModule(Module &M) override;
|
||||
|
||||
private:
|
||||
GlobalVariable *FindGlobalCtors(Module &M);
|
||||
bool OptimizeFunctions(Module &M);
|
||||
bool OptimizeGlobalVars(Module &M);
|
||||
bool OptimizeGlobalAliases(Module &M);
|
||||
bool OptimizeGlobalCtorsList(GlobalVariable *&GCL);
|
||||
bool ProcessGlobal(GlobalVariable *GV,Module::global_iterator &GVI);
|
||||
bool ProcessInternalGlobal(GlobalVariable *GV,Module::global_iterator &GVI,
|
||||
const GlobalStatus &GS);
|
||||
|
@ -1963,116 +1962,6 @@ bool GlobalOpt::OptimizeGlobalVars(Module &M) {
|
|||
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
|
||||
isSimpleEnoughValueToCommit(Constant *C,
|
||||
SmallPtrSet<Constant*, 8> &SimpleConstants,
|
||||
|
@ -2788,6 +2677,8 @@ static bool EvaluateStaticConstructor(Function *F, const DataLayout *DL,
|
|||
SmallVector<Constant*, 0>());
|
||||
|
||||
if (EvalSuccess) {
|
||||
++NumCtorsEvaluated;
|
||||
|
||||
// We succeeded at evaluation: commit the result.
|
||||
DEBUG(dbgs() << "FULLY EVALUATED GLOBAL CTOR FUNCTION '"
|
||||
<< F->getName() << "' to " << Eval.getMutatedMemory().size()
|
||||
|
@ -2805,46 +2696,6 @@ static bool EvaluateStaticConstructor(Function *F, const DataLayout *DL,
|
|||
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) {
|
||||
return (*A)->getName().compare((*B)->getName());
|
||||
}
|
||||
|
@ -3162,9 +3013,6 @@ bool GlobalOpt::runOnModule(Module &M) {
|
|||
DL = DLP ? &DLP->getDataLayout() : nullptr;
|
||||
TLI = &getAnalysis<TargetLibraryInfo>();
|
||||
|
||||
// Try to find the llvm.globalctors list.
|
||||
GlobalVariable *GlobalCtors = FindGlobalCtors(M);
|
||||
|
||||
bool LocalChange = true;
|
||||
while (LocalChange) {
|
||||
LocalChange = false;
|
||||
|
@ -3173,8 +3021,10 @@ bool GlobalOpt::runOnModule(Module &M) {
|
|||
LocalChange |= OptimizeFunctions(M);
|
||||
|
||||
// Optimize global_ctors list.
|
||||
if (GlobalCtors)
|
||||
LocalChange |= OptimizeGlobalCtorsList(GlobalCtors);
|
||||
LocalChange |= optimizeGlobalCtorsList(M, [](void *C, Function *F) -> bool {
|
||||
GlobalOpt *self = static_cast<GlobalOpt *>(C);
|
||||
return EvaluateStaticConstructor(F, self->DL, self->TLI);
|
||||
}, this);
|
||||
|
||||
// Optimize non-address-taken globals.
|
||||
LocalChange |= OptimizeGlobalVars(M);
|
||||
|
|
|
@ -5,6 +5,7 @@ add_llvm_library(LLVMTransformUtils
|
|||
BreakCriticalEdges.cpp
|
||||
BuildLibCalls.cpp
|
||||
BypassSlowDivision.cpp
|
||||
CtorUtils.cpp
|
||||
CloneFunction.cpp
|
||||
CloneModule.cpp
|
||||
CmpInstAnalysis.cpp
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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