mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 18:33:14 +01:00
Merge
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include "gen/functions.h"
|
||||
#include "gen/runtime.h"
|
||||
#include "gen/dvalue.h"
|
||||
#include "gen/nested.h"
|
||||
|
||||
#include "ir/irstruct.h"
|
||||
|
||||
@@ -1330,7 +1331,7 @@ LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl)
|
||||
// index vtbl
|
||||
funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, fdecl->toChars());
|
||||
// load funcptr
|
||||
funcval = DtoLoad(funcval);
|
||||
funcval = DtoAlignedLoad(funcval);
|
||||
|
||||
if (Logger::enabled())
|
||||
Logger::cout() << "funcval: " << *funcval << '\n';
|
||||
|
||||
46
gen/dvalue.h
46
gen/dvalue.h
@@ -38,8 +38,8 @@ struct DValue : Object
|
||||
|
||||
Type*& getType() { assert(type); return type; }
|
||||
|
||||
virtual LLValue* getLVal() { assert(0); return 0; }
|
||||
virtual LLValue* getRVal() { assert(0); return 0; }
|
||||
virtual llvm::Value* getLVal() { assert(0); return 0; }
|
||||
virtual llvm::Value* getRVal() { assert(0); return 0; }
|
||||
|
||||
virtual bool isLVal() { return false; }
|
||||
|
||||
@@ -60,11 +60,11 @@ protected:
|
||||
// immediate d-value
|
||||
struct DImValue : DValue
|
||||
{
|
||||
LLValue* val;
|
||||
llvm::Value* val;
|
||||
|
||||
DImValue(Type* t, LLValue* v) : DValue(t), val(v) { }
|
||||
DImValue(Type* t, llvm::Value* v) : DValue(t), val(v) { }
|
||||
|
||||
virtual LLValue* getRVal() { assert(val); return val; }
|
||||
virtual llvm::Value* getRVal() { assert(val); return val; }
|
||||
|
||||
virtual DImValue* isIm() { return this; }
|
||||
};
|
||||
@@ -72,11 +72,11 @@ struct DImValue : DValue
|
||||
// constant d-value
|
||||
struct DConstValue : DValue
|
||||
{
|
||||
LLConstant* c;
|
||||
llvm::Constant* c;
|
||||
|
||||
DConstValue(Type* t, LLConstant* con) : DValue(t), c(con) {}
|
||||
DConstValue(Type* t, llvm::Constant* con) : DValue(t), c(con) {}
|
||||
|
||||
virtual LLValue* getRVal();
|
||||
virtual llvm::Value* getRVal();
|
||||
|
||||
virtual DConstValue* isConst() { return this; }
|
||||
};
|
||||
@@ -84,7 +84,7 @@ struct DConstValue : DValue
|
||||
// null d-value
|
||||
struct DNullValue : DConstValue
|
||||
{
|
||||
DNullValue(Type* t, LLConstant* con) : DConstValue(t,con) {}
|
||||
DNullValue(Type* t, llvm::Constant* con) : DConstValue(t,con) {}
|
||||
virtual DNullValue* isNull() { return this; }
|
||||
};
|
||||
|
||||
@@ -92,14 +92,14 @@ struct DNullValue : DConstValue
|
||||
struct DVarValue : DValue
|
||||
{
|
||||
VarDeclaration* var;
|
||||
LLValue* val;
|
||||
llvm::Value* val;
|
||||
|
||||
DVarValue(Type* t, VarDeclaration* vd, LLValue* llvmValue);
|
||||
DVarValue(Type* t, LLValue* llvmValue);
|
||||
DVarValue(Type* t, VarDeclaration* vd, llvm::Value* llvmValue);
|
||||
DVarValue(Type* t, llvm::Value* llvmValue);
|
||||
|
||||
virtual bool isLVal() { return true; }
|
||||
virtual LLValue* getLVal();
|
||||
virtual LLValue* getRVal();
|
||||
virtual llvm::Value* getLVal();
|
||||
virtual llvm::Value* getRVal();
|
||||
|
||||
virtual DVarValue* isVar() { return this; }
|
||||
};
|
||||
@@ -107,19 +107,19 @@ struct DVarValue : DValue
|
||||
// field d-value
|
||||
struct DFieldValue : DVarValue
|
||||
{
|
||||
DFieldValue(Type* t, LLValue* llvmValue) : DVarValue(t, llvmValue) {}
|
||||
DFieldValue(Type* t, llvm::Value* llvmValue) : DVarValue(t, llvmValue) {}
|
||||
virtual DFieldValue* isField() { return this; }
|
||||
};
|
||||
|
||||
// slice d-value
|
||||
struct DSliceValue : DValue
|
||||
{
|
||||
LLValue* len;
|
||||
LLValue* ptr;
|
||||
llvm::Value* len;
|
||||
llvm::Value* ptr;
|
||||
|
||||
DSliceValue(Type* t, LLValue* l, LLValue* p) : DValue(t), len(l), ptr(p) {}
|
||||
DSliceValue(Type* t, llvm::Value* l, llvm::Value* p) : DValue(t), len(l), ptr(p) {}
|
||||
|
||||
virtual LLValue* getRVal();
|
||||
virtual llvm::Value* getRVal();
|
||||
|
||||
virtual DSliceValue* isSlice() { return this; }
|
||||
};
|
||||
@@ -128,12 +128,12 @@ struct DSliceValue : DValue
|
||||
struct DFuncValue : DValue
|
||||
{
|
||||
FuncDeclaration* func;
|
||||
LLValue* val;
|
||||
LLValue* vthis;
|
||||
llvm::Value* val;
|
||||
llvm::Value* vthis;
|
||||
|
||||
DFuncValue(FuncDeclaration* fd, LLValue* v, LLValue* vt = 0);
|
||||
DFuncValue(FuncDeclaration* fd, llvm::Value* v, llvm::Value* vt = 0);
|
||||
|
||||
virtual LLValue* getRVal();
|
||||
virtual llvm::Value* getRVal();
|
||||
|
||||
virtual DFuncValue* isFunc() { return this; }
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "gen/classes.h"
|
||||
#include "gen/dvalue.h"
|
||||
#include "gen/abi.h"
|
||||
#include "gen/nested.h"
|
||||
|
||||
using namespace llvm::Attribute;
|
||||
|
||||
@@ -672,13 +673,6 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
||||
// debug info - after all allocas, but before any llvm.dbg.declare etc
|
||||
if (global.params.symdebug) DtoDwarfFuncStart(fd);
|
||||
|
||||
// need result variable?
|
||||
if (fd->vresult) {
|
||||
Logger::println("vresult value");
|
||||
fd->vresult->ir.irLocal = new IrLocal(fd->vresult);
|
||||
fd->vresult->ir.irLocal->value = DtoAlloca(DtoType(fd->vresult->type), "function_vresult");
|
||||
}
|
||||
|
||||
// this hack makes sure the frame pointer elimination optimization is disabled.
|
||||
// this this eliminates a bunch of inline asm related issues.
|
||||
if (fd->inlineAsm)
|
||||
@@ -775,100 +769,20 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
||||
fd->nestedVars.insert(fd->vresult);
|
||||
}
|
||||
|
||||
// construct nested variables array
|
||||
if (!fd->nestedVars.empty())
|
||||
DtoCreateNestedContext(fd);
|
||||
|
||||
#if DMDV2
|
||||
if (fd->vresult && fd->vresult->nestedrefs.dim)
|
||||
#else
|
||||
if (fd->vresult && fd->vresult->nestedref)
|
||||
#endif
|
||||
{
|
||||
Logger::println("has nested frame");
|
||||
// start with adding all enclosing parent frames until a static parent is reached
|
||||
int nparelems = 0;
|
||||
if (!fd->isStatic())
|
||||
{
|
||||
Dsymbol* par = fd->toParent2();
|
||||
while (par)
|
||||
{
|
||||
if (FuncDeclaration* parfd = par->isFuncDeclaration())
|
||||
{
|
||||
nparelems += parfd->nestedVars.size();
|
||||
// stop at first static
|
||||
if (parfd->isStatic())
|
||||
break;
|
||||
}
|
||||
else if (ClassDeclaration* parcd = par->isClassDeclaration())
|
||||
{
|
||||
// nothing needed
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
par = par->toParent2();
|
||||
}
|
||||
}
|
||||
int nelems = fd->nestedVars.size() + nparelems;
|
||||
|
||||
// make array type for nested vars
|
||||
const LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems);
|
||||
|
||||
// alloca it
|
||||
LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".nested_vars");
|
||||
|
||||
// copy parent frame into beginning
|
||||
if (nparelems)
|
||||
{
|
||||
LLValue* src = irfunction->nestArg;
|
||||
if (!src)
|
||||
{
|
||||
assert(irfunction->thisArg);
|
||||
assert(fd->isMember2());
|
||||
LLValue* thisval = DtoLoad(irfunction->thisArg);
|
||||
ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
|
||||
assert(cd);
|
||||
assert(cd->vthis);
|
||||
src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis"));
|
||||
}
|
||||
DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE));
|
||||
}
|
||||
|
||||
// store in IrFunction
|
||||
irfunction->nestedVar = nestedVars;
|
||||
|
||||
// go through all nested vars and assign indices
|
||||
int idx = nparelems;
|
||||
for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
|
||||
{
|
||||
VarDeclaration* vd = *i;
|
||||
if (!vd->ir.irLocal)
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
|
||||
if (vd->isParameter())
|
||||
{
|
||||
Logger::println("nested param: %s", vd->toChars());
|
||||
LLValue* gep = DtoGEPi(nestedVars, 0, idx);
|
||||
LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
|
||||
DtoStore(val, gep);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::println("nested var: %s", vd->toChars());
|
||||
}
|
||||
|
||||
vd->ir.irLocal->nestedIndex = idx++;
|
||||
}
|
||||
|
||||
// fixup nested result variable
|
||||
#if DMDV2
|
||||
if (fd->vresult && fd->vresult->nestedrefs.dim) {
|
||||
#else
|
||||
if (fd->vresult && fd->vresult->nestedref) {
|
||||
#endif
|
||||
Logger::println("nested vresult value: %s", fd->vresult->toChars());
|
||||
LLValue* gep = DtoGEPi(nestedVars, 0, fd->vresult->ir.irLocal->nestedIndex);
|
||||
LLValue* val = DtoBitCast(fd->vresult->ir.irLocal->value, getVoidPtrType());
|
||||
DtoStore(val, gep);
|
||||
}
|
||||
DtoNestedInit(fd->vresult);
|
||||
} else if (fd->vresult) {
|
||||
fd->vresult->ir.irLocal = new IrLocal(fd->vresult);
|
||||
fd->vresult->ir.irLocal->value = DtoAlloca(DtoType(fd->vresult->type), fd->vresult->toChars());
|
||||
}
|
||||
|
||||
|
||||
// copy _argptr and _arguments to a memory location
|
||||
if (f->linkage == LINKd && f->varargs == 1)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "gen/llvmhelpers.h"
|
||||
#include "gen/llvm.h"
|
||||
#include "llvm/Target/TargetMachineRegistry.h"
|
||||
|
||||
@@ -9,7 +10,6 @@
|
||||
#include "module.h"
|
||||
|
||||
#include "gen/tollvm.h"
|
||||
#include "gen/llvmhelpers.h"
|
||||
#include "gen/irstate.h"
|
||||
#include "gen/runtime.h"
|
||||
#include "gen/logger.h"
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "gen/typeinf.h"
|
||||
#include "gen/todebug.h"
|
||||
#include "gen/cl_options.h"
|
||||
#include "gen/nested.h"
|
||||
#include "ir/irmodule.h"
|
||||
|
||||
#include <stack>
|
||||
@@ -311,75 +312,6 @@ void DtoLeaveMonitor(LLValue* v)
|
||||
gIR->CreateCallOrInvoke(fn, v);
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
/*////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NESTED VARIABLE HELPERS
|
||||
////////////////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd)
|
||||
{
|
||||
Dsymbol* vdparent = vd->toParent2();
|
||||
assert(vdparent);
|
||||
|
||||
IrFunction* irfunc = gIR->func();
|
||||
|
||||
// is the nested variable in this scope?
|
||||
if (vdparent == irfunc->decl)
|
||||
{
|
||||
LLValue* val = vd->ir.getIrValue();
|
||||
return new DVarValue(astype, vd, val);
|
||||
}
|
||||
|
||||
// get it from the nested context
|
||||
LLValue* ctx = 0;
|
||||
if (irfunc->decl->isMember2())
|
||||
{
|
||||
ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration();
|
||||
LLValue* val = DtoLoad(irfunc->thisArg);
|
||||
ctx = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis"));
|
||||
}
|
||||
else
|
||||
ctx = irfunc->nestArg;
|
||||
assert(ctx);
|
||||
|
||||
assert(vd->ir.irLocal);
|
||||
LLValue* val = DtoBitCast(ctx, getPtrToType(getVoidPtrType()));
|
||||
val = DtoGEPi1(val, vd->ir.irLocal->nestedIndex);
|
||||
val = DtoLoad(val);
|
||||
assert(vd->ir.irLocal->value);
|
||||
val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars());
|
||||
return new DVarValue(astype, vd, val);
|
||||
}
|
||||
|
||||
LLValue* DtoNestedContext(Loc loc, Dsymbol* sym)
|
||||
{
|
||||
Logger::println("DtoNestedContext for %s", sym->toPrettyChars());
|
||||
LOG_SCOPE;
|
||||
|
||||
IrFunction* irfunc = gIR->func();
|
||||
|
||||
// if this func has its own vars that are accessed by nested funcs
|
||||
// use its own context
|
||||
if (irfunc->nestedVar)
|
||||
return irfunc->nestedVar;
|
||||
// otherwise, it may have gotten a context from the caller
|
||||
else if (irfunc->nestArg)
|
||||
return irfunc->nestArg;
|
||||
// or just have a this argument
|
||||
else if (irfunc->thisArg)
|
||||
{
|
||||
ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration();
|
||||
if (!cd || !cd->vthis)
|
||||
return getNullPtr(getVoidPtrType());
|
||||
LLValue* val = DtoLoad(irfunc->thisArg);
|
||||
return DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return getNullPtr(getVoidPtrType());
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
/*////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ASSIGNMENT HELPER (store this in that)
|
||||
@@ -946,22 +878,7 @@ DValue* DtoDeclarationExp(Dsymbol* declaration)
|
||||
Logger::println("has nestedref set");
|
||||
assert(vd->ir.irLocal);
|
||||
|
||||
// alloca as usual if no value already
|
||||
if (!vd->ir.irLocal->value)
|
||||
{
|
||||
vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars());
|
||||
}
|
||||
|
||||
// store the address into the nested vars array
|
||||
|
||||
assert(vd->ir.irLocal->nestedIndex >= 0);
|
||||
LLValue* gep = DtoGEPi(gIR->func()->decl->ir.irFunc->nestedVar, 0, vd->ir.irLocal->nestedIndex);
|
||||
|
||||
assert(isaPointer(vd->ir.irLocal->value));
|
||||
LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
|
||||
|
||||
DtoStore(val, gep);
|
||||
|
||||
DtoNestedInit(vd);
|
||||
}
|
||||
// normal stack variable, allocate storage on the stack if it has not already been done
|
||||
else if(!vd->ir.irLocal) {
|
||||
@@ -1110,12 +1027,7 @@ LLValue* DtoRawVarDeclaration(VarDeclaration* var, LLValue* addr)
|
||||
else
|
||||
assert(!addr || addr == var->ir.irLocal->value);
|
||||
|
||||
// store the address into the nested vars array
|
||||
assert(var->ir.irLocal->nestedIndex >= 0);
|
||||
LLValue* gep = DtoGEPi(gIR->func()->decl->ir.irFunc->nestedVar, 0, var->ir.irLocal->nestedIndex);
|
||||
assert(isaPointer(var->ir.irLocal->value));
|
||||
LLValue* val = DtoBitCast(var->ir.irLocal->value, getVoidPtrType());
|
||||
DtoStore(val, gep);
|
||||
DtoNestedInit(var);
|
||||
}
|
||||
// normal local variable
|
||||
else
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
#define LDC_GEN_LLVMHELPERS_H
|
||||
|
||||
#include "gen/llvm.h"
|
||||
#include "gen/dvalue.h"
|
||||
|
||||
#include "statement.h"
|
||||
#include "mtype.h"
|
||||
|
||||
// this is used for tracking try-finally, synchronized and volatile scopes
|
||||
struct EnclosingHandler
|
||||
@@ -64,15 +67,6 @@ void DtoEnterMonitor(LLValue* v);
|
||||
/// Leaves a monitor lock.
|
||||
void DtoLeaveMonitor(LLValue* v);
|
||||
|
||||
// nested variable and context helpers
|
||||
|
||||
/// Gets the context value for a call to a nested function or newing a nested
|
||||
/// class with arbitrary nesting.
|
||||
LLValue* DtoNestedContext(Loc loc, Dsymbol* sym);
|
||||
|
||||
/// Gets the DValue of a nested variable with arbitrary nesting.
|
||||
DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd);
|
||||
|
||||
// basic operations
|
||||
void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs);
|
||||
|
||||
|
||||
411
gen/nested.cpp
Normal file
411
gen/nested.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
#include "gen/nested.h"
|
||||
|
||||
#include "gen/dvalue.h"
|
||||
#include "gen/irstate.h"
|
||||
#include "gen/llvmhelpers.h"
|
||||
#include "gen/logger.h"
|
||||
#include "gen/tollvm.h"
|
||||
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
namespace cl = llvm::cl;
|
||||
|
||||
/// What the context pointer for a nested function looks like
|
||||
enum NestedCtxType {
|
||||
/// Context is void*[] of pointers to variables.
|
||||
/// Variables from higher levels are at the front.
|
||||
NCArray,
|
||||
|
||||
/// Context is a struct containing variables belonging to the parent function.
|
||||
/// If the parent function itself has a parent function, one of the members is
|
||||
/// a pointer to its context. (linked-list style)
|
||||
// FIXME: implement
|
||||
// TODO: Functions without any variables accessed by nested functions, but
|
||||
// with a parent whose variables are accessed, can use the parent's
|
||||
// context.
|
||||
// NOTE: This is what DMD seems to do.
|
||||
NCStruct,
|
||||
|
||||
/// Context is a list of pointers to structs. Each function with variables
|
||||
/// accessed by nested functions puts them in a struct, and appends a
|
||||
/// pointer to that struct to it's local copy of the list.
|
||||
NCHybrid
|
||||
};
|
||||
|
||||
static cl::opt<NestedCtxType> nestedCtx("nested-ctx",
|
||||
cl::desc("How to construct a nested function's context:"),
|
||||
cl::ZeroOrMore,
|
||||
cl::values(
|
||||
clEnumValN(NCArray, "array", "Array of pointers to variables (including multi-level)"),
|
||||
//clEnumValN(NCStruct, "struct", "Struct of variables (with multi-level via linked list)"),
|
||||
clEnumValN(NCHybrid, "hybrid", "List of pointers to structs of variables, one per level."),
|
||||
clEnumValEnd),
|
||||
cl::init(NCHybrid));
|
||||
|
||||
|
||||
/****************************************************************************************/
|
||||
/*////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NESTED VARIABLE HELPERS
|
||||
////////////////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
static FuncDeclaration* getParentFunc(Dsymbol* sym) {
|
||||
Dsymbol* parent = sym->parent;
|
||||
assert(parent);
|
||||
while (parent && !parent->isFuncDeclaration())
|
||||
parent = parent->parent;
|
||||
|
||||
return (parent ? parent->isFuncDeclaration() : NULL);
|
||||
}
|
||||
|
||||
DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd)
|
||||
{
|
||||
Logger::println("DtoNestedVariable for %s @ %s", vd->toChars(), loc.toChars());
|
||||
LOG_SCOPE;
|
||||
|
||||
////////////////////////////////////
|
||||
// Locate context value
|
||||
|
||||
Dsymbol* vdparent = vd->toParent2();
|
||||
assert(vdparent);
|
||||
|
||||
IrFunction* irfunc = gIR->func();
|
||||
|
||||
// is the nested variable in this scope?
|
||||
if (vdparent == irfunc->decl)
|
||||
{
|
||||
LLValue* val = vd->ir.getIrValue();
|
||||
return new DVarValue(astype, vd, val);
|
||||
}
|
||||
|
||||
// get the nested context
|
||||
LLValue* ctx = 0;
|
||||
if (irfunc->decl->isMember2())
|
||||
{
|
||||
ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration();
|
||||
LLValue* val = DtoLoad(irfunc->thisArg);
|
||||
ctx = DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis"));
|
||||
}
|
||||
else
|
||||
ctx = irfunc->nestArg;
|
||||
assert(ctx);
|
||||
|
||||
assert(vd->ir.irLocal);
|
||||
|
||||
////////////////////////////////////
|
||||
// Extract variable from nested context
|
||||
|
||||
if (nestedCtx == NCArray) {
|
||||
LLValue* val = DtoBitCast(ctx, getPtrToType(getVoidPtrType()));
|
||||
val = DtoGEPi1(val, vd->ir.irLocal->nestedIndex);
|
||||
val = DtoAlignedLoad(val);
|
||||
assert(vd->ir.irLocal->value);
|
||||
val = DtoBitCast(val, vd->ir.irLocal->value->getType(), vd->toChars());
|
||||
return new DVarValue(astype, vd, val);
|
||||
}
|
||||
else if (nestedCtx == NCHybrid) {
|
||||
FuncDeclaration *parentfunc = getParentFunc(vd);
|
||||
assert(parentfunc && "No parent function for nested variable?");
|
||||
|
||||
LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType));
|
||||
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth);
|
||||
val = DtoAlignedLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str());
|
||||
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
|
||||
if (vd->ir.irLocal->byref)
|
||||
val = DtoAlignedLoad(val);
|
||||
return new DVarValue(astype, vd, val);
|
||||
}
|
||||
else {
|
||||
assert(0 && "Not implemented yet");
|
||||
}
|
||||
}
|
||||
|
||||
void DtoNestedInit(VarDeclaration* vd)
|
||||
{
|
||||
Logger::println("DtoNestedInit for %s", vd->toChars());
|
||||
LOG_SCOPE
|
||||
|
||||
LLValue* nestedVar = gIR->func()->decl->ir.irFunc->nestedVar;
|
||||
|
||||
if (nestedCtx == NCArray) {
|
||||
// alloca as usual if no value already
|
||||
if (!vd->ir.irLocal->value)
|
||||
vd->ir.irLocal->value = DtoAlloca(DtoType(vd->type), vd->toChars());
|
||||
|
||||
// store the address into the nested vars array
|
||||
assert(vd->ir.irLocal->nestedIndex >= 0);
|
||||
LLValue* gep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedIndex);
|
||||
|
||||
assert(isaPointer(vd->ir.irLocal->value));
|
||||
LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
|
||||
|
||||
DtoAlignedStore(val, gep);
|
||||
}
|
||||
else if (nestedCtx == NCHybrid) {
|
||||
assert(vd->ir.irLocal->value && "Nested variable without storage?");
|
||||
if (!vd->isParameter() && (vd->isRef() || vd->isOut())) {
|
||||
Logger::println("Initializing non-parameter byref value");
|
||||
LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth);
|
||||
|
||||
FuncDeclaration *parentfunc = getParentFunc(vd);
|
||||
assert(parentfunc && "No parent function for nested variable?");
|
||||
LLValue* frame = DtoAlignedLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str());
|
||||
|
||||
LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex);
|
||||
DtoAlignedStore(vd->ir.irLocal->value, slot);
|
||||
} else {
|
||||
// Already initialized in DtoCreateNestedContext
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(0 && "Not implemented yet");
|
||||
}
|
||||
}
|
||||
|
||||
LLValue* DtoNestedContext(Loc loc, Dsymbol* sym)
|
||||
{
|
||||
Logger::println("DtoNestedContext for %s", sym->toPrettyChars());
|
||||
LOG_SCOPE;
|
||||
|
||||
IrFunction* irfunc = gIR->func();
|
||||
|
||||
// if this func has its own vars that are accessed by nested funcs
|
||||
// use its own context
|
||||
if (irfunc->nestedVar)
|
||||
return irfunc->nestedVar;
|
||||
// otherwise, it may have gotten a context from the caller
|
||||
else if (irfunc->nestArg)
|
||||
return irfunc->nestArg;
|
||||
// or just have a this argument
|
||||
else if (irfunc->thisArg)
|
||||
{
|
||||
ClassDeclaration* cd = irfunc->decl->isMember2()->isClassDeclaration();
|
||||
if (!cd || !cd->vthis)
|
||||
return getNullPtr(getVoidPtrType());
|
||||
LLValue* val = DtoLoad(irfunc->thisArg);
|
||||
return DtoLoad(DtoGEPi(val, 0,cd->vthis->ir.irField->index, ".vthis"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return getNullPtr(getVoidPtrType());
|
||||
}
|
||||
}
|
||||
|
||||
void DtoCreateNestedContext(FuncDeclaration* fd) {
|
||||
Logger::println("DtoCreateNestedContext for %s", fd->toChars());
|
||||
LOG_SCOPE
|
||||
|
||||
if (nestedCtx == NCArray) {
|
||||
// construct nested variables array
|
||||
if (!fd->nestedVars.empty())
|
||||
{
|
||||
Logger::println("has nested frame");
|
||||
// start with adding all enclosing parent frames until a static parent is reached
|
||||
int nparelems = 0;
|
||||
if (!fd->isStatic())
|
||||
{
|
||||
Dsymbol* par = fd->toParent2();
|
||||
while (par)
|
||||
{
|
||||
if (FuncDeclaration* parfd = par->isFuncDeclaration())
|
||||
{
|
||||
nparelems += parfd->nestedVars.size();
|
||||
// stop at first static
|
||||
if (parfd->isStatic())
|
||||
break;
|
||||
}
|
||||
else if (ClassDeclaration* parcd = par->isClassDeclaration())
|
||||
{
|
||||
// nothing needed
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
par = par->toParent2();
|
||||
}
|
||||
}
|
||||
int nelems = fd->nestedVars.size() + nparelems;
|
||||
|
||||
// make array type for nested vars
|
||||
const LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems);
|
||||
|
||||
// alloca it
|
||||
LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".nested_vars");
|
||||
|
||||
IrFunction* irfunction = fd->ir.irFunc;
|
||||
|
||||
// copy parent frame into beginning
|
||||
if (nparelems)
|
||||
{
|
||||
LLValue* src = irfunction->nestArg;
|
||||
if (!src)
|
||||
{
|
||||
assert(irfunction->thisArg);
|
||||
assert(fd->isMember2());
|
||||
LLValue* thisval = DtoLoad(irfunction->thisArg);
|
||||
ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
|
||||
assert(cd);
|
||||
assert(cd->vthis);
|
||||
src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis"));
|
||||
}
|
||||
DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE),
|
||||
getABITypeAlign(getVoidPtrType()));
|
||||
}
|
||||
|
||||
// store in IrFunction
|
||||
irfunction->nestedVar = nestedVars;
|
||||
|
||||
// go through all nested vars and assign indices
|
||||
int idx = nparelems;
|
||||
for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
|
||||
{
|
||||
VarDeclaration* vd = *i;
|
||||
if (!vd->ir.irLocal)
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
|
||||
if (vd->isParameter())
|
||||
{
|
||||
Logger::println("nested param: %s", vd->toChars());
|
||||
LLValue* gep = DtoGEPi(nestedVars, 0, idx);
|
||||
LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
|
||||
DtoAlignedStore(val, gep);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::println("nested var: %s", vd->toChars());
|
||||
}
|
||||
|
||||
vd->ir.irLocal->nestedIndex = idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (nestedCtx == NCHybrid) {
|
||||
// construct nested variables array
|
||||
if (!fd->nestedVars.empty())
|
||||
{
|
||||
Logger::println("has nested frame");
|
||||
// start with adding all enclosing parent frames until a static parent is reached
|
||||
typedef std::vector<const LLType*> TypeVec;
|
||||
TypeVec frametypes;
|
||||
if (!fd->isStatic()) {
|
||||
Dsymbol* par = fd->toParent2();
|
||||
while (par) {
|
||||
if (FuncDeclaration* parfd = par->isFuncDeclaration()) {
|
||||
// skip functions without nested parameters
|
||||
if (!parfd->nestedVars.empty()) {
|
||||
// Copy the types of parent function frames.
|
||||
const LLStructType* parft = parfd->ir.irFunc->framesType;
|
||||
frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end());
|
||||
break; // That's all the info needed.
|
||||
}
|
||||
} else if (ClassDeclaration* parcd = par->isClassDeclaration()) {
|
||||
// skip
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
par = par->toParent2();
|
||||
}
|
||||
}
|
||||
unsigned depth = frametypes.size();
|
||||
|
||||
// Construct a struct for the direct nested variables of this function, and update their indices to match.
|
||||
// TODO: optimize ordering for minimal space usage?
|
||||
TypeVec types;
|
||||
for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
|
||||
{
|
||||
VarDeclaration* vd = *i;
|
||||
if (!vd->ir.irLocal)
|
||||
vd->ir.irLocal = new IrLocal(vd);
|
||||
|
||||
vd->ir.irLocal->nestedDepth = depth;
|
||||
vd->ir.irLocal->nestedIndex = types.size();
|
||||
if (vd->isParameter()) {
|
||||
// Parameters already have storage associated with them (to handle byref etc.),
|
||||
// so handle specially for now by storing a pointer instead of a value.
|
||||
assert(vd->ir.irLocal->value);
|
||||
// FIXME: don't do this for normal parameters?
|
||||
types.push_back(vd->ir.irLocal->value->getType());
|
||||
} else if (vd->isRef() || vd->isOut()) {
|
||||
// Foreach variables can also be by reference, for instance.
|
||||
types.push_back(DtoType(vd->type->pointerTo()));
|
||||
} else {
|
||||
types.push_back(DtoType(vd->type));
|
||||
}
|
||||
if (Logger::enabled()) {
|
||||
Logger::println("Nested var: %s", vd->toChars());
|
||||
Logger::cout() << "of type: " << *types.back() << '\n';
|
||||
}
|
||||
}
|
||||
// Append current frame type to frame type list
|
||||
const LLType* frameType = LLStructType::get(types);
|
||||
frametypes.push_back(LLPointerType::getUnqual(frameType));
|
||||
|
||||
// make struct type for nested frame list
|
||||
const LLStructType* nestedVarsTy = LLStructType::get(frametypes);
|
||||
|
||||
// Store type in IrFunction
|
||||
IrFunction* irfunction = fd->ir.irFunc;
|
||||
irfunction->framesType = nestedVarsTy;
|
||||
|
||||
// alloca it
|
||||
// FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca
|
||||
LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".frame_list");
|
||||
|
||||
// copy parent frames into beginning
|
||||
if (depth != 0)
|
||||
{
|
||||
LLValue* src = irfunction->nestArg;
|
||||
if (!src)
|
||||
{
|
||||
assert(irfunction->thisArg);
|
||||
assert(fd->isMember2());
|
||||
LLValue* thisval = DtoLoad(irfunction->thisArg);
|
||||
ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
|
||||
assert(cd);
|
||||
assert(cd->vthis);
|
||||
Logger::println("Indexing to 'this'");
|
||||
src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis"));
|
||||
}
|
||||
src = DtoBitCast(src, getVoidPtrType());
|
||||
LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType());
|
||||
DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE),
|
||||
getABITypeAlign(getVoidPtrType()));
|
||||
}
|
||||
|
||||
// Create frame for current function and append to frames list
|
||||
LLValue* frame = DtoAlloca(frameType, ".frame");
|
||||
// store current frame in list
|
||||
DtoAlignedStore(frame, DtoGEPi(nestedVars, 0, depth));
|
||||
|
||||
// store context in IrFunction
|
||||
irfunction->nestedVar = nestedVars;
|
||||
|
||||
// go through all nested vars and assign addresses where possible.
|
||||
for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
|
||||
{
|
||||
VarDeclaration* vd = *i;
|
||||
|
||||
LLValue* gep = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
|
||||
if (vd->isParameter()) {
|
||||
Logger::println("nested param: %s", vd->toChars());
|
||||
DtoAlignedStore(vd->ir.irLocal->value, gep);
|
||||
vd->ir.irLocal->byref = true;
|
||||
} else if (vd->isRef() || vd->isOut()) {
|
||||
// This slot is initialized in DtoNestedInit, to handle things like byref foreach variables
|
||||
// which move around in memory.
|
||||
vd->ir.irLocal->byref = true;
|
||||
} else {
|
||||
Logger::println("nested var: %s", vd->toChars());
|
||||
if (vd->ir.irLocal->value)
|
||||
Logger::cout() << "Pre-existing value: " << *vd->ir.irLocal->value << '\n';
|
||||
assert(!vd->ir.irLocal->value);
|
||||
vd->ir.irLocal->value = gep;
|
||||
vd->ir.irLocal->byref = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(0 && "Not implemented yet");
|
||||
}
|
||||
}
|
||||
25
gen/nested.h
Normal file
25
gen/nested.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef LDC_GEN_NESTED_H
|
||||
#define LDC_GEN_NESTED_H
|
||||
|
||||
#include "declaration.h"
|
||||
#include "mtype.h"
|
||||
#include "gen/dvalue.h"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Nested variable and context helpers
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/// Creates the context value for a nested function.
|
||||
void DtoCreateNestedContext(FuncDeclaration* fd);
|
||||
|
||||
/// Allocate space for variable accessed from nested function.
|
||||
void DtoNestedInit(VarDeclaration* vd);
|
||||
|
||||
/// Gets the context value for a call to a nested function or newing a nested
|
||||
/// class with arbitrary nesting.
|
||||
llvm::Value* DtoNestedContext(Loc loc, Dsymbol* sym);
|
||||
|
||||
/// Gets the DValue of a nested variable with arbitrary nesting.
|
||||
DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd);
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "gen/dvalue.h"
|
||||
#include "gen/functions.h"
|
||||
#include "gen/abi.h"
|
||||
#include "gen/nested.h"
|
||||
|
||||
#include "gen/logger.h"
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "gen/aa.h"
|
||||
#include "gen/functions.h"
|
||||
#include "gen/todebug.h"
|
||||
#include "gen/nested.h"
|
||||
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
|
||||
|
||||
@@ -426,7 +426,7 @@ void DtoMemSetZero(LLValue* dst, LLValue* nbytes)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes)
|
||||
void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes, unsigned align)
|
||||
{
|
||||
dst = DtoBitCast(dst,getVoidPtrType());
|
||||
src = DtoBitCast(src,getVoidPtrType());
|
||||
@@ -435,7 +435,7 @@ void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes)
|
||||
llvm::Function* fn = llvm::Intrinsic::getDeclaration(gIR->module,
|
||||
llvm::Intrinsic::memcpy, &intTy, 1);
|
||||
|
||||
gIR->ir->CreateCall4(fn, dst, src, nbytes, DtoConstUint(0), "");
|
||||
gIR->ir->CreateCall4(fn, dst, src, nbytes, DtoConstUint(align), "");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -573,11 +573,20 @@ LLValue* DtoLoad(LLValue* src, const char* name)
|
||||
{
|
||||
// if (Logger::enabled())
|
||||
// Logger::cout() << "loading " << *src << '\n';
|
||||
LLValue* ld = gIR->ir->CreateLoad(src, name ? name : "tmp");
|
||||
llvm::LoadInst* ld = gIR->ir->CreateLoad(src, name ? name : "tmp");
|
||||
//ld->setVolatile(gIR->func()->inVolatile);
|
||||
return ld;
|
||||
}
|
||||
|
||||
// Like DtoLoad, but the pointer is guaranteed to be aligned appropriately for the type.
|
||||
LLValue* DtoAlignedLoad(LLValue* src, const char* name)
|
||||
{
|
||||
llvm::LoadInst* ld = gIR->ir->CreateLoad(src, name ? name : "tmp");
|
||||
ld->setAlignment(getABITypeAlign(ld->getType()));
|
||||
return ld;
|
||||
}
|
||||
|
||||
|
||||
void DtoStore(LLValue* src, LLValue* dst)
|
||||
{
|
||||
// if (Logger::enabled())
|
||||
@@ -586,6 +595,13 @@ void DtoStore(LLValue* src, LLValue* dst)
|
||||
//st->setVolatile(gIR->func()->inVolatile);
|
||||
}
|
||||
|
||||
// Like DtoStore, but the pointer is guaranteed to be aligned appropriately for the type.
|
||||
void DtoAlignedStore(LLValue* src, LLValue* dst)
|
||||
{
|
||||
llvm::StoreInst* st = gIR->ir->CreateStore(src,dst);
|
||||
st->setAlignment(getABITypeAlign(src->getType()));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LLValue* DtoBitCast(LLValue* v, const LLType* t, const char* name)
|
||||
|
||||
@@ -63,7 +63,9 @@ LLConstant* DtoConstBool(bool);
|
||||
|
||||
// llvm wrappers
|
||||
LLValue* DtoLoad(LLValue* src, const char* name=0);
|
||||
LLValue* DtoAlignedLoad(LLValue* src, const char* name=0);
|
||||
void DtoStore(LLValue* src, LLValue* dst);
|
||||
void DtoAlignedStore(LLValue* src, LLValue* dst);
|
||||
LLValue* DtoBitCast(LLValue* v, const LLType* t, const char* name=0);
|
||||
LLConstant* DtoBitCast(LLConstant* v, const LLType* t);
|
||||
|
||||
@@ -117,8 +119,9 @@ void DtoMemSetZero(LLValue* dst, LLValue* nbytes);
|
||||
* @param dst Destination memory.
|
||||
* @param src Source memory.
|
||||
* @param nbytes Number of bytes to copy.
|
||||
* @param align The minimum alignment of the source and destination memory.
|
||||
*/
|
||||
void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes);
|
||||
void DtoMemCpy(LLValue* dst, LLValue* src, LLValue* nbytes, unsigned align = 0);
|
||||
|
||||
/**
|
||||
* Generates a call to C memcmp.
|
||||
|
||||
@@ -111,6 +111,7 @@ IrFunction::IrFunction(FuncDeclaration* fd)
|
||||
nestArg = NULL;
|
||||
|
||||
nestedVar = NULL;
|
||||
framesType = NULL;
|
||||
|
||||
_arguments = NULL;
|
||||
_argptr = NULL;
|
||||
|
||||
@@ -45,6 +45,7 @@ struct IrFunction : IrBase
|
||||
llvm::Value* nestArg; // nested function 'this' arg
|
||||
|
||||
llvm::Value* nestedVar; // nested var alloca
|
||||
const llvm::StructType* framesType; // type of nested context (not for -nested-ctx=array)
|
||||
|
||||
llvm::Value* _arguments;
|
||||
llvm::Value* _argptr;
|
||||
|
||||
@@ -26,6 +26,8 @@ struct IrLocal : IrVar
|
||||
{
|
||||
IrLocal(VarDeclaration* v);
|
||||
|
||||
bool byref; // Not used for -nested-ctx=array
|
||||
int nestedDepth; // ditto
|
||||
int nestedIndex;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user