[svn r54] Added support for nested delegates referencing parent's stack variables.

Replaced tester.sh with a version written in D.
A few bugfixes.
This commit is contained in:
Tomas Lindquist Olsen
2007-10-22 15:40:56 +02:00
parent f16a0c35b5
commit a9189bd3a9
15 changed files with 393 additions and 70 deletions

View File

@@ -31,7 +31,7 @@ Declaration::Declaration(Identifier *id)
type = NULL;
storage_class = STCundefined;
protection = PROTundefined;
linkage = LINKdefault;
linkage = LINKdefault;
llvmTouched = false;
}
@@ -549,6 +549,7 @@ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer
onstack = 0;
canassign = 0;
value = NULL;
llvmNestedIndex = -1;
}
Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s)
@@ -1049,6 +1050,7 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
fdthis->getLevel(loc, fdv);
nestedref = 1;
fdv->nestedFrameRef = 1;
fdv->llvmNestedVars.insert(this);
//printf("var %s in function %s is nested ref\n", toChars(), fdv->toChars());
}
}

View File

@@ -15,6 +15,8 @@
#pragma once
#endif /* __DMC__ */
#include <set>
#include "dsymbol.h"
#include "lexer.h"
#include "mtype.h"
@@ -125,8 +127,8 @@ struct Declaration : Dsymbol
Declaration *isDeclaration() { return this; }
virtual void toObjFile(); // compile to .obj file
virtual void toObjFile(); // compile to .obj file
bool llvmTouched;
};
@@ -255,6 +257,9 @@ struct VarDeclaration : Declaration
// Eliminate need for dynamic_cast
VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; }
// LLVMDC
int llvmNestedIndex;
};
/**************************************************************/
@@ -514,6 +519,8 @@ struct FuncDeclaration : Declaration
bool llvmQueued;
llvm::Value* llvmThisVar;
std::set<VarDeclaration*> llvmNestedVars;
llvm::Value* llvmNested;
};
struct FuncAliasDeclaration : FuncDeclaration

View File

@@ -75,6 +75,7 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, enum STC s
shidden = NULL;
llvmQueued = false;
llvmThisVar = NULL;
llvmNested = NULL;
}
Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)

View File

