diff --git a/dmd/declaration.c b/dmd/declaration.c index d4327a5b..c342b5b0 100644 --- a/dmd/declaration.c +++ b/dmd/declaration.c @@ -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()); } } diff --git a/dmd/declaration.h b/dmd/declaration.h index 78c0238c..d6823cce 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -15,6 +15,8 @@ #pragma once #endif /* __DMC__ */ +#include + #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 llvmNestedVars; + llvm::Value* llvmNested; }; struct FuncAliasDeclaration : FuncDeclaration diff --git a/dmd/func.c b/dmd/func.c index d0f4b241..7ad39ff9 100644 --- a/dmd/func.c +++ b/dmd/func.c @@ -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) diff --git a/gen/toir.c b/gen/toir.c index c94d84f0..467cac77 100644 --- a/gen/toir.c +++ b/gen/toir.c @@ -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 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(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; } diff --git a/gen/tollvm.c b/gen/tollvm.c index e6407dae..50dfd07b 100644 --- a/gen/tollvm.c +++ b/gen/tollvm.c @@ -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& 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 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; +} diff --git a/gen/tollvm.h b/gen/tollvm.h index 67f6d932..a394be0d 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -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& 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& 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" diff --git a/gen/toobj.c b/gen/toobj.c index f7d939b4..184d27d7 100644 --- a/gen/toobj.c +++ b/gen/toobj.c @@ -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 nestTypes; + int j = 0; + if (parentNested) { + nestTypes.push_back(parentNested->getType()); + j++; + } + for (std::set::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()); } - } } diff --git a/test/bug20.d b/test/bug20.d new file mode 100644 index 00000000..fef83ec2 --- /dev/null +++ b/test/bug20.d @@ -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); +} diff --git a/test/bug21.d b/test/bug21.d new file mode 100644 index 00000000..4f26916d --- /dev/null +++ b/test/bug21.d @@ -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); +} diff --git a/test/bug22.d b/test/bug22.d new file mode 100644 index 00000000..def342ba --- /dev/null +++ b/test/bug22.d @@ -0,0 +1,11 @@ +module bug22; + +void main() +{ + int i; + delegate { + i = 42; + }(); + printf("%d\n", i); + assert(i == 42); +} diff --git a/test/bug23.d b/test/bug23.d new file mode 100644 index 00000000..5989cc8b --- /dev/null +++ b/test/bug23.d @@ -0,0 +1,13 @@ +module bug23; +void main() +{ + int i; + delegate { + i++; + delegate { + i++; + }(); + }(); + printf("%d\n", i); + assert(i == 2); +} diff --git a/test/bug24.d b/test/bug24.d new file mode 100644 index 00000000..707ffd28 --- /dev/null +++ b/test/bug24.d @@ -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); +} diff --git a/test/bug25.d b/test/bug25.d new file mode 100644 index 00000000..d9667235 --- /dev/null +++ b/test/bug25.d @@ -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); +} diff --git a/tester.d b/tester.d new file mode 100644 index 00000000..113abb34 --- /dev/null +++ b/tester.d @@ -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; +} diff --git a/tester.sh b/tester.sh deleted file mode 100755 index 54e897d2..00000000 --- a/tester.sh +++ /dev/null @@ -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