changeset 11:d3ee9efe20e2 trunk

[svn r15] * Fixed a bunch problems with virtual calls. Seems I did some rather poor testing. * Now 50/51 tests compile. * Added a simple runalltests.d scripts that should be run with 'gdmd -run runalltests.d' - LLVMDC will not compile it yet.
author lindquist
date Tue, 02 Oct 2007 05:10:18 +0200
parents c0f2c47e5034
children ee302fe07296
files dmd/declaration.h dmd/func.c dmd/mars.c dmd/mtype.c dmd/mtype.h gen/arrays.c gen/arrays.h gen/elem.c gen/elem.h gen/toir.c gen/tollvm.c gen/tollvm.h gen/toobj.c lib/llvmdcore.bc lphobos/llvm/intrinsic.d runalltests.d test/classes6.d test/classes7.d tester.sh
diffstat 19 files changed, 205 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/dmd/declaration.h	Mon Oct 01 23:32:29 2007 +0200
+++ b/dmd/declaration.h	Tue Oct 02 05:10:18 2007 +0200
@@ -509,6 +509,8 @@
     int cvMember(unsigned char *p);
 
     FuncDeclaration *isFuncDeclaration() { return this; }
+
+    bool llvmQueued;
 };
 
 struct FuncAliasDeclaration : FuncDeclaration
--- a/dmd/func.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/dmd/func.c	Tue Oct 02 05:10:18 2007 +0200
@@ -73,6 +73,7 @@
     nrvo_can = 1;
     nrvo_var = NULL;
     shidden = NULL;
+    llvmQueued = false;
 }
 
 Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
--- a/dmd/mars.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/dmd/mars.c	Tue Oct 02 05:10:18 2007 +0200
@@ -349,7 +349,7 @@
 		global.params.Dversion = 1;
 	    else if (strcmp(p + 1, "w") == 0)
 		global.params.warnings = 1;