@@ -41,20 +41,29 @@ elem* DeclarationExp::toElem(IRState* p)
{
Logger::println("VarDeclaration");
// static
if (vd->isDataseg())
{
vd->toObjFile();
}
else
{
// allocate storage on the stack
Logger::println("vdtype = %s", vd->type->toChars());
const llvm::Type* lltype = LLVM_DtoType(vd->type);
llvm::AllocaInst* allocainst = new llvm::AllocaInst(lltype, vd->toChars(), p->topallocapoint());
//allocainst->setAlignment(vd->type->alignsize()); // TODO
vd->llvmValue = allocainst;
// e->val = really needed??
// referenced by nested delegate?
if (vd->nestedref) {
Logger::println("has nestedref set");
vd->llvmValue = p->func().decl->llvmNested;
assert(vd->llvmValue);
assert(vd->llvmNestedIndex >= 0);
}
// normal stack variable
else {
// allocate storage on the stack
const llvm::Type* lltype = LLVM_DtoType(vd->type);
llvm::AllocaInst* allocainst = new llvm::AllocaInst(lltype, vd->toChars(), p->topallocapoint());
//allocainst->setAlignment(vd->type->alignsize()); // TODO
vd->llvmValue = allocainst;
}
LLVM_DtoInitializer(vd->init);
}
}
@@ -97,7 +106,11 @@ elem* VarExp::toElem(IRState* p)
if (VarDeclaration* vd = var->isVarDeclaration())
{
Logger::println("VarDeclaration");
if (vd->nestedref) {
Logger::println("has nested ref");
}
// needed to take care of forward references of global variables
if (!vd->llvmTouched && vd->isDataseg())
vd->toObjFile();
@@ -111,6 +124,8 @@ elem* VarExp::toElem(IRState* p)
// or it could be a forward declaration of a global variable
if (!vd->llvmValue)
{
assert(!vd->nestedref);
Logger::println("special - no llvmValue");
// dollar
if (!p->arrays.empty())
{
@@ -137,6 +152,7 @@ elem* VarExp::toElem(IRState* p)
// function parameter
if (vd->storage_class & STCparameter) {
assert(!vd->nestedref);
Logger::println("function param");
if (vd->storage_class & (STCref | STCout)) {
e->mem = vd->llvmValue;
@@ -163,8 +179,31 @@ elem* VarExp::toElem(IRState* p)
}
}
else {
e->mem = vd->llvmValue;
//e->mem->setName(toChars());
// nested variable
if (vd->nestedref) {
/*
FuncDeclaration* fd = vd->toParent()->isFuncDeclaration();
assert(fd != NULL);
llvm::Value* ptr = NULL;
// inside nested function
if (fd != p->func().decl) {
ptr = p->func().decl->llvmThisVar;
Logger::cout() << "nested var reference:" << '\n' << *ptr << *vd->llvmValue->getType() << '\n';
ptr = p->ir->CreateBitCast(ptr, vd->llvmValue->getType(), "tmp");
}
// inside the actual parent function
else {
ptr = vd->llvmValue;
}
assert(ptr);
e->mem = LLVM_DtoGEPi(ptr,0,unsigned(vd->llvmNestedIndex),"tmp",p->scopebb());
*/
e->mem = LLVM_DtoNestedVariable(vd);
}
// normal local variable
else {
e->mem = vd->llvmValue;
}
e->vardecl = vd;
e->type = elem::VAR;
}
@@ -1283,13 +1322,16 @@ elem* SymOffExp::toElem(IRState* p)
Logger::println("VarDeclaration");
assert(vd->llvmValue);
Type* t = LLVM_DtoDType(type);
Type* vdtype = LLVM_DtoDType(vd->type);
Type* vdtype = LLVM_DtoDType(vd->type);
llvm::Value* llvalue = vd->nestedref ? LLVM_DtoNestedVariable(vd) : vd->llvmValue;
if (vdtype->ty == Tstruct && !(t->ty == Tpointer && t->next == vdtype)) {
TypeStruct* vdt = (TypeStruct*)vdtype;
e = new elem;
std::vector<unsigned> dst(1,0);
vdt->sym->offsetToIndex(t->next, offset, dst);
llvm::Value* ptr = vd->llvmValue;
llvm::Value* ptr = llvalue;
assert(ptr);
e->mem = LLVM_DtoGEP(ptr,dst,"tmp",p->scopebb());
e->type = elem::VAL;
@@ -1302,17 +1344,14 @@ elem* SymOffExp::toElem(IRState* p)
e = new elem;
llvm::Value* idx0 = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
//llvm::Value* idx1 = llvm::ConstantInt::get(llvm::Type::Int32Ty, 1, false);
e->mem = LLVM_DtoGEP(vd->llvmValue,idx0,idx0,"tmp",p->scopebb());
e->arg = vd->llvmValue;
e->mem = LLVM_DtoGEP(llvalue,idx0,idx0,"tmp",p->scopebb());
e->arg = llvalue;
e->type = elem::VAL;
}
else if (offset == 0) {
/*if (!vd->llvmValue)
vd->toObjFile();*/
assert(vd->llvmValue);
e = new elem;
e->mem = vd->llvmValue;
//e->vardecl = vd;
assert(llvalue);
e->mem = llvalue;
e->type = elem::VAL;
}
else {
@@ -1326,7 +1365,6 @@ elem* SymOffExp::toElem(IRState* p)
if (fd->llvmValue == 0)
fd->toObjFile();
e->val = fd->llvmValue;
//e->aspointer = true;
e->type = elem::FUNC;
}
assert(e != 0);
@@ -2595,13 +2633,29 @@ elem* FuncExp::toElem(IRState* p)
fd->toObjFile();
llvm::Value* lval = p->toplval();
llvm::Value* lval = NULL;
if (p->lvals.empty() || p->toplval() == NULL) {
const llvm::Type* dgty = LLVM_DtoType(type);
Logger::cout() << "delegate without explicit storage:" << '\n' << *dgty << '\n';
lval = new llvm::AllocaInst(dgty,"dgstorage",p->topallocapoint());
}
else {
lval = p->toplval();
}
elem* e = new elem;
llvm::Value* context = LLVM_DtoGEPi(lval,0,0,"tmp",p->scopebb());
//llvm::Value* castcontext = llvm::ConstantPointerNull::get(context->getType());
//new llvm::StoreInst(castcontext, context, p->scopebb());
const llvm::PointerType* pty = llvm::cast<llvm::PointerType>(context->getType()->getContainedType(0));
llvm::Value* llvmNested = p->func().decl->llvmNested;
if (llvmNested == NULL) {
llvm::Value* nullcontext = llvm::ConstantPointerNull::get(pty);
p->ir->CreateStore(nullcontext, context);
}
else {
llvm::Value* nestedcontext = p->ir->CreateBitCast(llvmNested, pty, "tmp");
p->ir->CreateStore(nestedcontext, context);
}
llvm::Value* fptr = LLVM_DtoGEPi(lval,0,1,"tmp",p->scopebb());
@@ -2610,6 +2664,8 @@ elem* FuncExp::toElem(IRState* p)
new llvm::StoreInst(castfptr, fptr, p->scopebb());
e->inplace = true;
e->mem = lval;
e->type = elem::VAR;
return e;
}

View File

@@ -875,7 +875,7 @@ llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, llvm::Value* i0, llvm::Value* i1, con
v[0] = i0;
v[1] = i1;
Logger::cout() << "DtoGEP: " << *ptr << '\n';
return new llvm::GetElementPtrInst(ptr, v.begin(), v.end(), var, bb);
return new llvm::GetElementPtrInst(ptr, v.begin(), v.end(), var, bb?bb:gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
@@ -892,14 +892,14 @@ llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, const std::vector<unsigned>& src, con
dst[i] = llvm::ConstantInt::get(llvm::Type::Int32Ty, src[i], false);
}
ostr << '\n';
return new llvm::GetElementPtrInst(ptr, dst.begin(), dst.end(), var, bb);
return new llvm::GetElementPtrInst(ptr, dst.begin(), dst.end(), var, bb?bb:gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i, const std::string& var, llvm::BasicBlock* bb)
{
return new llvm::GetElementPtrInst(ptr, llvm::ConstantInt::get(llvm::Type::Int32Ty, i, false), var, bb);
return new llvm::GetElementPtrInst(ptr, llvm::ConstantInt::get(llvm::Type::Int32Ty, i, false), var, bb?bb:gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
@@ -909,7 +909,7 @@ llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i0, unsigned i1, const std:
std::vector<llvm::Value*> v(2);
v[0] = llvm::ConstantInt::get(llvm::Type::Int32Ty, i0, false);
v[1] = llvm::ConstantInt::get(llvm::Type::Int32Ty, i1, false);
return new llvm::GetElementPtrInst(ptr, v.begin(), v.end(), var, bb);
return new llvm::GetElementPtrInst(ptr, v.begin(), v.end(), var, bb?bb:gIR->scopebb());
}
//////////////////////////////////////////////////////////////////////////////////////////
@@ -1166,4 +1166,46 @@ llvm::Value* LLVM_DtoArgument(const llvm::Type* paramtype, Argument* fnarg, Expr
return retval;
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::Value* LLVM_DtoNestedVariable(VarDeclaration* vd)
{
FuncDeclaration* fd = vd->toParent()->isFuncDeclaration();
assert(fd != NULL);
IRFunction* fcur = &gIR->func();
FuncDeclaration* f = fcur->decl;
// on this stack
if (fd == f) {
return LLVM_DtoGEPi(vd->llvmValue,0,unsigned(vd->llvmNestedIndex),"tmp");
}
// on a caller stack
llvm::Value* ptr = f->llvmThisVar;
assert(ptr);
f = f->toParent()->isFuncDeclaration();
assert(f);
assert(f->llvmNested);
const llvm::Type* nesttype = f->llvmNested->getType();
assert(nesttype);
ptr = gIR->ir->CreateBitCast(ptr, nesttype, "tmp");
Logger::cout() << "nested var reference:" << '\n' << *ptr << *nesttype << '\n';
while (f) {
if (fd == f) {
return LLVM_DtoGEPi(ptr,0,vd->llvmNestedIndex,"tmp");
}
else {
ptr = LLVM_DtoGEPi(ptr,0,0,"tmp");
ptr = gIR->ir->CreateLoad(ptr,"tmp");
}
f = f->toParent()->isFuncDeclaration();
}
assert(0 && "nested var not found");
return NULL;
}

View File

@@ -40,10 +40,10 @@ llvm::Function* LLVM_DeclareMemSet64();
llvm::Function* LLVM_DeclareMemCpy32();
llvm::Function* LLVM_DeclareMemCpy64();
llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, llvm::Value* i0, llvm::Value* i1, const std::string& var, llvm::BasicBlock* bb);
llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, const std::vector<unsigned>& src, const std::string& var, llvm::BasicBlock* bb);
llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i0, const std::string& var, llvm::BasicBlock* bb);
llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i0, unsigned i1, const std::string& var, llvm::BasicBlock* bb);
llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, llvm::Value* i0, llvm::Value* i1, const std::string& var, llvm::BasicBlock* bb=NULL);
llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, const std::vector<unsigned>& src, const std::string& var, llvm::BasicBlock* bb=NULL);
llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i0, const std::string& var, llvm::BasicBlock* bb=NULL);
llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i0, unsigned i1, const std::string& var, llvm::BasicBlock* bb=NULL);
void LLVM_DtoGiveArgumentStorage(elem* e);
@@ -54,4 +54,6 @@ void LLVM_DtoAssert(llvm::Value* cond, llvm::Value* loc, llvm::Value* msg);
llvm::Value* LLVM_DtoArgument(const llvm::Type* paramtype, Argument* fnarg, Expression* argexp);
llvm::Value* LLVM_DtoNestedVariable(VarDeclaration* vd);
#include "enums.h"

