Refactoring: Move IR global declaration & definition to IrGlobal

This commit is contained in:
Martin Kinkelin 2020-07-18 18:36:23 +02:00
parent a74601f91d
commit a765bf8901
7 changed files with 144 additions and 115 deletions

View file

@ -278,57 +278,13 @@ public:
"manifest constant being codegen'd!");
assert(!irs->dcomputetarget);
IrGlobal *irGlobal = getIrGlobal(decl);
LLGlobalVariable *gvar = llvm::cast<LLGlobalVariable>(irGlobal->value);
assert(gvar && "DtoResolveVariable should have created value");
if (global.params.vtls && gvar->isThreadLocal() &&
!(decl->storage_class & STCtemp)) {
const char *p = decl->loc.toChars();
message("%s: `%s` is thread local", p, decl->toChars());
}
// Check if we are defining or just declaring the global in this module.
// If we reach here during codegen of an available_externally function,
// new variable declarations should stay external and therefore must not
// have an initializer.
if (!(decl->storage_class & STCextern) && !decl->inNonRoot()) {
// Build the initializer. Might use irGlobal->value!
LLConstant *initVal =
DtoConstInitializer(decl->loc, decl->type, decl->_init);
bool define = !(decl->storage_class & STCextern) && !decl->inNonRoot();
// Cache it.
assert(!irGlobal->constInit);
irGlobal->constInit = initVal;
// Set the initializer, swapping out the variable if the types do not
// match.
irGlobal->value = irs->setGlobalVarInitializer(gvar, initVal);
// Finalize linkage & DLL storage class.
const auto lwc = DtoLinkage(decl);
setLinkage(lwc, gvar);
if (gvar->hasDLLImportStorageClass()) {
gvar->setDLLStorageClass(LLGlobalValue::DLLExportStorageClass);
}
// Hide non-exported symbols
if (opts::defaultToHiddenVisibility && !decl->isExport()) {
gvar->setVisibility(LLGlobalValue::HiddenVisibility);
}
// Also set up the debug info.
irs->DBuilder.EmitGlobalVariable(gvar, decl);
}
// If this global is used from a naked function, we need to create an
// artificial "use" for it, or it could be removed by the optimizer if
// the only reference to it is in inline asm.
if (irGlobal->nakedUse) {
irs->usedArray.push_back(gvar);
}
IF_LOG Logger::cout() << *gvar << '\n';
getIrGlobal(decl)->getValue(define);
}
}

View file