-	    else if (strcmp(p + 1, "O") == 0)
+	    else if (p[1] == 'O')
         {
             global.params.optimize = 1;
             if (p[2] != 0) {
--- a/dmd/mtype.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/dmd/mtype.c	Tue Oct 02 05:10:18 2007 +0200
@@ -114,7 +114,7 @@
     this->arrayof = NULL;
     this->vtinfo = NULL;
     this->ctype = NULL;
-    this->llvmType = 0;
+    this->llvmType = NULL;
 }
 
 Type *Type::syntaxCopy()
--- a/dmd/mtype.h	Mon Oct 01 23:32:29 2007 +0200
+++ b/dmd/mtype.h	Tue Oct 02 05:10:18 2007 +0200
@@ -250,7 +250,7 @@
     virtual type *toCParamtype();
     virtual Symbol *toSymbol();
 
-    llvm::Type* llvmType;
+    const llvm::Type* llvmType;
 
     // For eliminating dynamic_cast
     virtual TypeBasic *isTypeBasic();
--- a/gen/arrays.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/arrays.c	Tue Oct 02 05:10:18 2007 +0200
@@ -19,7 +19,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-llvm::StructType* LLVM_DtoArrayType(Type* t)
+const llvm::StructType* LLVM_DtoArrayType(Type* t)
 {
     assert(t->next);
     const llvm::Type* at = LLVM_DtoType(t->next);
@@ -51,7 +51,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-llvm::ArrayType* LLVM_DtoStaticArrayType(Type* t)
+const llvm::ArrayType* LLVM_DtoStaticArrayType(Type* t)
 {
     if (t->llvmType)
         return llvm::cast<llvm::ArrayType>(t->llvmType);
@@ -63,7 +63,7 @@
 
     TypeSArray* tsa = (TypeSArray*)t;
     assert(tsa->dim->type->isintegral());
-    llvm::ArrayType* arrty = llvm::ArrayType::get(at,tsa->dim->toUInteger());
+    const llvm::ArrayType* arrty = llvm::ArrayType::get(at,tsa->dim->toUInteger());
 
     tsa->llvmType = arrty;
     return arrty;
@@ -298,7 +298,7 @@
         inits[i] = v;
     }
 
-    llvm::ArrayType* arrty = LLVM_DtoStaticArrayType(t);
+    const llvm::ArrayType* arrty = LLVM_DtoStaticArrayType(t);
     return llvm::ConstantArray::get(arrty, inits);
 }
 
--- a/gen/arrays.h	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/arrays.h	Tue Oct 02 05:10:18 2007 +0200
@@ -1,9 +1,9 @@
 #ifndef LLVMC_GEN_ARRAYS_H
 #define LLVMC_GEN_ARRAYS_H
 
-llvm::StructType* LLVM_DtoArrayType(Type* t);
+const llvm::StructType* LLVM_DtoArrayType(Type* t);
 
-llvm::ArrayType* LLVM_DtoStaticArrayType(Type* t);
+const llvm::ArrayType* LLVM_DtoStaticArrayType(Type* t);
 
 llvm::Value* LLVM_DtoNullArray(llvm::Value* v);
 
--- a/gen/elem.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/elem.c	Tue Oct 02 05:10:18 2007 +0200
@@ -17,6 +17,7 @@
     type = NONE;
     inplace = false;
     field = false;
+    callconv = (unsigned)-1;
 
     vardecl = 0;
     funcdecl = 0;
--- a/gen/elem.h	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/elem.h	Tue Oct 02 05:10:18 2007 +0200
@@ -31,6 +31,7 @@
     int type;
     bool inplace;
     bool field;
+    unsigned callconv;
 
     VarDeclaration* vardecl;
     FuncDeclaration* funcdecl;
--- a/gen/toir.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/toir.c	Tue Oct 02 05:10:18 2007 +0200
@@ -954,6 +954,8 @@
     // set calling convention
     if ((fn->funcdecl && (fn->funcdecl->llvmInternal != LLVMintrinsic)) || delegateCall)
         call->setCallingConv(LLVM_DtoCallingConv(dlink));
+    else if (fn->callconv != (unsigned)-1)
+        call->setCallingConv(fn->callconv);
 
     delete fn;
     return e;
@@ -1253,7 +1255,7 @@
             funcval = LLVM_DtoGEP(funcval, zero, vtblidx, "tmp", p->scopebb());
             funcval = new llvm::LoadInst(funcval,"tmp",p->scopebb());
             assert(funcval->getType() == fdecl->llvmValue->getType());
-            //funcval = new llvm::BitCastInst(funcval, fdecl->llvmValue->getType(), "tmp", p->scopebb());
+            e->callconv = LLVM_DtoCallingConv(fdecl->linkage);
         }
         e->val = funcval;
         e->type = elem::VAL;