View File

@@ -699,6 +699,7 @@ void FuncDeclaration::toObjFile()
// this handling
if (f->llvmUsesThis) {
Logger::println("uses this");
if (f->llvmRetInPtr)
llvmThisVar = ++func->arg_begin();
else
@@ -719,6 +720,34 @@ void FuncDeclaration::toObjFile()
f->llvmAllocaPoint = new llvm::BitCastInst(llvm::ConstantInt::get(llvm::Type::Int32Ty,0,false),llvm::Type::Int32Ty,"alloca point",gIR->scopebb());
gIR->func().allocapoint = f->llvmAllocaPoint;
llvm::Value* parentNested = NULL;
if (FuncDeclaration* fd = toParent()->isFuncDeclaration()) {
parentNested = fd->llvmNested;
}
// construct nested variables struct
if (!llvmNestedVars.empty() || parentNested) {
std::vector<const llvm::Type*> nestTypes;
int j = 0;
if (parentNested) {
nestTypes.push_back(parentNested->getType());
j++;
}
for (std::set<VarDeclaration*>::iterator i=llvmNestedVars.begin(); i!=llvmNestedVars.end(); ++i) {
VarDeclaration* vd = *i;
vd->llvmNestedIndex = j++;
nestTypes.push_back(LLVM_DtoType(vd->type));
}
const llvm::StructType* nestSType = llvm::StructType::get(nestTypes);
Logger::cout() << "nested var struct has type:" << '\n' << *nestSType;
llvmNested = new llvm::AllocaInst(nestSType,"nestedvars",f->llvmAllocaPoint);
if (parentNested) {
assert(llvmThisVar);
llvm::Value* ptr = gIR->ir->CreateBitCast(llvmThisVar, parentNested->getType(), "tmp");
gIR->ir->CreateStore(ptr, LLVM_DtoGEPi(llvmNested, 0,0, "tmp"));
}
}
// output function body
fbody->toIR(gIR);
@@ -731,7 +760,6 @@ void FuncDeclaration::toObjFile()
if (func->getReturnType() == llvm::Type::VoidTy) {
new llvm::ReturnInst(gIR->scopebb());
}
}
}

