changeset 50:6fcc08a4d406 trunk

[svn r54] Added support for nested delegates referencing parent's stack variables. Replaced tester.sh with a version written in D. A few bugfixes.
author lindquist
date Mon, 22 Oct 2007 15:40:56 +0200
parents e5c4bece7fa1
children 61bc1b4ad3c4
files dmd/declaration.c dmd/declaration.h dmd/func.c gen/toir.c gen/tollvm.c gen/tollvm.h gen/toobj.c test/bug20.d test/bug21.d test/bug22.d test/bug23.d test/bug24.d test/bug25.d tester.d tester.sh
diffstat 15 files changed, 393 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/dmd/declaration.c	Fri Oct 19 17:43:46 2007 +0200
+++ b/dmd/declaration.c	Mon Oct 22 15:40:56 2007 +0200
@@ -31,7 +31,7 @@
     type = NULL;
     storage_class = STCundefined;
     protection = PROTundefined;
-    linkage = LINKdefault;
+    linkage = LINKdefault;
     llvmTouched = false;
 }
 
@@ -549,6 +549,7 @@
     onstack = 0;
     canassign = 0;
     value = NULL;
+    llvmNestedIndex = -1;
 }
 
 Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s)
@@ -1049,6 +1050,7 @@
 		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());
 	}
     }
--- a/dmd/declaration.h	Fri Oct 19 17:43:46 2007 +0200
+++ b/dmd/declaration.h	Mon Oct 22 15:40:56 2007 +0200
@@ -15,6 +15,8 @@
 #pragma once
 #endif /* __DMC__ */
 
+#include <set>
+
 #include "dsymbol.h"
 #include "lexer.h"
 #include "mtype.h"
@@ -125,8 +127,8 @@
 
     Declaration *isDeclaration() { return this; }
 
-    virtual void toObjFile();           // compile to .obj file
-    
+    virtual void toObjFile();           // compile to .obj file
+    
     bool llvmTouched;
 };
 
@@ -255,6 +257,9 @@
 
     // Eliminate need for dynamic_cast
     VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; }
+
+    // LLVMDC
+    int llvmNestedIndex;
 };
 
 /**************************************************************/
@@ -514,6 +519,8 @@
 
     bool llvmQueued;
     llvm::Value* llvmThisVar;
+    std::set<VarDeclaration*> llvmNestedVars;
+    llvm::Value* llvmNested;
 };
 
 struct FuncAliasDeclaration : FuncDeclaration
--- a/dmd/func.c	Fri Oct 19 17:43:46 2007 +0200
+++ b/dmd/func.c	Mon Oct 22 15:40:56 2007 +0200
@@ -75,6 +75,7 @@
     shidden = NULL;
     llvmQueued = false;
     llvmThisVar = NULL;
+    llvmNested = NULL;
 }
 
 Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
--- a/gen/toir.c	Fri Oct 19 17:43:46 2007 +0200
+++ b/gen/toir.c	Mon Oct 22 15:40:56 2007 +0200
@@ -41,20 +41,29 @@
     {
         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 @@
     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 @@
         // 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 @@
 
         // 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 @@
             }
         }
         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 @@
         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 @@
             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 @@
         if (fd->llvmValue == 0)
             fd->toObjFile();
         e->val = fd->llvmValue;
-        //e->aspointer = true;
         e->type = elem::FUNC;
     }
     assert(e != 0);
@@ -2595,13 +2633,29 @@
 
     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 @@
     new llvm::StoreInst(castfptr, fptr, p->scopebb());
 
     e->inplace = true;
+    e->mem = lval;
+    e->type = elem::VAR;
 
     return e;
 }
--- a/gen/tollvm.c	Fri Oct 19 17:43:46 2007 +0200
+++ b/gen/tollvm.c	Mon Oct 22 15:40:56 2007 +0200
@@ -875,7 +875,7 @@
     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 @@
         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 @@
     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 @@
     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;
+}
--- a/gen/tollvm.h	Fri Oct 19 17:43:46 2007 +0200
+++ b/gen/tollvm.h	Mon Oct 22 15:40:56 2007 +0200
@@ -40,10 +40,10 @@
 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 @@
 
 llvm::Value* LLVM_DtoArgument(const llvm::Type* paramtype, Argument* fnarg, Expression* argexp);
 
+llvm::Value* LLVM_DtoNestedVariable(VarDeclaration* vd);
+
 #include "enums.h"
--- a/gen/toobj.c	Fri Oct 19 17:43:46 2007 +0200
+++ b/gen/toobj.c	Mon Oct 22 15:40:56 2007 +0200
@@ -699,6 +699,7 @@
 
             // this handling
             if (f->llvmUsesThis) {
+                Logger::println("uses this");
                 if (f->llvmRetInPtr)
                     llvmThisVar = ++func->arg_begin();
                 else
@@ -719,6 +720,34 @@
                 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 @@
                         if (func->getReturnType() == llvm::Type::VoidTy) {
                             new llvm::ReturnInst(gIR->scopebb());
                         }
-                        
                     }
                 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug20.d	Mon Oct 22 15:40:56 2007 +0200
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug21.d	Mon Oct 22 15:40:56 2007 +0200
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug22.d	Mon Oct 22 15:40:56 2007 +0200
@@ -0,0 +1,11 @@
+module bug22;
+
+void main()
+{
+    int i;
+    delegate {
+        i = 42;
+    }();
+    printf("%d\n", i);
+    assert(i == 42);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug23.d	Mon Oct 22 15:40:56 2007 +0200
@@ -0,0 +1,13 @@
+module bug23;
+void main()
+{
+    int i;
+    delegate {
+        i++;
+        delegate {
+            i++;
+        }();
+    }();
+    printf("%d\n", i);
+    assert(i == 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug24.d	Mon Oct 22 15:40:56 2007 +0200
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/bug25.d	Mon Oct 22 15:40:56 2007 +0200
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tester.d	Mon Oct 22 15:40:56 2007 +0200
@@ -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;
+}
--- a/tester.sh	Fri Oct 19 17:43:46 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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