--- a/gen/tollvm.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/tollvm.c	Tue Oct 02 05:10:18 2007 +0200
@@ -159,7 +159,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-llvm::FunctionType* LLVM_DtoFunctionType(Type* t, const llvm::Type* thisparam)
+const llvm::FunctionType* LLVM_DtoFunctionType(Type* t, const llvm::Type* thisparam)
 {
     TypeFunction* f = (TypeFunction*)t;
 
@@ -203,7 +203,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl)
+const llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl)
 {
     TypeFunction* f = (TypeFunction*)fdecl->type;
     assert(f != 0);
@@ -309,7 +309,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-llvm::StructType* LLVM_DtoDelegateType(Type* t)
+const llvm::StructType* LLVM_DtoDelegateType(Type* t)
 {
     const llvm::Type* i8ptr = llvm::PointerType::get(llvm::Type::Int8Ty);
     const llvm::Type* func = LLVM_DtoFunctionType(t->next, i8ptr);
@@ -323,7 +323,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-llvm::Type* LLVM_DtoStructType(Type* t)
+const llvm::Type* LLVM_DtoStructType(Type* t)
 {
     assert(0);
     std::vector<const llvm::Type*> types;
@@ -763,26 +763,40 @@
 
 void LLVM_DtoInitClass(TypeClass* tc, llvm::Value* dst)
 {
-    assert(tc->llvmInit);
-    assert(dst->getType() == tc->llvmInit->getType());
     assert(gIR);
 
     assert(tc->llvmType);
-    uint64_t n = gTargetData->getTypeSize(tc->llvmType);
-    llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty);
+    uint64_t size_t_size = gTargetData->getTypeSize(LLVM_DtoSize_t());
+    uint64_t n = gTargetData->getTypeSize(tc->llvmType) - size_t_size;
 
-    llvm::Value* dstarr = new llvm::BitCastInst(dst,arrty,"tmp",gIR->scopebb());
-    llvm::Value* srcarr = new llvm::BitCastInst(tc->llvmInit,arrty,"tmp",gIR->scopebb());
+    // set vtable field
+    llvm::Value* vtblvar = LLVM_DtoGEPi(dst,0,0,"tmp",gIR->scopebb());
+    assert(tc->sym->llvmVtbl);
+    new llvm::StoreInst(tc->sym->llvmVtbl, vtblvar, gIR->scopebb());
+
+    // copy the static initializer
+    if (n > 0) {
+        assert(tc->llvmInit);
+        assert(dst->getType() == tc->llvmInit->getType());
+
+        llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty);
 
-    llvm::Function* fn = LLVM_DeclareMemCpy32();
-    std::vector<llvm::Value*> llargs;
-    llargs.resize(4);
-    llargs[0] = dstarr;
-    llargs[1] = srcarr;
-    llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
-    llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
+        llvm::Value* dstarr = new llvm::BitCastInst(dst,arrty,"tmp",gIR->scopebb());
+        dstarr = LLVM_DtoGEPi(dstarr,size_t_size,"tmp",gIR->scopebb());
+
+        llvm::Value* srcarr = new llvm::BitCastInst(tc->llvmInit,arrty,"tmp",gIR->scopebb());
+        srcarr = LLVM_DtoGEPi(srcarr,size_t_size,"tmp",gIR->scopebb());
 
-    new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
+        llvm::Function* fn = LLVM_DeclareMemCpy32();
+        std::vector<llvm::Value*> llargs;
+        llargs.resize(4);
+        llargs[0] = dstarr;
+        llargs[1] = srcarr;
+        llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
+        llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
+
+        new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
+    }
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -825,14 +839,19 @@
     return _init;
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////
+
 llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, llvm::Value* i0, llvm::Value* i1, const std::string& var, llvm::BasicBlock* bb)
 {
     std::vector<llvm::Value*> v(2);
     v[0] = i0;
     v[1] = i1;
+    Logger::cout() << "DtoGEP: " << *ptr << '\n';
     return new llvm::GetElementPtrInst(ptr, v.begin(), v.end(), var, bb);
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////
+
 llvm::Value* LLVM_DtoGEP(llvm::Value* ptr, const std::vector<unsigned>& src, const std::string& var, llvm::BasicBlock* bb)
 {
     size_t n = src.size();
@@ -847,9 +866,32 @@
     return new llvm::GetElementPtrInst(ptr, dst.begin(), dst.end(), var, bb);
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+llvm::Value* LLVM_DtoGEPi(llvm::Value* ptr, unsigned i0, unsigned i1, const std::string& var, llvm::BasicBlock* bb)
+{
+    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);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
 llvm::Function* LLVM_DtoDeclareFunction(FuncDeclaration* fdecl)
 {
+    TypeFunction* f = (TypeFunction*)fdecl->type;
+    assert(f != 0);
+
     if (fdecl->llvmValue != 0) {
+        assert(llvm::isa<llvm::Function>(fdecl->llvmValue));
         return llvm::cast<llvm::Function>(fdecl->llvmValue);
     }
 
@@ -863,9 +905,7 @@
     }
 
     // construct function
-    TypeFunction* f = (TypeFunction*)fdecl->type;
-    assert(f != 0);
-    llvm::FunctionType* functype = (f->llvmType == 0) ? LLVM_DtoFunctionType(fdecl) : llvm::cast<llvm::FunctionType>(f->llvmType);
+    const llvm::FunctionType* functype = (f->llvmType == 0) ? LLVM_DtoFunctionType(fdecl) : llvm::cast<llvm::FunctionType>(f->llvmType);
 
     // mangled name
     char* mangled_name = (fdecl->llvmInternal == LLVMintrinsic) ? fdecl->llvmInternal1 : fdecl->mangle();
@@ -881,6 +921,7 @@
 
     fdecl->llvmValue = func;
     f->llvmType = functype;
+    assert(llvm::isa<llvm::FunctionType>(f->llvmType));
 
     if (fdecl->isMain()) {
         gIR->mainFunc = func;
--- a/gen/tollvm.h	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/tollvm.h	Tue Oct 02 05:10:18 2007 +0200
@@ -4,16 +4,16 @@
 
 const llvm::Type* LLVM_DtoType(Type* t);
 
-llvm::Type* LLVM_DtoStructType(Type* t);
+const llvm::Type* LLVM_DtoStructType(Type* t);
 llvm::Value* LLVM_DtoStructZeroInit(TypeStruct* t, llvm::Value* v);
 llvm::Value* LLVM_DtoStructCopy(TypeStruct* t, llvm::Value* dst, llvm::Value* src);
 llvm::Constant* LLVM_DtoStructInitializer(StructInitializer* si);
 
-llvm::FunctionType* LLVM_DtoFunctionType(Type* t, const llvm::Type* thisparam = 0);
-llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl);
+const llvm::FunctionType* LLVM_DtoFunctionType(Type* t, const llvm::Type* thisparam = 0);
+const llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl);
 llvm::Function* LLVM_DtoDeclareFunction(FuncDeclaration* fdecl);
 
-llvm::StructType* LLVM_DtoDelegateType(Type* t);
+const llvm::StructType* LLVM_DtoDelegateType(Type* t);
 llvm::Value* LLVM_DtoNullDelegate(llvm::Value* v);
 llvm::Value* LLVM_DtoDelegateCopy(llvm::Value* dst, llvm::Value* src);
 
@@ -39,5 +39,7 @@
 
 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);
 
 #include "enums.h"
--- a/gen/toobj.c	Mon Oct 01 23:32:29 2007 +0200
+++ b/gen/toobj.c	Tue Oct 02 05:10:18 2007 +0200
@@ -80,18 +80,21 @@
     delete gTargetData;
     gTargetData = 0;
 
+    // emit the llvm main function if necessary
+    if (ir.emitMain) {
+        LLVM_DtoMain();
+    }
+
     // verify the llvm
     std::string verifyErr;
+    Logger::println("Verifying module...");
     if (llvm::verifyModule(*ir.module,llvm::ReturnStatusAction,&verifyErr))
     {
         error("%s", verifyErr.c_str());
         fatal();
     }
-
-    // emit the llvm main function if necessary
-    if (ir.emitMain) {
-        LLVM_DtoMain();
-    }
+    else
+        Logger::println("Verification passed!");
 
     // run passes
     // TODO
@@ -386,6 +389,7 @@
 
         if (FuncDeclaration* fd = dsym->isFuncDeclaration()) {
             fd->toObjFile();
+            assert(fd->llvmValue);
             llvm::Constant* c = llvm::cast<llvm::Constant>(fd->llvmValue);
             sinits.push_back(c);
             sinits_ty.push_back(c->getType());
@@ -437,17 +441,11 @@
     assert(svtblVar != 0);
     gIR->topstruct().inits[0] = svtblVar;
 
-    //assert(tk == gIR->topstruct().size());
-    #ifndef LLVMD_NO_LOGGER
-    Logger::cout() << *structtype << '\n';
-    //for (size_t k=0; k<gIR->topstruct().inits.size(); ++k)
-    //    Logger::cout() << *gIR->topstruct().inits[k] << '\n';
-    #endif
     _init = llvm::ConstantStruct::get(structtype,gIR->topstruct().inits);
     assert(_init);
     std::string initname(mangle());
     initname.append("__initZ");
-    Logger::cout() << *_init << '\n';
+    //Logger::cout() << *_init << '\n';
     llvm::GlobalVariable* initvar = new llvm::GlobalVariable(ts->llvmType, true, _linkage, 0, initname, gIR->module);
     ts->llvmInit = initvar;
     if (define_vtable) {
@@ -616,27 +614,33 @@
     llvm::Function* func = LLVM_DtoDeclareFunction(this);
 
     if (!gIR->queueClassMethods.empty() && gIR->queueClassMethods.back()) {
-        Logger::println("queueing %s", toChars());
-        assert(!gIR->classmethods.empty());
-        gIR->classmethods.back().push_back(this);
+        if (!llvmQueued) {
+            Logger::println("queueing %s", toChars());
+            assert(!gIR->classmethods.empty());
+            gIR->classmethods.back().push_back(this);
+            llvmQueued = true;
+        }
         return; // we wait with the definition as they might invoke a virtual method and the vtable is not yet complete
     }
 
     TypeFunction* f = (TypeFunction*)type;
-    llvm::FunctionType* functype = llvm::cast<llvm::FunctionType>(f->llvmType);
+    assert(f->llvmType);
+    const llvm::FunctionType* functype = llvm::cast<llvm::FunctionType>(llvmValue->getType()->getContainedType(0));
 
     // only members of the current module maybe be defined
     if (getModule() == gIR->dmodule)
     {
+        llvmDModule = gIR->dmodule;
+
         bool allow_fbody = true;
         // handle static constructor / destructor
         if (isStaticCtorDeclaration() || isStaticDtorDeclaration()) {
             const llvm::ArrayType* sctor_type = llvm::ArrayType::get(llvm::PointerType::get(functype),1);
             //Logger::cout() << "static ctor type: " << *sctor_type << '\n';
-            
+
             llvm::Constant* sctor_func = llvm::cast<llvm::Constant>(llvmValue);
             //Logger::cout() << "static ctor func: " << *sctor_func << '\n';
-            
+
             llvm::Constant* sctor_init = 0;
             if (llvmInternal == LLVMnull)
             {
@@ -648,10 +652,9 @@
             {
                 sctor_init = llvm::ConstantArray::get(sctor_type,&sctor_func,1);
             }
-            
+
             //Logger::cout() << "static ctor init: " << *sctor_init << '\n';
-            
-            
+
             // output the llvm.global_ctors array
             const char* varname = isStaticCtorDeclaration() ? "_d_module_ctor_array" : "_d_module_dtor_array";
             llvm::GlobalVariable* sctor_arr = new llvm::GlobalVariable(sctor_type, false, llvm::GlobalValue::AppendingLinkage, sctor_init, varname, gIR->module);
@@ -660,6 +663,9 @@
         // function definition
         if (allow_fbody && fbody != 0)
         {
+            // first make absolutely sure the type is up to date
+            f->llvmType = llvmValue->getType()->getContainedType(0);
+
             if (isMain())
                 gIR->emitMain = true;
 
@@ -672,10 +678,10 @@
 
             //assert(gIR->scopes.empty());
             gIR->scopes.push_back(irs);
-                
+
                 // create alloca point
                 f->llvmAllocaPoint = new llvm::BitCastInst(llvm::ConstantInt::get(llvm::Type::Int32Ty,0,false),llvm::Type::Int32Ty,"alloca point",gIR->scopebb());
-                
+
                 // output function body
                 fbody->toIR(gIR);
 
@@ -686,7 +692,7 @@
                     //new llvm::BranchInst(irs.end, irs.begin);
                     new llvm::ReturnInst(gIR->scopebb());
                 }
-                
+
                 // erase alloca point
                 f->llvmAllocaPoint->eraseFromParent();
                 f->llvmAllocaPoint = 0;
@@ -710,8 +716,4 @@
             }
         }
     }
-
-    llvmDModule = gIR->dmodule;
-
-    Logger::println("FuncDeclaration done\n");
 }
Binary file lib/llvmdcore.bc has changed
--- a/lphobos/llvm/intrinsic.d	Mon Oct 01 23:32:29 2007 +0200
+++ b/lphobos/llvm/intrinsic.d	Tue Oct 02 05:10:18 2007 +0200
@@ -29,10 +29,10 @@
 
 pragma(LLVM_internal, "intrinsic", "llvm.prefetch")
     void llvm_prefetch(void* ptr, uint rw, uint locality);
+*/
 
 pragma(LLVM_internal, "intrinsic", "llvm.readcyclecounter")
-    ulong llvm_readcyclecounter();
-*/
+    ulong readcyclecounter();
 
 // standard C intrinsics
 pragma(LLVM_internal, "intrinsic", "llvm.memcpy.i32")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runalltests.d	Tue Oct 02 05:10:18 2007 +0200
@@ -0,0 +1,33 @@
+module runalltests;
+
+import std.file;
+import std.path;
+import std.process;
+import std.stdio;
+
+int main(string[] args) {
+    string[] good;
+    string[] bad;
+
+    auto contents = listdir("test", "*.d");
+    foreach(c; contents) {
+        if (system("./tester.sh "~getName(c)~" ll") != 0) {
+            bad ~= c;
+        }
+        else {
+            good ~= c;
+        }
+    }
+
+    int ret = 0;
+    if (bad.length > 0) {
+        writefln(bad.length, '/', contents.length, " tests failed:");
+        foreach(b; bad) {
+            writefln("  ",b);
+        }
+        ret = 1;
+    }
+
+    writefln(good.length, '/', contents.length, " tests passed");
+    return ret;
+}
--- a/test/classes6.d	Mon Oct 01 23:32:29 2007 +0200
+++ b/test/classes6.d	Tue Oct 02 05:10:18 2007 +0200
@@ -4,12 +4,34 @@
 {
     void f()
     {
-        printf("hello world\n");
+        printf("world\n");
+    }
+}
+
+class D : C
+{
+    void f()
+    {
+        printf("moon\n");
     }
 }
 
+
+extern(C)
+{
+    void srand(uint seed);
+    int rand();
+}
+
+import llvm.intrinsic;
+
 void main()
 {
-    scope c = new C;
+    C c;
+    srand(readcyclecounter());
+    if (rand() % 2)
+        c = new C;
+    else
+        c = new D;
     c.f();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/classes7.d	Tue Oct 02 05:10:18 2007 +0200
@@ -0,0 +1,21 @@
+module classes7;
+
+class C
+{
+    int i=0;
+    void f()
+    {
+        i=42;
+    }
+    void g()
+    {
+        f();
+    }
+}
+
+void main()
+{
+    scope c = new C;
+    c.g();
+    assert(c.i == 43);
+}
--- a/tester.sh	Mon Oct 01 23:32:29 2007 +0200
+++ b/tester.sh	Tue Oct 02 05:10:18 2007 +0200
@@ -6,24 +6,30 @@
 fi
 
 if [ "$2" = "ll" ]; then
-    make &&
     llvmdc $1 -Itest -odtest -c &&
     llvm-dis -f $1.bc &&
     cat $1.ll
     exit $?
+elif [ "$2" = "llopt" ]; then
+    llvmdc $1 -Itest -odtest -c &&
+    opt -f -o=$1.bc -std-compile-opts $1.bc &&
+    llvm-dis -f $1.bc &&
+    cat $1.ll
+    exit $?
 elif [ "$2" = "run" ]; then
-    make &&
     llvmdc $1 -Itest -odtest -of$1 &&
     $1
     exit $?
 elif [ "$2" = "c" ]; then
-    make &&
     llvmdc $1 -Itest -odtest -c
     exit $?
 elif [ "$2" = "gdb" ]; then
-    make &&
     gdb --args llvmdc $1 -Itest -odtest '-c'
     exit $?
+elif [ "$2" = "gdbrun" ]; then
+    llvmdc $1 -Itest -odtest '-c' &&
+    gdb $1
+    exit $?
 else
     echo "bad command or filename"
 fi