mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-27 13:40:33 +03:00

We don't have any constant pointer-bitcasts anymore for these, but can use the helper global directly.
257 lines
8.6 KiB
C++
257 lines
8.6 KiB
C++
//===-- irvar.cpp ---------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#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)
|
||
define = defineOnDeclare(V, /*isFunction=*/false);
|
||
}
|
||
|
||
if (define) {
|
||
if (V->storage_class & STCextern) {
|
||
// external
|
||
} else if (!gIR->funcGenStates.empty() &&
|
||
gIR->topfunc()->getLinkage() ==
|
||
LLGlobalValue::AvailableExternallyLinkage) {
|
||
// don't define globals while codegen'ing available_externally functions
|
||
} else {
|
||
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;
|
||
}
|
||
|
||
llvm::Type *IrGlobal::getType() {
|
||
return llvm::dyn_cast<llvm::GlobalVariable>(value)->getValueType();
|
||
}
|
||
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);
|
||
|
||
// Windows: for globals with `export` visibility, initialize the DLL storage
|
||
// class with dllimport unless the variable is defined in a root module
|
||
// (=> no extra indirection for other root modules, assuming *all* root
|
||
// modules will be linked together to one or more binaries).
|
||
// [Defining a global overrides its DLL storage class.]
|
||
bool useDLLImport = false;
|
||
if (global.params.targetTriple->isOSWindows()) {
|
||
// dllimport isn't supported for thread-local globals (MSVC++ neither)
|
||
if (!V->isThreadlocal()) {
|
||
// implicitly include extern(D) globals with -dllimport
|
||
useDLLImport =
|
||
(V->isExport() || V->_linkage == LINK::d) && dllimportDataSymbol(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
|
||
// 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(), useDLLImport);
|
||
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(llvm::MaybeAlign(DtoAlignment(V)));
|
||
|
||
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.v.tls && 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, V->isCsymbol());
|
||
|
||
// Set the initializer, swapping out the variable if the types do not
|
||
// match.
|
||
auto gvar = llvm::cast<LLGlobalVariable>(value);
|
||
gvar = gIR->setGlobalVarInitializer(gvar, initVal, V);
|
||
value = gvar;
|
||
|
||
// dllexport isn't supported for thread-local globals (MSVC++ neither);
|
||
// don't let LLVM create a useless /EXPORT directive (yields the same linker
|
||
// error anyway when trying to dllimport).
|
||
if (gvar->hasDLLExportStorageClass() && V->isThreadlocal())
|
||
gvar->setDLLStorageClass(LLGlobalValue::DefaultStorageClass);
|
||
|
||
// 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.
|
||
// Also prevent linker-level dead-symbol-elimination from stripping
|
||
// special `rt_*` druntime symbol overrides (e.g., from executables linked
|
||
// against *shared* druntime; required at least for Apple's ld64 linker).
|
||
const auto name = gvar->getName();
|
||
if (nakedUse || name == "rt_options" || name == "rt_envvars_enabled" ||
|
||
name == "rt_cmdline_enabled") {
|
||
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) {
|
||
assert(isIrVarCreated(decl));
|
||
assert(decl->ir->irVar != NULL);
|
||
return decl->ir->irVar;
|
||
}
|
||
|
||
llvm::Value *getIrValue(VarDeclaration *decl) { return getIrVar(decl)->value; }
|
||
|
||
bool isIrVarCreated(VarDeclaration *decl) {
|
||
int t = decl->ir->type();
|
||
bool isIrVar = t == IrDsymbol::GlobalType || t == IrDsymbol::LocalType ||
|
||
t == IrDsymbol::ParamterType || t == IrDsymbol::FieldType;
|
||
assert(isIrVar || t == IrDsymbol::NotSet);
|
||
return isIrVar;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
IrGlobal *getIrGlobal(VarDeclaration *decl, bool create) {
|
||
if (!isIrGlobalCreated(decl) && create) {
|
||
assert(decl->ir->irGlobal == NULL);
|
||
decl->ir->irGlobal = new IrGlobal(decl);
|
||
decl->ir->m_type = IrDsymbol::GlobalType;
|
||
}
|
||
assert(decl->ir->irGlobal != NULL);
|
||
return decl->ir->irGlobal;
|
||
}
|
||
|
||
bool isIrGlobalCreated(VarDeclaration *decl) {
|
||
int t = decl->ir->type();
|
||
assert(t == IrDsymbol::GlobalType || t == IrDsymbol::NotSet);
|
||
return t == IrDsymbol::GlobalType;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
IrLocal *getIrLocal(VarDeclaration *decl, bool create) {
|
||
if (!isIrLocalCreated(decl) && create) {
|
||
assert(decl->ir->irLocal == NULL);
|
||
decl->ir->irLocal = new IrLocal(decl);
|
||
decl->ir->m_type = IrDsymbol::LocalType;
|
||
}
|
||
assert(decl->ir->irLocal != NULL);
|
||
return decl->ir->irLocal;
|
||
}
|
||
|
||
bool isIrLocalCreated(VarDeclaration *decl) {
|
||
int t = decl->ir->type();
|
||
assert(t == IrDsymbol::LocalType || t == IrDsymbol::ParamterType ||
|
||
t == IrDsymbol::NotSet);
|
||
return t == IrDsymbol::LocalType || t == IrDsymbol::ParamterType;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
IrParameter *getIrParameter(VarDeclaration *decl, bool create) {
|
||
if (!isIrParameterCreated(decl) && create) {
|
||
assert(decl->ir->irParam == NULL);
|
||
decl->ir->irParam = new IrParameter(decl);
|
||
decl->ir->m_type = IrDsymbol::ParamterType;
|
||
}
|
||
return decl->ir->irParam;
|
||
}
|
||
|
||
bool isIrParameterCreated(VarDeclaration *decl) {
|
||
int t = decl->ir->type();
|
||
assert(t == IrDsymbol::ParamterType || t == IrDsymbol::NotSet);
|
||
return t == IrDsymbol::ParamterType;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
IrField *getIrField(VarDeclaration *decl, bool create) {
|
||
if (!isIrFieldCreated(decl) && create) {
|
||
assert(decl->ir->irField == NULL);
|
||
decl->ir->irField = new IrField(decl);
|
||
decl->ir->m_type = IrDsymbol::FieldType;
|
||
}
|
||
assert(decl->ir->irField != NULL);
|
||
return decl->ir->irField;
|
||
}
|
||
|
||
bool isIrFieldCreated(VarDeclaration *decl) {
|
||
int t = decl->ir->type();
|
||
assert(t == IrDsymbol::FieldType || t == IrDsymbol::NotSet);
|
||
return t == IrDsymbol::FieldType;
|
||
}
|