@ -131,10 +131,12 @@ bool IRState::emitArrayBoundsChecks() {
return t->ty == Tfunction && ((TypeFunction *)t)->trust == TRUST::safe;
}
LLConstant *IRState::setGlobalVarInitializer(LLGlobalVariable *&globalVar,
LLConstant *initializer) {
LLConstant *
IRState::setGlobalVarInitializer(LLGlobalVariable *&globalVar,
LLConstant *initializer,
Dsymbol *symbolForLinkageAndVisibility) {
if (initializer->getType() == globalVar->getType()->getContainedType(0)) {
globalVar->setInitializer(initializer);
defineGlobal(globalVar, initializer, symbolForLinkageAndVisibility);
return globalVar;
}
@ -142,7 +144,7 @@ LLConstant *IRState::setGlobalVarInitializer(LLGlobalVariable *&globalVar,
// It inherits most properties from the existing globalVar.
auto globalHelperVar = new LLGlobalVariable(
module, initializer->getType(), globalVar->isConstant(),
globalVar->getLinkage(), initializer, "", nullptr,
globalVar->getLinkage(), nullptr, "", nullptr,
globalVar->getThreadLocalMode());
globalHelperVar->setAlignment(LLMaybeAlign(globalVar->getAlignment()));
globalHelperVar->setComdat(globalVar->getComdat());
@ -150,6 +152,8 @@ LLConstant *IRState::setGlobalVarInitializer(LLGlobalVariable *&globalVar,
globalHelperVar->setSection(globalVar->getSection());
globalHelperVar->takeName(globalVar);
defineGlobal(globalHelperVar, initializer, symbolForLinkageAndVisibility);
// Replace all existing uses of globalVar by the bitcast pointer.
auto castHelperVar = DtoBitCast(globalHelperVar, globalVar->getType());
globalVar->replaceAllUsesWith(castHelperVar);

View file

@ -229,8 +229,10 @@ public:
// Returns either the specified globalVar if the types match, or the bitcast
// pointer replacing globalVar (and resets globalVar to the new helper
// global).
llvm::Constant *setGlobalVarInitializer(llvm::GlobalVariable *&globalVar,
llvm::Constant *initializer);
llvm::Constant *
setGlobalVarInitializer(llvm::GlobalVariable *&globalVar,
llvm::Constant *initializer,
Dsymbol *symbolForLinkageAndVisibility);
// To be called when finalizing the IR module in order to perform a second
// replacement pass for global variables replaced (and registered) by

View file

@ -861,67 +861,12 @@ void DtoResolveVariable(VarDeclaration *vd) {
}
vd->ir->setDeclared();
getIrGlobal(vd, true);
IF_LOG {
if (vd->parent) {
Logger::println("parent: %s (%s)", vd->parent->toChars(),
vd->parent->kind());
} else {
Logger::println("parent: null");
}
}
// If a const/immutable value has a proper initializer (not "= void"),
// it cannot be assigned again in a static constructor. Thus, we can
// emit it as read-only data.
// We also do so for forward-declared (extern) globals, just like clang.
const bool isLLConst = (vd->isConst() || vd->isImmutable()) &&
((vd->_init && !vd->_init->isVoidInitializer()) ||
(vd->storage_class & STCextern));
auto irGlobal = getIrGlobal(vd, true);
irGlobal->getValue();
assert(!vd->ir->isInitialized());
if (gIR->dmodule) {
vd->ir->setInitialized();
}
const auto irMangle = getIRMangledName(vd);
// Since the type of a global must exactly match the type of its
// initializer, we cannot know the type until after we have emitted the
// latter (e.g. in case of unions, …). However, it is legal for the
// initializer to refer to the address of the variable. Thus, we first
// create a global with the generic type (note the assignment to
// vd->ir->irGlobal->value!), and in case we also do an initializer
// with a different type later, swap it out and replace any existing
// uses with bitcasts to the previous type.
llvm::GlobalVariable *gvar =
declareGlobal(vd->loc, gIR->module, DtoMemType(vd->type), irMangle,
isLLConst, vd->isThreadlocal());
if (vd->llvmInternal == LLVMextern_weak)
gvar->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
auto varIr = getIrGlobal(vd);
varIr->value = gvar;
// Set the alignment (it is important not to use type->alignsize because
// VarDeclarations can have an align() attribute independent of the type
// as well).
gvar->setAlignment(LLMaybeAlign(DtoAlignment(vd)));
// Windows: initialize DLL storage class with `dllimport` for `export`ed
// symbols
if (global.params.isWindows && vd->isExport()) {
gvar->setDLLStorageClass(LLGlobalValue::DLLImportStorageClass);
}
applyVarDeclUDAs(vd, gvar);
if (varIr->dynamicCompileConst) {
addDynamicCompiledVar(gIR, varIr);
}
IF_LOG Logger::cout() << *gvar << '\n';
}
}
/******************************************************************************

View file

@ -407,7 +407,7 @@ public:
p->setStructLiteralConstant(se, globalVar);
llvm::Constant *constValue = toConstElem(se);
constValue = p->setGlobalVarInitializer(globalVar, constValue);
constValue = p->setGlobalVarInitializer(globalVar, constValue, nullptr);
p->setStructLiteralConstant(se, constValue);
result = constValue;
@ -604,7 +604,7 @@ public:
llvm::Constant *constValue =
getIrAggr(origClass)->createInitializerConstant(varInits);
constValue = p->setGlobalVarInitializer(globalVar, constValue);
constValue = p->setGlobalVarInitializer(globalVar, constValue, nullptr);
p->setStructLiteralConstant(value, constValue);
result = constValue;

View file

@ -10,12 +10,131 @@
#include "ir/irvar.h"
#include "dmd/declaration.h"
#include "dmd/errors.h"
#include "dmd/init.h"
#include "gen/dynamiccompile.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/mangling.h"
#include "gen/pragma.h"
#include "gen/uda.h"
#include "ir/irdsymbol.h"
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
LLValue *IrGlobal::getValue(bool define) {
if (!value) {
declare();
}
if (define && !(V->storage_class & STCextern)) {
auto gvar = llvm::dyn_cast<LLGlobalVariable>(value);
const bool isDefined = !gvar // bitcast pointer to a helper global
|| gvar->hasInitializer();
if (!isDefined)
this->define();
}
return value;
}
void IrGlobal::declare() {
Logger::println("Declaring global: %s", V->toChars());
LOG_SCOPE
IF_LOG {
if (V->parent) {
Logger::println("parent: %s (%s)", V->parent->toChars(),
V->parent->kind());
} else {
Logger::println("parent: null");
}
}
assert(!value);
// If a const/immutable value has a proper initializer (not "= void"),
// it cannot be assigned again in a static constructor. Thus, we can
// emit it as read-only data.
// We also do so for forward-declared (extern) globals, just like clang.
const bool isLLConst = (V->isConst() || V->isImmutable()) &&
((V->_init && !V->_init->isVoidInitializer()) ||
(V->storage_class & STCextern));
const auto irMangle = getIRMangledName(V);
// Since the type of a global must exactly match the type of its
// initializer, we cannot know the type until after we have emitted the
// latter (e.g. in case of unions, …). However, it is legal for the
// initializer to refer to the address of the variable. Thus, we first
// create a global with the generic type (note the assignment to
// vd->ir->irGlobal->value!), and in case we also do an initializer
// with a different type later, swap it out and replace any existing
// uses with bitcasts to the previous type.
LLGlobalVariable *gvar =
declareGlobal(V->loc, gIR->module, DtoMemType(V->type), irMangle,
isLLConst, V->isThreadlocal());
value = gvar;
if (V->llvmInternal == LLVMextern_weak)
gvar->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
// Set the alignment (it is important not to use type->alignsize because
// VarDeclarations can have an align() attribute independent of the type
// as well).
gvar->setAlignment(LLMaybeAlign(DtoAlignment(V)));
// Windows: initialize DLL storage class with `dllimport` for `export`ed
// symbols
if (global.params.isWindows && V->isExport()) {
gvar->setDLLStorageClass(LLGlobalValue::DLLImportStorageClass);
}
applyVarDeclUDAs(V, gvar);
if (dynamicCompileConst)
addDynamicCompiledVar(gIR, this);
IF_LOG Logger::cout() << *gvar << '\n';
}
void IrGlobal::define() {
Logger::println("Defining global: %s", V->toChars());
LOG_SCOPE
if (global.params.vtls && V->isThreadlocal() &&
!(V->storage_class & STCtemp)) {
message("%s: `%s` is thread local", V->loc.toChars(), V->toChars());
}
LLConstant *initVal = DtoConstInitializer(V->loc, V->type, V->_init);
// Set the initializer, swapping out the variable if the types do not
// match.
auto gvar = llvm::cast<LLGlobalVariable>(value);
value = gIR->setGlobalVarInitializer(gvar, initVal, V);
// Finalize DLL storage class.
if (gvar->hasDLLImportStorageClass()) {
gvar->setDLLStorageClass(LLGlobalValue::DLLExportStorageClass);
}
// If this global is used from a naked function, we need to create an
// artificial "use" for it, or it could be removed by the optimizer if
// the only reference to it is in inline asm.
if (nakedUse) {
gIR->usedArray.push_back(gvar);
}
// Also set up the debug info.
gIR->DBuilder.EmitGlobalVariable(gvar, V);
IF_LOG Logger::cout() << *gvar << '\n';
}
//////////////////////////////////////////////////////////////////////////////
IrVar *getIrVar(VarDeclaration *decl) {

View file

@ -34,12 +34,15 @@ struct IrVar {
struct IrGlobal : IrVar {
explicit IrGlobal(VarDeclaration *v) : IrVar(v) {}
llvm::Constant *constInit = nullptr;
// This var is used by a naked function.
bool nakedUse = false;
llvm::Value *getValue(bool define = false);
llvm::Type *getType() { return value->getType()->getContainedType(0); }
private:
void declare();
void define();
};
// represents a local variable variable