mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 09:00:33 +03:00
For the outermost function needing a context frame, use the address of that
frame as the nest argument instead of the address of a single-element list containing only that frame address. This saves some stack space and reduces memory accesses.
This commit is contained in:
parent
cc492ecc82
commit
5a04c4e9a1
3 changed files with 87 additions and 29 deletions
114
gen/nested.cpp
114
gen/nested.cpp
|
@ -28,6 +28,8 @@ enum NestedCtxType {
|
||||||
/// Context is a list of pointers to structs. Each function with variables
|
/// Context is a list of pointers to structs. Each function with variables
|
||||||
/// accessed by nested functions puts them in a struct, and appends a
|
/// accessed by nested functions puts them in a struct, and appends a
|
||||||
/// pointer to that struct to it's local copy of the list.
|
/// pointer to that struct to it's local copy of the list.
|
||||||
|
/// As an additional optimization, if the list has length one it's not
|
||||||
|
/// generated; the only element is used directly instead.
|
||||||
NCHybrid
|
NCHybrid
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,12 +104,17 @@ DValue* DtoNestedVariable(Loc loc, Type* astype, VarDeclaration* vd)
|
||||||
return new DVarValue(astype, vd, val);
|
return new DVarValue(astype, vd, val);
|
||||||
}
|
}
|
||||||
else if (nestedCtx == NCHybrid) {
|
else if (nestedCtx == NCHybrid) {
|
||||||
FuncDeclaration *parentfunc = getParentFunc(vd);
|
FuncDeclaration* parentfunc = getParentFunc(irfunc->decl);
|
||||||
assert(parentfunc && "No parent function for nested variable?");
|
assert(parentfunc && "No parent function for nested function?");
|
||||||
|
Logger::println("Parent function: %s", parentfunc->toChars());
|
||||||
|
|
||||||
LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType));
|
LLValue* val = DtoBitCast(ctx, LLPointerType::getUnqual(parentfunc->ir.irFunc->framesType));
|
||||||
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth);
|
Logger::cout() << "Context: " << *val << '\n';
|
||||||
val = DtoAlignedLoad(val, (std::string(".frame.") + parentfunc->toChars()).c_str());
|
|
||||||
|
if (!parentfunc->ir.irFunc->elidedCtxList) {
|
||||||
|
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedDepth);
|
||||||
|
val = DtoAlignedLoad(val, (std::string(".frame.") + vdparent->toChars()).c_str());
|
||||||
|
}
|
||||||
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
|
val = DtoGEPi(val, 0, vd->ir.irLocal->nestedIndex, vd->toChars());
|
||||||
if (vd->ir.irLocal->byref)
|
if (vd->ir.irLocal->byref)
|
||||||
val = DtoAlignedLoad(val);
|
val = DtoAlignedLoad(val);
|
||||||
|
@ -123,7 +130,8 @@ void DtoNestedInit(VarDeclaration* vd)
|
||||||
Logger::println("DtoNestedInit for %s", vd->toChars());
|
Logger::println("DtoNestedInit for %s", vd->toChars());
|
||||||
LOG_SCOPE
|
LOG_SCOPE
|
||||||
|
|
||||||
LLValue* nestedVar = gIR->func()->decl->ir.irFunc->nestedVar;
|
IrFunction* irfunc = gIR->func()->decl->ir.irFunc;
|
||||||
|
LLValue* nestedVar = irfunc->nestedVar;
|
||||||
|
|
||||||
if (nestedCtx == NCArray) {
|
if (nestedCtx == NCArray) {
|
||||||
// alloca as usual if no value already
|
// alloca as usual if no value already
|
||||||
|
@ -143,12 +151,16 @@ void DtoNestedInit(VarDeclaration* vd)
|
||||||
assert(vd->ir.irLocal->value && "Nested variable without storage?");
|
assert(vd->ir.irLocal->value && "Nested variable without storage?");
|
||||||
if (!vd->isParameter() && (vd->isRef() || vd->isOut())) {
|
if (!vd->isParameter() && (vd->isRef() || vd->isOut())) {
|
||||||
Logger::println("Initializing non-parameter byref value");
|
Logger::println("Initializing non-parameter byref value");
|
||||||
LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth);
|
LLValue* frame;
|
||||||
|
if (!irfunc->elidedCtxList) {
|
||||||
FuncDeclaration *parentfunc = getParentFunc(vd);
|
LLValue* framep = DtoGEPi(nestedVar, 0, vd->ir.irLocal->nestedDepth);
|
||||||
assert(parentfunc && "No parent function for nested variable?");
|
|
||||||
LLValue* frame = DtoAlignedLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str());
|
FuncDeclaration *parentfunc = getParentFunc(vd);
|
||||||
|
assert(parentfunc && "No parent function for nested variable?");
|
||||||
|
frame = DtoAlignedLoad(framep, (std::string(".frame.") + parentfunc->toChars()).c_str());
|
||||||
|
} else {
|
||||||
|
frame = nestedVar;
|
||||||
|
}
|
||||||
LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex);
|
LLValue* slot = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex);
|
||||||
DtoAlignedStore(vd->ir.irLocal->value, slot);
|
DtoAlignedStore(vd->ir.irLocal->value, slot);
|
||||||
} else {
|
} else {
|
||||||
|
@ -293,9 +305,15 @@ void DtoCreateNestedContext(FuncDeclaration* fd) {
|
||||||
if (FuncDeclaration* parfd = par->isFuncDeclaration()) {
|
if (FuncDeclaration* parfd = par->isFuncDeclaration()) {
|
||||||
// skip functions without nested parameters
|
// skip functions without nested parameters
|
||||||
if (!parfd->nestedVars.empty()) {
|
if (!parfd->nestedVars.empty()) {
|
||||||
// Copy the types of parent function frames.
|
|
||||||
const LLStructType* parft = parfd->ir.irFunc->framesType;
|
const LLStructType* parft = parfd->ir.irFunc->framesType;
|
||||||
frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end());
|
if (parfd->ir.irFunc->elidedCtxList) {
|
||||||
|
// This is the outermost function with a nested context.
|
||||||
|
// Its context is not a list of frames, but just the frame itself.
|
||||||
|
frametypes.push_back(LLPointerType::getUnqual(parft));
|
||||||
|
} else {
|
||||||
|
// Copy the types of parent function frames.
|
||||||
|
frametypes.insert(frametypes.begin(), parft->element_begin(), parft->element_end());
|
||||||
|
}
|
||||||
break; // That's all the info needed.
|
break; // That's all the info needed.
|
||||||
}
|
}
|
||||||
} else if (ClassDeclaration* parcd = par->isClassDeclaration()) {
|
} else if (ClassDeclaration* parcd = par->isClassDeclaration()) {
|
||||||
|
@ -308,6 +326,13 @@ void DtoCreateNestedContext(FuncDeclaration* fd) {
|
||||||
}
|
}
|
||||||
unsigned depth = frametypes.size();
|
unsigned depth = frametypes.size();
|
||||||
|
|
||||||
|
if (Logger::enabled()) {
|
||||||
|
Logger::println("Frame types: ");
|
||||||
|
LOG_SCOPE;
|
||||||
|
for (TypeVec::iterator i=frametypes.begin(); i!=frametypes.end(); ++i)
|
||||||
|
Logger::cout() << **i << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
// Construct a struct for the direct nested variables of this function, and update their indices to match.
|
// Construct a struct for the direct nested variables of this function, and update their indices to match.
|
||||||
// TODO: optimize ordering for minimal space usage?
|
// TODO: optimize ordering for minimal space usage?
|
||||||
TypeVec types;
|
TypeVec types;
|
||||||
|
@ -336,24 +361,43 @@ void DtoCreateNestedContext(FuncDeclaration* fd) {
|
||||||
Logger::cout() << "of type: " << *types.back() << '\n';
|
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
|
// Append current frame type to frame type list
|
||||||
const LLStructType* nestedVarsTy = LLStructType::get(frametypes);
|
const LLStructType* frameType = LLStructType::get(types);
|
||||||
|
const LLStructType* nestedVarsTy = NULL;
|
||||||
|
if (!frametypes.empty()) {
|
||||||
|
assert(depth > 0);
|
||||||
|
frametypes.push_back(LLPointerType::getUnqual(frameType));
|
||||||
|
|
||||||
|
// make struct type for nested frame list
|
||||||
|
nestedVarsTy = LLStructType::get(frametypes);
|
||||||
|
} else {
|
||||||
|
assert(depth == 0);
|
||||||
|
// For the outer function, just use the frame as the context
|
||||||
|
// instead of alloca'ing a single-element framelist and passing
|
||||||
|
// a pointer to that.
|
||||||
|
nestedVarsTy = frameType;
|
||||||
|
fd->ir.irFunc->elidedCtxList = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::cout() << "nestedVarsTy = " << *nestedVarsTy << '\n';
|
||||||
|
|
||||||
// Store type in IrFunction
|
// Store type in IrFunction
|
||||||
IrFunction* irfunction = fd->ir.irFunc;
|
IrFunction* irfunction = fd->ir.irFunc;
|
||||||
irfunction->framesType = nestedVarsTy;
|
irfunction->framesType = nestedVarsTy;
|
||||||
|
|
||||||
// alloca it
|
LLValue* nestedVars = NULL;
|
||||||
|
|
||||||
|
// Create frame for current function and append to frames list
|
||||||
// FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca
|
// FIXME: For D2, this should be a gc_malloc (or similar) call, not alloca
|
||||||
LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".frame_list");
|
LLValue* frame = DtoAlloca(frameType, ".frame");
|
||||||
|
|
||||||
// copy parent frames into beginning
|
// copy parent frames into beginning
|
||||||
if (depth != 0)
|
if (depth != 0)
|
||||||
{
|
{
|
||||||
|
// alloca frame list first
|
||||||
|
nestedVars = DtoAlloca(nestedVarsTy, ".frame_list");
|
||||||
|
|
||||||
LLValue* src = irfunction->nestArg;
|
LLValue* src = irfunction->nestArg;
|
||||||
if (!src)
|
if (!src)
|
||||||
{
|
{
|
||||||
|
@ -366,17 +410,25 @@ void DtoCreateNestedContext(FuncDeclaration* fd) {
|
||||||
Logger::println("Indexing to 'this'");
|
Logger::println("Indexing to 'this'");
|
||||||
src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis"));
|
src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis"));
|
||||||
}
|
}
|
||||||
src = DtoBitCast(src, getVoidPtrType());
|
if (depth == 1) {
|
||||||
LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType());
|
// Just copy nestArg into framelist; the outer frame is not a list of pointers
|
||||||
DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE),
|
// but a direct pointer.
|
||||||
getABITypeAlign(getVoidPtrType()));
|
src = DtoBitCast(src, frametypes[0]);
|
||||||
|
LLValue* gep = DtoGEPi(nestedVars, 0, 0);
|
||||||
|
DtoAlignedStore(src, gep);
|
||||||
|
} else {
|
||||||
|
src = DtoBitCast(src, getVoidPtrType());
|
||||||
|
LLValue* dst = DtoBitCast(nestedVars, getVoidPtrType());
|
||||||
|
DtoMemCpy(dst, src, DtoConstSize_t(depth * PTRSIZE),
|
||||||
|
getABITypeAlign(getVoidPtrType()));
|
||||||
|
}
|
||||||
|
// store current frame in list
|
||||||
|
DtoAlignedStore(frame, DtoGEPi(nestedVars, 0, depth));
|
||||||
|
} else {
|
||||||
|
// Use frame as context directly
|
||||||
|
nestedVars = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// store context in IrFunction
|
||||||
irfunction->nestedVar = nestedVars;
|
irfunction->nestedVar = nestedVars;
|
||||||
|
|
||||||
|
@ -403,6 +455,10 @@ void DtoCreateNestedContext(FuncDeclaration* fd) {
|
||||||
vd->ir.irLocal->byref = false;
|
vd->ir.irLocal->byref = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (FuncDeclaration* parFunc = getParentFunc(fd)) {
|
||||||
|
// Propagate context arg properties if the context arg is passed on unmodified.
|
||||||
|
fd->ir.irFunc->framesType = parFunc->ir.irFunc->framesType;
|
||||||
|
fd->ir.irFunc->elidedCtxList = parFunc->ir.irFunc->elidedCtxList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -109,6 +109,7 @@ IrFunction::IrFunction(FuncDeclaration* fd)
|
||||||
retArg = NULL;
|
retArg = NULL;
|
||||||
thisArg = NULL;
|
thisArg = NULL;
|
||||||
nestArg = NULL;
|
nestArg = NULL;
|
||||||
|
elidedCtxList = false;
|
||||||
|
|
||||||
nestedVar = NULL;
|
nestedVar = NULL;
|
||||||
framesType = NULL;
|
framesType = NULL;
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct IrFunction : IrBase
|
||||||
|
|
||||||
llvm::Value* nestedVar; // nested var alloca
|
llvm::Value* nestedVar; // nested var alloca
|
||||||
const llvm::StructType* framesType; // type of nested context (not for -nested-ctx=array)
|
const llvm::StructType* framesType; // type of nested context (not for -nested-ctx=array)
|
||||||
|
bool elidedCtxList; // whether the nested context is a raw frame instead of a list of frames (-nested-ctx=hybrid only)
|
||||||
|
|
||||||
llvm::Value* _arguments;
|
llvm::Value* _arguments;
|
||||||
llvm::Value* _argptr;
|
llvm::Value* _argptr;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue