Merge branch 'master' into merge-2.070

This commit is contained in:
Johan Engelen 2016-03-29 11:48:14 +02:00
commit 15aebef5be
23 changed files with 525 additions and 230 deletions

View file

@ -17,32 +17,6 @@
#include "gen/abi-generic.h"
#include "gen/abi-aarch64.h"
namespace {
struct CompositeToArray64 : ABIRewrite {
LLValue *get(Type *dty, LLValue *v) override {
Logger::println("rewriting i64 array -> as %s", dty->toChars());
LLValue *lval = DtoRawAlloca(v->getType(), 0);
DtoStore(v, lval);
LLType *pTy = getPtrToType(DtoType(dty));
return DtoLoad(DtoBitCast(lval, pTy), "get-result");
}
LLValue *put(DValue *dv) override {
Type *dty = dv->getType();
Logger::println("rewriting %s -> as i64 array", dty->toChars());
LLType *t = type(dty, nullptr);
return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t)));
}
LLType *type(Type *t, LLType *) override {
// An i64 array that will hold Type 't'
size_t sz = (t->size() + 7) / 8;
return LLArrayType::get(LLIntegerType::get(gIR->context(), 64), sz);
}
};
}
struct AArch64TargetABI : TargetABI {
HFAToArray hfaToArray;
CompositeToArray64 compositeToArray64;

View file

@ -241,6 +241,10 @@ struct ExplicitByvalRewrite : ABIRewrite {
* float type.
*/
struct HFAToArray : ABIRewrite {
const int maxFloats = 4;
HFAToArray(const int max = 4) : maxFloats(max) {}
LLValue *get(Type *dty, LLValue *v) override {
Logger::println("rewriting array -> as HFA %s", dty->toChars());
LLValue *lval = DtoRawAlloca(v->getType(), 0);
@ -260,10 +264,37 @@ struct HFAToArray : ABIRewrite {
LLType *type(Type *dty, LLType *) override {
assert(dty->ty == Tstruct);
LLType *floatArrayType = nullptr;
if (TargetABI::isHFA((TypeStruct *)dty, &floatArrayType))
if (TargetABI::isHFA((TypeStruct *)dty, &floatArrayType, maxFloats))
return floatArrayType;
llvm_unreachable("Type dty should be an HFA");
}
};
/**
* Rewrite a composite as array of i64.
*/
struct CompositeToArray64 : ABIRewrite {
LLValue *get(Type *dty, LLValue *v) override {
Logger::println("rewriting i64 array -> as %s", dty->toChars());
LLValue *lval = DtoRawAlloca(v->getType(), 0);
DtoStore(v, lval);
LLType *pTy = getPtrToType(DtoType(dty));
return DtoLoad(DtoBitCast(lval, pTy), "get-result");
}
LLValue *put(DValue *dv) override {
Type *dty = dv->getType();
Logger::println("rewriting %s -> as i64 array", dty->toChars());
LLType *t = type(dty, nullptr);
return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t)));
}
LLType *type(Type *t, LLType *) override {
// An i64 array that will hold Type 't'
size_t sz = (t->size() + 7) / 8;
return LLArrayType::get(LLIntegerType::get(gIR->context(), 64), sz);
}
};
#endif

View file

@ -7,25 +7,32 @@
//
//===----------------------------------------------------------------------===//
//
// The ABI implementation used for 32/64 bit big-endian PowerPC targets.
//
// The System V Application Binary Interface PowerPC Processor Supplement can be
// found here:
// http://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf
//
// The PowerOpen 64bit ABI can be found here:
// http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
// http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.pdf
//
//===----------------------------------------------------------------------===//
#include "gen/abi.h"
#include "gen/abi-generic.h"
#include "gen/abi-ppc64.h"
#include "gen/abi-ppc.h"
#include "gen/dvalue.h"
#include "gen/irstate.h"
#include "gen/llvmhelpers.h"
#include "gen/tollvm.h"
struct PPC64TargetABI : TargetABI {
struct PPCTargetABI : TargetABI {
ExplicitByvalRewrite byvalRewrite;
IntegerRewrite integerRewrite;
const bool Is64Bit;
explicit PPC64TargetABI(const bool Is64Bit) : Is64Bit(Is64Bit) {}
explicit PPCTargetABI(const bool Is64Bit) : Is64Bit(Is64Bit) {}
bool returnInArg(TypeFunction *tf) override {
if (tf->isref) {
@ -81,6 +88,6 @@ struct PPC64TargetABI : TargetABI {
};
// The public getter for abi.cpp
TargetABI *getPPC64TargetABI(bool Is64Bit) {
return new PPC64TargetABI(Is64Bit);
TargetABI *getPPCTargetABI(bool Is64Bit) {
return new PPCTargetABI(Is64Bit);
}

View file

@ -7,15 +7,15 @@
//
//===----------------------------------------------------------------------===//
//
// The ABI implementation used for 64 bit PowerPC targets.
// The ABI implementation used for 32/64 bit big-endian PowerPC targets.
//
//===----------------------------------------------------------------------===//
#ifndef LDC_GEN_ABI_PPC64_H
#define LDC_GEN_ABI_PPC64_H
#ifndef LDC_GEN_ABI_PPC_H
#define LDC_GEN_ABI_PPC_H
struct TargetABI;
TargetABI *getPPC64TargetABI(bool Is64Bit);
TargetABI *getPPCTargetABI(bool Is64Bit);
#endif

107
gen/abi-ppc64le.cpp Normal file
View file

@ -0,0 +1,107 @@
//===-- abi-ppc64.cpp -----------------------------------------------------===//
//
// LDC - the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// The ABI implementation used for 64 bit little-endian PowerPC targets.
//
// The PowerOpen 64bit ELF v2 ABI can be found here:
// https://members.openpowerfoundation.org/document/dl/576
//===----------------------------------------------------------------------===//
#include "gen/abi.h"
#include "gen/abi-generic.h"
#include "gen/abi-ppc64le.h"
#include "gen/dvalue.h"
#include "gen/irstate.h"
#include "gen/llvmhelpers.h"
#include "gen/tollvm.h"
struct PPC64LETargetABI : TargetABI {
HFAToArray hfaToArray;
CompositeToArray64 compositeToArray64;
IntegerRewrite integerRewrite;
explicit PPC64LETargetABI() : hfaToArray(8) {}
bool returnInArg(TypeFunction *tf) override {
if (tf->isref) {
return false;
}
Type *rt = tf->next->toBasetype();
// FIXME: The return value of this function translates
// to RETstack or RETregs in function retStyle(), which
// directly influences if NRVO is possible or not
// (false -> RETregs -> nrvo_can = false). Depending on
// NRVO, the postblit constructor is called or not.
// Thus using the rules of the C ABI here (as mandated by
// the D specification) leads to crashes.
if (tf->linkage == LINKd)
return rt->ty == Tsarray || rt->ty == Tstruct;
return rt->ty == Tsarray || (rt->ty == Tstruct && rt->size() > 16 &&
!isHFA((TypeStruct *)rt, nullptr, 8));
}
bool passByVal(Type *t) override {
t = t->toBasetype();
return t->ty == Tsarray || (t->ty == Tstruct && t->size() > 16 &&
!isHFA((TypeStruct *)t, nullptr, 8));
}
void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override {
// RETURN VALUE
Type *retTy = fty.ret->type->toBasetype();
if (!fty.ret->byref) {
if (retTy->ty == Tstruct || retTy->ty == Tsarray) {
if (retTy->ty == Tstruct &&
isHFA((TypeStruct *)retTy, &fty.ret->ltype, 8)) {
fty.ret->rewrite = &hfaToArray;
fty.ret->ltype = hfaToArray.type(fty.ret->type, fty.ret->ltype);
} else if (canRewriteAsInt(retTy, true)) {
fty.ret->rewrite = &integerRewrite;
fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype);
} else {
fty.ret->rewrite = &compositeToArray64;
fty.ret->ltype =
compositeToArray64.type(fty.ret->type, fty.ret->ltype);
}
} else if (retTy->isintegral())
fty.ret->attrs.add(retTy->isunsigned() ? LLAttribute::ZExt
: LLAttribute::SExt);
}
// EXPLICIT PARAMETERS
for (auto arg : fty.args) {
if (!arg->byref) {
rewriteArgument(fty, *arg);
}
}
}
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
Type *ty = arg.type->toBasetype();
if (ty->ty == Tstruct || ty->ty == Tsarray) {
if (ty->ty == Tstruct && isHFA((TypeStruct *)ty, &arg.ltype, 8)) {
arg.rewrite = &hfaToArray;
arg.ltype = hfaToArray.type(arg.type, arg.ltype);
} else if (canRewriteAsInt(ty, true)) {
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
} else {
arg.rewrite = &compositeToArray64;
arg.ltype = compositeToArray64.type(arg.type, arg.ltype);
}
} else if (ty->isintegral())
arg.attrs.add(ty->isunsigned() ? LLAttribute::ZExt : LLAttribute::SExt);
}
};
// The public getter for abi.cpp
TargetABI *getPPC64LETargetABI() { return new PPC64LETargetABI(); }

21
gen/abi-ppc64le.h Normal file
View file

@ -0,0 +1,21 @@
//===-- gen/abi-ppc-64.h - PPC64 ABI description ----------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// The ABI implementation used for 64 bit little-endian PowerPC targets.
//
//===----------------------------------------------------------------------===//
#ifndef LDC_GEN_ABI_PPC64LE_H
#define LDC_GEN_ABI_PPC64LE_H
struct TargetABI;
TargetABI *getPPC64LETargetABI();
#endif

View file

@ -14,7 +14,8 @@
#include "gen/abi-aarch64.h"
#include "gen/abi-arm.h"
#include "gen/abi-mips64.h"
#include "gen/abi-ppc64.h"
#include "gen/abi-ppc.h"
#include "gen/abi-ppc64le.h"
#include "gen/abi-win64.h"
#include "gen/abi-x86-64.h"
#include "gen/abi-x86.h"
@ -166,8 +167,8 @@ bool isNestedHFA(const TypeStruct *t, d_uns64 &floatSize, int &num,
else if (sz != floatSize) // different float size, reject
return false;
if (n > 4)
return false; // too many floats for HFA, reject
//if (n > 4)
// return false; // too many floats for HFA, reject
} else {
return false; // reject all other types
}
@ -181,11 +182,12 @@ bool isNestedHFA(const TypeStruct *t, d_uns64 &floatSize, int &num,
}
}
bool TargetABI::isHFA(TypeStruct *t, llvm::Type **rewriteType) {
bool TargetABI::isHFA(TypeStruct *t, llvm::Type **rewriteType, const int maxFloats) {
d_uns64 floatSize = 0;
int num = 0;
if (isNestedHFA(t, floatSize, num, 1)) {
if (num <= maxFloats) {
if (rewriteType) {
llvm::Type *floatType = nullptr;
switch (floatSize) {
@ -205,6 +207,7 @@ bool TargetABI::isHFA(TypeStruct *t, llvm::Type **rewriteType) {
}
return true;
}
}
return false;
}
@ -336,9 +339,11 @@ TargetABI *TargetABI::getTarget() {
case llvm::Triple::mips64:
case llvm::Triple::mips64el:
return getMIPS64TargetABI(global.params.is64bit);
case llvm::Triple::ppc:
case llvm::Triple::ppc64:
return getPPCTargetABI(global.params.targetTriple->isArch64Bit());
case llvm::Triple::ppc64le:
return getPPC64TargetABI(global.params.targetTriple->isArch64Bit());
return getPPC64LETargetABI();
#if LDC_LLVM_VER == 305
case llvm::Triple::arm64:
case llvm::Triple::arm64_be:

View file

@ -154,7 +154,7 @@ struct TargetABI {
/// Check if struct 't' is a Homogeneous Floating-point Aggregate (HFA)
/// consisting of up to 4 of same floating point type. If so, optionally
/// produce the rewriteType: an array of that floating point type
static bool isHFA(TypeStruct *t, llvm::Type **rewriteType = nullptr);
static bool isHFA(TypeStruct *t, llvm::Type **rewriteType = nullptr, const int maxFloats = 4);
protected:

View file

@ -15,7 +15,29 @@
#include "gen/logger.h"
#include "gen/tollvm.h"
namespace {
bool isDefinedInFuncEntryBB(llvm::Value *v) {
auto instr = llvm::dyn_cast<llvm::Instruction>(v);
if (!instr) {
// Global, constant, ...
return true;
}
auto bb = instr->getParent();
if (bb != &(bb->getParent()->getEntryBlock())) {
return false;
}
// An invoke instruction in the entry BB does not necessarily dominate the
// rest of the function because of the failure path.
return !llvm::isa<llvm::InvokeInst>(instr);
}
}
////////////////////////////////////////////////////////////////////////////////
bool DImValue::definedInFuncEntryBB() { return isDefinedInFuncEntryBB(val); }
////////////////////////////////////////////////////////////////////////////////
static bool checkVarValueType(LLType *t, bool extraDeref) {
@ -41,29 +63,19 @@ static bool checkVarValueType(LLType *t, bool extraDeref) {
return true;
}
DVarValue::DVarValue(Type *t, VarDeclaration *vd, LLValue *llvmValue)
: DValue(t), var(vd), val(llvmValue) {
assert(checkVarValueType(llvmValue->getType(), isSpecialRefVar(vd)));
DVarValue::DVarValue(Type *t, LLValue *llvmValue, bool isSpecialRefVar)
: DValue(t), val(llvmValue), isSpecialRefVar(isSpecialRefVar) {
assert(llvmValue && "Unexpected null llvm::Value.");
assert(checkVarValueType(llvmValue->getType(), isSpecialRefVar));
}
DVarValue::DVarValue(Type *t, LLValue *llvmValue)
: DValue(t), var(nullptr), val(llvmValue) {
assert(checkVarValueType(llvmValue->getType(), false));
}
LLValue *DVarValue::getLVal() {
assert(val);
if (var && isSpecialRefVar(var)) {
return DtoLoad(val);
}
return val;
}
LLValue *DVarValue::getLVal() { return isSpecialRefVar ? DtoLoad(val) : val; }
LLValue *DVarValue::getRVal() {
assert(val);
llvm::Value *storage = val;
if (var && isSpecialRefVar(var)) {
if (isSpecialRefVar) {
storage = DtoLoad(storage);
}
@ -83,12 +95,12 @@ LLValue *DVarValue::getRVal() {
}
LLValue *DVarValue::getRefStorage() {
assert(val);
assert(isSpecialRefVar(var));
assert(isSpecialRefVar);
return val;
}
////////////////////////////////////////////////////////////////////////////////
bool DVarValue::definedInFuncEntryBB() { return isDefinedInFuncEntryBB(val); }
////////////////////////////////////////////////////////////////////////////////
LLValue *DSliceValue::getRVal() {
@ -97,7 +109,10 @@ LLValue *DSliceValue::getRVal() {
return DtoAggrPair(len, ptr);
}
////////////////////////////////////////////////////////////////////////////////
bool DSliceValue::definedInFuncEntryBB() {
return isDefinedInFuncEntryBB(len) && isDefinedInFuncEntryBB(ptr);
}
////////////////////////////////////////////////////////////////////////////////
DFuncValue::DFuncValue(Type *t, FuncDeclaration *fd, llvm::Value *v,
@ -112,7 +127,18 @@ LLValue *DFuncValue::getRVal() {
return val;
}
////////////////////////////////////////////////////////////////////////////////
bool DFuncValue::definedInFuncEntryBB() {
if (!isDefinedInFuncEntryBB(val)) {
return false;
}
if (vthis && !isDefinedInFuncEntryBB(vthis)) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
LLValue *DConstValue::getRVal() {

View file

@ -35,7 +35,6 @@ class DImValue;
class DConstValue;
class DNullValue;
class DVarValue;
class DFieldValue;
class DFuncValue;
class DSliceValue;
@ -62,11 +61,20 @@ public:
virtual bool isLVal() { return false; }
/// Returns true iff the value can be accessed at the end of the entry basic
/// block of the current function, in the sense that it is either not derived
/// from an llvm::Instruction (but from a global, constant, etc.) or that
/// instruction is part of the entry basic block.
///
/// In other words, whatever value the result of getLVal()/getRVal() might be
/// derived from then certainly dominates uses in all other basic blocks of
/// the function.
virtual bool definedInFuncEntryBB() = 0;
virtual DImValue *isIm() { return nullptr; }
virtual DConstValue *isConst() { return nullptr; }
virtual DNullValue *isNull() { return nullptr; }
virtual DVarValue *isVar() { return nullptr; }
virtual DFieldValue *isField() { return nullptr; }
virtual DSliceValue *isSlice() { return nullptr; }
virtual DFuncValue *isFunc() { return nullptr; }
@ -89,6 +97,8 @@ public:
return val;
}
bool definedInFuncEntryBB() override;
DImValue *isIm() override { return this; }
protected:
@ -102,6 +112,8 @@ public:
llvm::Value *getRVal() override;
bool definedInFuncEntryBB() override { return true; }
DConstValue *isConst() override { return this; }
llvm::Constant *c;
@ -114,11 +126,14 @@ public:
DNullValue *isNull() override { return this; }
};
// variable d-value
/// This is really a misnomer, DVarValue represents generic lvalues, which
/// might or might not come from variable declarations.
// TODO: Rename this, probably remove getLVal() from parent since this is the
// only lvalue. The isSpecialRefVar case should probably also be its own
// subclass.
class DVarValue : public DValue {
public:
DVarValue(Type *t, VarDeclaration *vd, llvm::Value *llvmValue);
DVarValue(Type *t, llvm::Value *llvmValue);
DVarValue(Type *t, llvm::Value *llvmValue, bool isSpecialRefVar = false);
bool isLVal() override { return true; }
llvm::Value *getLVal() override;
@ -126,21 +141,15 @@ public:
/// Returns the underlying storage for special internal ref variables.
/// Illegal to call on any other value.
virtual llvm::Value *getRefStorage();
llvm::Value *getRefStorage();
bool definedInFuncEntryBB() override;
DVarValue *isVar() override { return this; }
VarDeclaration *var;
protected:
llvm::Value *val;
};
// field d-value
class DFieldValue : public DVarValue {
public:
DFieldValue(Type *t, llvm::Value *llvmValue) : DVarValue(t, llvmValue) {}
DFieldValue *isField() override { return this; }
llvm::Value *const val;
bool const isSpecialRefVar;
};
// slice d-value
@ -151,6 +160,8 @@ public:
llvm::Value *getRVal() override;
bool definedInFuncEntryBB() override;
DSliceValue *isSlice() override { return this; }
llvm::Value *len;
@ -166,6 +177,8 @@ public:
llvm::Value *getRVal() override;
bool definedInFuncEntryBB() override;
DFuncValue *isFunc() override { return this; }
FuncDeclaration *func;

View file

@ -10,11 +10,7 @@
#include "gen/llvmhelpers.h"
#include "declaration.h"
#include "expression.h"
#include "id.h"
#include "init.h"
#include "mars.h"
#include "module.h"
#include "template.h"
#include "gen/abi.h"
#include "gen/arrays.h"
#include "gen/classes.h"
#include "gen/complex.h"
@ -30,10 +26,14 @@
#include "gen/tollvm.h"
#include "gen/typeinf.h"
#include "gen/uda.h"
#include "gen/abi.h"
#include "id.h"
#include "init.h"
#include "ir/irfunction.h"
#include "ir/irmodule.h"
#include "ir/irtypeaggr.h"
#include "mars.h"
#include "module.h"
#include "template.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
@ -884,23 +884,21 @@ void DtoVarDeclaration(VarDeclaration *vd) {
if (isIrLocalCreated(vd)) {
// Nothing to do if it has already been allocated.
}
/* Named Return Value Optimization (NRVO):
T f(){
T ret; // &ret == hidden pointer
ret = ...
return ret; // NRVO.
}
*/
else if (gIR->func()->retArg && gIR->func()->decl->nrvo_can &&
} else if (gIR->func()->retArg && gIR->func()->decl->nrvo_can &&
gIR->func()->decl->nrvo_var == vd) {
// Named Return Value Optimization (NRVO):
// T f() {
// T ret; // &ret == hidden pointer
// ret = ...
// return ret; // NRVO.
// }
assert(!isSpecialRefVar(vd) && "Can this happen?");
IrLocal *irLocal = getIrLocal(vd, true);
irLocal->value = gIR->func()->retArg;
getIrLocal(vd, true)->value = gIR->func()->retArg;
}
// normal stack variable, allocate storage on the stack if it has not already
// been done
else {
// normal stack variable, allocate storage on the stack if it has not
// already been done
IrLocal *irLocal = getIrLocal(vd, true);
Type *type = isSpecialRefVar(vd) ? vd->type->pointerTo() : vd->type;
@ -978,7 +976,6 @@ DValue *DtoDeclarationExp(Dsymbol *declaration) {
IF_LOG Logger::print("DtoDeclarationExp: %s\n", declaration->toChars());
LOG_SCOPE;
// variable declaration
if (VarDeclaration *vd = declaration->isVarDeclaration()) {
Logger::println("VarDeclaration");
@ -1000,25 +997,19 @@ DValue *DtoDeclarationExp(Dsymbol *declaration) {
} else {
DtoVarDeclaration(vd);
}
return new DVarValue(vd->type, vd, getIrValue(vd));
return makeVarDValue(vd->type, vd);
}
// struct declaration
if (StructDeclaration *s = declaration->isStructDeclaration()) {
Logger::println("StructDeclaration");
Declaration_codegen(s);
}
// function declaration
else if (FuncDeclaration *f = declaration->isFuncDeclaration()) {
} else if (FuncDeclaration *f = declaration->isFuncDeclaration()) {
Logger::println("FuncDeclaration");
Declaration_codegen(f);
}
// class
else if (ClassDeclaration *e = declaration->isClassDeclaration()) {
} else if (ClassDeclaration *e = declaration->isClassDeclaration()) {
Logger::println("ClassDeclaration");
Declaration_codegen(e);
}
// attribute declaration
else if (AttribDeclaration *a = declaration->isAttribDeclaration()) {
} else if (AttribDeclaration *a = declaration->isAttribDeclaration()) {
Logger::println("AttribDeclaration");
// choose the right set in case this is a conditional declaration
Dsymbols *d = a->include(nullptr, nullptr);
@ -1027,17 +1018,13 @@ DValue *DtoDeclarationExp(Dsymbol *declaration) {
DtoDeclarationExp((*d)[i]);
}
}
}
// mixin declaration
else if (TemplateMixin *m = declaration->isTemplateMixin()) {
} else if (TemplateMixin *m = declaration->isTemplateMixin()) {
Logger::println("TemplateMixin");
for (unsigned i = 0; i < m->members->dim; ++i) {
Dsymbol *mdsym = static_cast<Dsymbol *>(m->members->data[i]);
DtoDeclarationExp(mdsym);
}
}
// tuple declaration
else if (TupleDeclaration *tupled = declaration->isTupleDeclaration()) {
} else if (TupleDeclaration *tupled = declaration->isTupleDeclaration()) {
Logger::println("TupleDeclaration");
assert(tupled->isexp && "Non-expression tuple decls not handled yet.");
assert(tupled->objects);
@ -1522,25 +1509,29 @@ DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl) {
if (vd->ident == Id::_arguments && gIR->func()->_arguments) {
Logger::println("Id::_arguments");
LLValue *v = gIR->func()->_arguments;
return new DVarValue(type, vd, v);
assert(!isSpecialRefVar(vd) && "Code not expected to handle special ref "
"vars, although it can easily be made "
"to.");
return new DVarValue(type, v);
}
// _argptr
if (vd->ident == Id::_argptr && gIR->func()->_argptr) {
Logger::println("Id::_argptr");
LLValue *v = gIR->func()->_argptr;
return new DVarValue(type, vd, v);
assert(!isSpecialRefVar(vd) && "Code not expected to handle special ref "
"vars, although it can easily be made "
"to.");
return new DVarValue(type, v);
}
// _dollar
if (vd->ident == Id::dollar) {
Logger::println("Id::dollar");
LLValue *val = nullptr;
if (isIrVarCreated(vd) && (val = getIrValue(vd))) {
// It must be length of a range
return new DVarValue(type, vd, val);
if (isIrVarCreated(vd)) {
// This is the length of a range.
return makeVarDValue(type, vd);
}
assert(!gIR->arrays.empty());
val = DtoArrayLen(gIR->arrays.back());
return new DImValue(type, val);
return new DImValue(type, DtoArrayLen(gIR->arrays.back()));
}
// typeinfo
if (TypeInfoDeclaration *tid = vd->isTypeInfoDeclaration()) {
@ -1577,7 +1568,10 @@ DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl) {
}
if (vd->isRef() || vd->isOut() || DtoIsInMemoryOnly(vd->type) ||
llvm::isa<llvm::AllocaInst>(getIrValue(vd))) {
return new DVarValue(type, vd, getIrValue(vd));
assert(!isSpecialRefVar(vd) && "Code not expected to handle special "
"ref vars, although it can easily be "
"made to.");
return new DVarValue(type, getIrValue(vd));
}
if (llvm::isa<llvm::Argument>(getIrValue(vd))) {
return new DImValue(type, getIrValue(vd));
@ -1588,32 +1582,11 @@ DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl) {
Logger::println("a normal variable");
// take care of forward references of global variables
const bool isGlobal = vd->isDataseg() || (vd->storage_class & STCextern);
if (isGlobal) {
if (vd->isDataseg() || (vd->storage_class & STCextern)) {
DtoResolveVariable(vd);
}
assert(isIrVarCreated(vd) && "Variable not resolved.");
llvm::Value *val = getIrValue(vd);
assert(val && "Variable value not set yet.");
if (isGlobal) {
llvm::Type *expectedType =
llvm::PointerType::getUnqual(DtoMemType(type));
// The type of globals is determined by their initializer, so
// we might need to cast. Make sure that the type sizes fit -
// '==' instead of '<=' should probably work as well.
if (val->getType() != expectedType) {
llvm::Type *t =
llvm::cast<llvm::PointerType>(val->getType())->getElementType();
assert(getTypeStoreSize(DtoType(type)) <= getTypeStoreSize(t) &&
"Global type mismatch, encountered type too small.");
val = DtoBitCast(val, expectedType);
}
}
return new DVarValue(type, vd, val);
return makeVarDValue(type, vd);
}
}
@ -1845,3 +1818,28 @@ unsigned getFieldGEPIndex(AggregateDeclaration *ad, VarDeclaration *vd) {
assert(byteOffset == 0 && "Cannot address field by a simple GEP.");
return fieldIndex;
}
DValue *makeVarDValue(Type *type, VarDeclaration *vd, llvm::Value *storage) {
auto val = storage;
if (!val) {
assert(isIrVarCreated(vd) && "Variable not resolved.");
val = getIrValue(vd);
}
if (vd->isDataseg() || (vd->storage_class & STCextern)) {
// The type of globals is determined by their initializer, so
// we might need to cast. Make sure that the type sizes fit -
// '==' instead of '<=' should probably work as well.
llvm::Type *expectedType = llvm::PointerType::getUnqual(DtoMemType(type));
if (val->getType() != expectedType) {
llvm::Type *t =
llvm::cast<llvm::PointerType>(val->getType())->getElementType();
assert(getTypeStoreSize(DtoType(type)) <= getTypeStoreSize(t) &&
"Global type mismatch, encountered type too small.");
val = DtoBitCast(val, expectedType);
}
}
return new DVarValue(type, val, isSpecialRefVar(vd));
}

View file

@ -16,11 +16,11 @@
#ifndef LDC_GEN_LLVMHELPERS_H
#define LDC_GEN_LLVMHELPERS_H
#include "mtype.h"
#include "statement.h"
#include "gen/dvalue.h"
#include "gen/llvm.h"
#include "ir/irfuncty.h"
#include "mtype.h"
#include "statement.h"
struct IRState;
@ -262,4 +262,11 @@ DValue *toElem(Expression *e, bool tryGetLvalue);
DValue *toElemDtor(Expression *e);
LLConstant *toConstElem(Expression *e, IRState *p);
/// Creates a DVarValue for the given VarDeclaration.
///
/// If the storage is not given explicitly, the declaration is expected to be
/// already resolved, and the value from the associated IrVar will be used.
DValue *makeVarDValue(Type *type, VarDeclaration *vd,
llvm::Value *storage = nullptr);
#endif

View file

@ -62,8 +62,7 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd,
if (fd->isStatic()) {
error(loc, "function %s cannot access frame of function %s",
irfunc->decl->toPrettyChars(), vdparent->toPrettyChars());
return new DVarValue(astype, vd,
llvm::UndefValue::get(DtoPtrToType(astype)));
return new DVarValue(astype, llvm::UndefValue::get(DtoPtrToType(astype)));
}
fd = getParentFunc(fd, false);
assert(fd);
@ -71,8 +70,7 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd,
// is the nested variable in this scope?
if (vdparent == irfunc->decl) {
LLValue *val = getIrValue(vd);
return new DVarValue(astype, vd, val);
return makeVarDValue(astype, vd);
}
LLValue *dwarfValue = nullptr;
@ -120,8 +118,9 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd,
Logger::cout() << "of type: " << *irfunc->frameType << '\n';
}
unsigned vardepth = getIrLocal(vd)->nestedDepth;
unsigned funcdepth = irfunc->depth;
IrLocal *const irLocal = getIrLocal(vd);
const auto vardepth = irLocal->nestedDepth;
const auto funcdepth = irfunc->depth;
IF_LOG {
Logger::cout() << "Variable: " << vd->toChars() << '\n';
@ -148,7 +147,7 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd,
IF_LOG Logger::cout() << "Frame: " << *val << '\n';
}
int idx = getIrLocal(vd)->nestedIndex;
const auto idx = irLocal->nestedIndex;
assert(idx != -1 && "Nested context not yet resolved for variable.");
if (dwarfValue && global.params.symdebug) {
@ -165,8 +164,8 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd,
val = DtoAlignedLoad(val);
// dwarfOpDeref(dwarfAddr);
IF_LOG {
Logger::cout() << "Was byref, now: " << *val << '\n';
Logger::cout() << "of type: " << *val->getType() << '\n';
Logger::cout() << "Was byref, now: " << *irLocal->value << '\n';
Logger::cout() << "of type: " << *irLocal->value->getType() << '\n';
}
}
@ -174,7 +173,7 @@ DValue *DtoNestedVariable(Loc &loc, Type *astype, VarDeclaration *vd,
gIR->DBuilder.EmitLocalVariable(dwarfValue, vd, nullptr, false, dwarfAddr);
}
return new DVarValue(astype, vd, val);
return makeVarDValue(astype, vd, val);
}
void DtoResolveNestedContext(Loc &loc, AggregateDeclaration *decl,

View file

@ -9,15 +9,6 @@
#include "attrib.h"
#include "enum.h"
#include "hdrgen.h"
#include "id.h"
#include "init.h"
#include "mtype.h"
#include "ldcbindings.h"
#include "module.h"
#include "port.h"
#include "rmem.h"
#include "template.h"
#include "gen/aa.h"
#include "gen/abi.h"
#include "gen/arrays.h"
@ -38,9 +29,18 @@
#include "gen/tollvm.h"
#include "gen/typeinf.h"
#include "gen/warnings.h"
#include "hdrgen.h"
#include "id.h"
#include "init.h"
#include "ir/irfunction.h"
#include "ir/irtypeclass.h"
#include "ir/irtypestruct.h"
#include "ldcbindings.h"
#include "module.h"
#include "mtype.h"
#include "port.h"
#include "rmem.h"
#include "template.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include <fstream>
@ -157,7 +157,9 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd,
}
// get a pointer to this field
DVarValue field(vd->type, vd, DtoIndexAggregate(mem, sd, vd));
assert(!isSpecialRefVar(vd) && "Code not expected to handle special ref "
"vars, although it can easily be made to.");
DVarValue field(vd->type, DtoIndexAggregate(mem, sd, vd));
// store the initializer there
DtoAssign(loc, &field, val, TOKconstruct, true);
@ -287,11 +289,17 @@ public:
DValue *getResult() {
if (destructTemporaries &&
p->func()->scopes->currentCleanupScope() != initialCleanupScope) {
// If the results is an (LLVM) r-value, temporarily store it in an
// alloca slot to avoid running into instruction dominance issues
// if we share the cleanups with another exit path (e.g. unwinding).
// We might share the CFG edges through the below cleanup blocks with
// other paths (e.g. exception unwinding) where the result value has not
// been constructed. At runtime, the branches will be chosen such that the
// end bb (which will likely go on to access the value) is never executed
// in those other cases, but we need to make sure that the SSA is also
// well-formed statically (i.e. all instructions dominate their uses).
// Thus, dump the result to a temporary stack slot (created in the entry
// bb) if it is not guaranteed to dominate the end bb after possibly
// adding more control flow.
if (result && result->getType()->ty != Tvoid &&
(result->isIm() || result->isSlice())) {
!result->definedInFuncEntryBB()) {
LLValue *alloca = DtoAllocaDump(result);
result = new DVarValue(result->getType(), alloca);
}
@ -322,15 +330,12 @@ public:
result = DtoDeclarationExp(e->declaration);
if (result) {
if (DVarValue *varValue = result->isVar()) {
VarDeclaration *vd = varValue->var;
if (auto vd = e->declaration->isVarDeclaration()) {
if (!vd->isDataseg() && vd->edtor && !vd->noscope) {
pushVarDtorCleanup(p, vd);
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
@ -1045,11 +1050,6 @@ public:
}
DValue *v = toElem(e->e1, true);
if (v->isField()) {
Logger::println("is field");
result = v;
return;
}
if (DFuncValue *fv = v->isFunc()) {
Logger::println("is func");
// Logger::println("FuncDeclaration");
@ -1139,9 +1139,10 @@ public:
if (e->cachedLvalue) {
Logger::println("using cached lvalue");
LLValue *V = e->cachedLvalue;
VarDeclaration *vd = e->var->isVarDeclaration();
assert(vd);
result = new DVarValue(e->type, vd, V);
assert(!isSpecialRefVar(e->var->isVarDeclaration()) &&
"Code not expected to handle special ref vars, although it can "
"easily be made to.");
result = new DVarValue(e->type, V);
return;
}
@ -1174,7 +1175,7 @@ public:
}
// Logger::cout() << "mem: " << *arrptr << '\n';
result = new DVarValue(e->type, vd, arrptr);
result = new DVarValue(e->type, arrptr);
} else if (FuncDeclaration *fdecl = e->var->isFuncDeclaration()) {
DtoResolveFunction(fdecl);
@ -1236,7 +1237,10 @@ public:
Logger::println("normal this exp");
v = p->func()->thisArg;
}
result = new DVarValue(e->type, vd, v);
assert(!isSpecialRefVar(vd) && "Code not expected to handle special ref "
"vars, although it can easily be made "
"to.");
result = new DVarValue(e->type, v);
} else {
llvm_unreachable("No VarDeclaration in ThisExp.");
}
@ -1294,25 +1298,20 @@ public:
e->type->toChars());
LOG_SCOPE;
// this is the new slicing code, it's different in that a full slice will no
// longer retain the original pointer.
// but this was broken if there *was* no original pointer, ie. a slice of a
// slice...
// now all slices have *both* the 'len' and 'ptr' fields set to != null.
// value being sliced
LLValue *elen = nullptr;
LLValue *eptr;
DValue *v = toElem(e->e1);
// handle pointer slicing
Type *etype = e->e1->type->toBasetype();
if (etype->ty == Tpointer) {
// pointer slicing
assert(e->lwr);
eptr = v->getRVal();
}
// array slice
else {
// array slice
eptr = DtoArrayPtr(v);
}
@ -1373,8 +1372,7 @@ public:
// no bounds or full slice -> just convert to slice
else {
assert(e->e1->type->toBasetype()->ty != Tpointer);
// if the sliceee is a static array, we use the length of that as DMD
// seems
// if the slicee is a static array, we use the length of that as DMD seems
// to give contrary inconsistent sizesin some multidimensional static
// array cases.
// (namely default initialization, int[16][16] arr; -> int[256] arr = 0;)
@ -1785,12 +1783,14 @@ public:
if (tc->sym->isInterfaceDeclaration()) {
DtoDeleteInterface(e->loc, dval);
onstack = true;
} else if (DVarValue *vv = dval->isVar()) {
if (vv->var && vv->var->onstack) {
} else if (e->e1->op == TOKvar) {
if (auto vd = static_cast<VarExp *>(e->e1)->var->isVarDeclaration()) {
if (vd->onstack) {
DtoFinalizeClass(e->loc, dval->getRVal());
onstack = true;
}
}
}
if (!onstack) {
DtoDeleteClass(e->loc, dval); // sets dval to null
@ -1896,8 +1896,9 @@ public:
// struct invariants
else if (global.params.useInvariants && condty->ty == Tpointer &&
condty->nextOf()->ty == Tstruct &&
(invdecl = static_cast<TypeStruct *>(condty->nextOf())
->sym->inv) != nullptr) {
(invdecl =
static_cast<TypeStruct *>(condty->nextOf())->sym->inv) !=
nullptr) {
Logger::print("calling struct invariant");
DtoResolveFunction(invdecl);
DFuncValue invfunc(invdecl, getIrFunc(invdecl)->func, cond->getRVal());
@ -2266,7 +2267,7 @@ public:
p->scope() = IRScope(condend);
if (retPtr) {
result = new DVarValue(e->type, DtoLoad(retPtr));
result = new DVarValue(e->type, retPtr, true);
} else {
result = new DConstValue(e->type, getNullValue(DtoMemType(dtype)));
}

View file

@ -29,6 +29,7 @@
#include "gen/runtime.h"
#include "gen/structs.h"
#include "gen/typeinf.h"
#include "gen/uda.h"
#include "ir/irtype.h"
#include "ir/irtypeclass.h"
#include "ir/irtypefunction.h"
@ -247,6 +248,12 @@ LLValue *DtoDelegateEquals(TOK op, LLValue *lhs, LLValue *rhs) {
LinkageWithCOMDAT DtoLinkage(Dsymbol *sym) {
auto linkage = (DtoIsTemplateInstance(sym) ? templateLinkage
: LLGlobalValue::ExternalLinkage);
// If @(ldc.attributes.weak) is applied, override the linkage to WeakAny
if (hasWeakUDA(sym)) {
linkage = LLGlobalValue::WeakAnyLinkage;
}
return {linkage, supportsCOMDAT()};
}

View file

@ -15,10 +15,11 @@ namespace {
namespace attr {
const std::string section = "section";
const std::string target = "target";
const std::string weak = "_weak";
}
bool isFromLdcAttibutes(StructLiteralExp *e) {
auto moduleDecl = e->sd->getModule()->md;
/// Checks whether `moduleDecl` is the ldc.attributes module.
bool isLdcAttibutes(const ModuleDeclaration *moduleDecl) {
if (!moduleDecl)
return false;
@ -33,6 +34,12 @@ bool isFromLdcAttibutes(StructLiteralExp *e) {
return true;
}
/// Checks whether the type of `e` is a struct from the ldc.attributes module.
bool isFromLdcAttibutes(const StructLiteralExp *e) {
auto moduleDecl = e->sd->getModule()->md;
return isLdcAttibutes(moduleDecl);
}
StructLiteralExp *getLdcAttributesStruct(Expression *attr) {
// See whether we can evaluate the attribute at compile-time. All the LDC
// attributes are struct literals that may be constructed using a CTFE
@ -171,6 +178,8 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) {
} else if (name == attr::target) {
sle->error("Special attribute 'ldc.attributes.target' is only valid for "
"functions");
} else if (name == attr::weak) {
// @weak is applied elsewhere
} else {
sle->warning(
"Ignoring unrecognized special attribute 'ldc.attributes.%s'",
@ -195,6 +204,8 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, llvm::Function *func) {
applyAttrSection(sle, func);
} else if (name == attr::target) {
applyAttrTarget(sle, func);
} else if (name == attr::weak) {
// @weak is applied elsewhere
} else {
sle->warning(
"ignoring unrecognized special attribute 'ldc.attributes.%s'",
@ -202,3 +213,33 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, llvm::Function *func) {
}
}
}
/// Checks whether 'sym' has the @ldc.attributes._weak() UDA applied.
bool hasWeakUDA(Dsymbol *sym) {
if (!sym->userAttribDecl)
return false;
// Loop over all UDAs and early return true if @weak was found.
Expressions *attrs = sym->userAttribDecl->getAttributes();
expandTuples(attrs);
for (auto &attr : *attrs) {
auto sle = getLdcAttributesStruct(attr);
if (!sle)
continue;
auto name = sle->sd->ident->string;
if (name == attr::weak) {
// Check whether @weak can be applied to this symbol.
// Because hasWeakUDA is currently only called for global symbols, this check never errors.
auto vd = sym->isVarDeclaration();
if (!(vd && vd->isDataseg()) && !sym->isFuncDeclaration()) {
sym->error("@ldc.attributes.weak can only be applied to functions or global variables");
return false;
}
return true;
}
}
return false;
}

View file

@ -15,6 +15,7 @@
#ifndef GEN_UDA_H
#define GEN_UDA_H
class Dsymbol;
class FuncDeclaration;
class VarDeclaration;
namespace llvm {
@ -25,4 +26,6 @@ class GlobalVariable;
void applyFuncDeclUDAs(FuncDeclaration *decl, llvm::Function *func);
void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar);
bool hasWeakUDA(Dsymbol *sym);
#endif

@ -1 +1 @@
Subproject commit 8438312451713a6e80f53efde0adb17ff3d0a535
Subproject commit 822be5157906f13d0eb6763d10f80e54ec41a350

View file

@ -14,6 +14,17 @@ import ldc.attributes;
//---------------------------------------------------------------------
//---------------------------------------------------------------------
//---- @(weak) --------------------------------------------------------
// CHECK-DAG: @{{.*}}myWeakGlobali = weak
@(ldc.attributes.weak) int myWeakGlobal;
// CHECK-DAG: define{{.*}} weak {{.*}}void @{{.*}}weakFunc
@weak void weakFunc() {}
//---------------------------------------------------------------------
// CHECK-LABEL: define i32 @_Dmain
void main() {

@ -1 +1 @@
Subproject commit d006abf3c861c6f07069e1004ea545d7a464961a
Subproject commit 7fbd3606b5a5735738f6cc449b5e05c090180ad0

21
tests/ir/attr_weak.d Normal file
View file

@ -0,0 +1,21 @@
// Test linking+running a program with @weak functions
// RUN: %ldc -O3 %S/inputs/attr_weak_input.d -c -of=%t%obj
// RUN: %ldc -O3 %t%obj -run %s
import ldc.attributes;
extern(C) int return_two() {
return 2;
}
// Should be overridden by attr_weak_input.d
extern(C) @weak int return_seven() {
return 1;
}
void main() {
assert( return_two() == 2 );
assert( return_seven() == 7 );
}

View file

@ -0,0 +1,9 @@
import ldc.attributes;
extern(C) @weak int return_two() {
return 1;
}
extern(C) int return_seven() {
return 7;
}

View file

@ -20,10 +20,15 @@ config.test_format = lit.formats.ShTest(execute_external=False)
config.suffixes = ['.d',
]
# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
# excludes: A list of directories to exclude from the testsuite. The 'inputs'
# subdirectories contain auxiliary inputs for various tests in their parent
# directories.
config.excludes = ['d2', 'CMakeLists.txt', 'runlit.py']
config.excludes = [
'inputs',
'd2',
'CMakeLists.txt',
'runlit.py',
]
# Define available features so that we can disable tests depending on LLVM version
config.available_features.add("llvm%d" % config.llvm_version)
@ -52,3 +57,12 @@ config.environment['PATH'] = path
# Add substitutions
config.substitutions.append( ('%ldc', config.ldc2_bin) )
# Add platform-dependent file extension substitutions
if (platform.system() == 'Windows'):
config.substitutions.append( ('%obj', '.obj') )
config.substitutions.append( ('%exe', '.exe') )
else:
config.substitutions.append( ('%obj', '.o') )
config.substitutions.append( ('%exe', '') )