18
test/bug20.d Normal file
View File

@@ -0,0 +1,18 @@
module bug20;
void func(void delegate() dg)
{
dg();
}
void main()
{
int i = 42;
void delegate() dg = {
i++;
};
printf("i = %d\n",i);
func(dg);
printf("i = %d\n",i);
assert(i == 43);
}

16
test/bug21.d Normal file
View File

@@ -0,0 +1,16 @@
module bug21;
void main()
{
int i = 42;
auto a = {
int j = i*2;
auto b = {
return j;
};
return b();
};
int i2 = a();
printf("%d\n", i2);
assert(i2 == i*2);
}

11
test/bug22.d Normal file
View File

@@ -0,0 +1,11 @@
module bug22;
void main()
{
int i;
delegate {
i = 42;
}();
printf("%d\n", i);
assert(i == 42);
}

13
test/bug23.d Normal file
View File

@@ -0,0 +1,13 @@
module bug23;
void main()
{
int i;
delegate {
i++;
delegate {
i++;
}();
}();
printf("%d\n", i);
assert(i == 2);
}

21
test/bug24.d Normal file
View File

@@ -0,0 +1,21 @@
module bug24;
struct S
{
long l;
float f;
}
void main()
{
S s = S(3L,2f);
delegate {
S t = S(4L, 1f);
delegate {
s.l += t.l;
s.f += t.f;
}();
}();
printf("%lu %f\n", s.l, s.f);
assert(s.l == 7 && s.f == 3);
}

12
test/bug25.d Normal file
View File

@@ -0,0 +1,12 @@
module bug25;
void main()
{
int i = 2;
delegate {
i = i*i;
i += i*i;
}();
printf("%d\n", i);
assert(i == 20);
}

129
tester.d Normal file
View File

@@ -0,0 +1,129 @@
module tester;
import std.file;
import std.path;
import std.process;
import std.stdio;
import std.string;
void printUsage(string cmd)
{
writefln("Usage:");
writefln(" ",cmd," %%name %%cmd %%...");
writefln("%%name:");
writefln(" name of test without path or extension. eg: bug1");
writefln("%%cmd:");
writefln(" c = compile module");
writefln(" gdb = same as 'c' but launches compiler in gdb");
writefln(" ll = compile module and print the disassemled bitcode");
writefln(" llo = compile and optimize module, then print the disassemled bitcode");
writefln("%%...");
writefln(" the rest of the command line options are passed directly to llvmdc");
}
string testFileName(string test, string ext="")
{
return "test/"~test~ext;
}
// couldnt get execvp to work
int execute(string cmd)
{
return system(cmd);
}
int execute(string cmd, string[] args)
{
char[] c = cmd.dup;
foreach(v; args) {
c ~= ' ';
c ~= v;
}
writefln(c);
return system(c);
}
void compileTest(string test, string[] args)
{
args = [testFileName(test,".d")] ~ args;
if (execute("llvmdc", args) != 0) {
throw new Exception("Failed to compile test: "~test);
}
}
void disassembleTest(string test, bool print)
{
string[] args = ["-f",testFileName(test,".bc")];
if (execute("llvm-dis", args) != 0) {
throw new Exception("Failed to disassemble test: "~test);
}
if (print) {
execute("cat "~testFileName(test,".ll"));
}
}
void debugTest(string test, string[] common)
{
string[] args = ["--args", "llvmdc", testFileName(test,".d")];
args ~= common;
if (execute("gdb", args) != 0) {
throw new Exception("Failed to compile test: '"~test~"' for debugging");
}
}
void optimizeTest(string test)
{
string bc = testFileName(test,".bc");
if (execute("opt -std-compile-opts -f -o="~bc~" "~bc)) {
throw new Exception("Failed to optimize test: "~test);
}
}
void runTest(string test)
{
if (execute(testFileName(test))) {
throw new Exception("Failed to run test: "~test);
}
}
int main(string[] args)
{
if (args.length < 3) {
printUsage(args[0]);
return 1;
}
string test = args[1];
string kind = args[2];
string[] compilelink = ["-Itest","-odtest"];
compilelink ~= args[3..$];
string[] compileonly = compilelink.dup;
compileonly ~= "-c";
compilelink ~= "-of"~testFileName(test);
switch(kind) {
case "c":
compileTest(test,compileonly);
break;
case "gdb":
debugTest(test,compileonly);
break;
case "ll":
compileTest(test,compileonly);
disassembleTest(test,true);
break;
case "llo":
compileTest(test,compileonly);
optimizeTest(test);
disassembleTest(test,true);
break;
case "run":
compileTest(test,compilelink);
runTest(test);
break;
default:
throw new Exception("Invalid command: "~kind);
}
return 0;
}

View File

@@ -1,35 +0,0 @@
#!/bin/bash
if [ -z $1 ]; then
echo "you need to specify the test name"
exit 1
fi
if [ "$2" = "ll" ]; then
llvmdc $1 -Itest -odtest -c -vv &&
llvm-dis -f $1.bc &&
cat $1.ll
exit $?
elif [ "$2" = "llopt" ]; then
llvmdc $1 -Itest -odtest -c -vv &&
opt -f -o=$1.bc -std-compile-opts $1.bc &&
llvm-dis -f $1.bc &&
cat $1.ll
exit $?
elif [ "$2" = "run" ]; then
llvmdc $1 lib/lphobos.bc -Itest -odtest -of$1 -vv &&
$1
exit $?
elif [ "$2" = "c" ]; then
llvmdc $1 -Itest -odtest -c -vv
exit $?
elif [ "$2" = "gdb" ]; then
gdb --args llvmdc $1 -Itest -odtest -c -vv
exit $?
elif [ "$2" = "gdbrun" ]; then
llvmdc $1 -Itest -odtest -c -vv &&
gdb $1
exit $?
else
echo "bad command or filename"
fi