nitmetrics: compute scalar module count
[nit.git] / src / global_compiler.nit
index 4ddf228..df8be17 100644 (file)
@@ -46,6 +46,9 @@ redef class ToolContext
 end
 
 redef class ModelBuilder
+       # Entry point to performs a global compilation on the AST of a complete program.
+       # `mainmodule` is the main module of the program
+       # `runtime_type_analysis` is a already computer type analysis.
        fun run_global_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis)
        do
                var time0 = get_time
@@ -59,16 +62,21 @@ redef class ModelBuilder
                v.add_decl("#include <string.h>")
 
                # TODO: Better way to activate the GC
-               #v.add_decl("#include <gc/gc.h>")
-               v.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
+               v.add_decl("#include <gc/gc.h>")
+               #v.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
 
-               # Declare structure for each live type
+               # Declaration of structures the live Nit types
+               # Each live type is generated as an independent C `struct' type.
+               # They only share a common first field `classid` used to implement the polymorphism.
+               # Usualy, all C variables that refers to a Nit object are typed on the abstract struct `val' that contains only the `classid` field.
 
-               v.add_decl("typedef struct \{int classid;\} val;")
+               v.add_decl("typedef struct \{int classid;\} val; /* general C type representing a Nit instance. */")
                for t in runtime_type_analysis.live_types do
                        compiler.declare_runtimeclass(v, t)
                end
 
+               # Global variable used by the legacy native interface
+
                v.add_decl("extern int glob_argc;")
                v.add_decl("extern char **glob_argv;")
                v.add_decl("extern val *glob_sys;")
@@ -86,8 +94,11 @@ redef class ModelBuilder
                # Init instance code (allocate and init-arguments)
 
                for t in runtime_type_analysis.live_types do
-                       if t.ctype != "val*" then continue
-                       compiler.generate_init_instance(t)
+                       if t.ctype == "val*" then
+                               compiler.generate_init_instance(t)
+                       else
+                               compiler.generate_box_instance(t)
+                       end
                end
 
                # The main function of the C
@@ -110,18 +121,31 @@ redef class ModelBuilder
                if main_method != null then
                        v.send(main_method, [glob_sys])
                end
+               v.add("return 0;")
                v.add("\}")
 
                # Compile until all runtime_functions are visited
 
                while not compiler.todos.is_empty do
                        var m = compiler.todos.shift
-                       self.toolcontext.info("Compile {m.mmethoddef} for {m.recv} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
-                       m.mmethoddef.compile_to_c(compiler, self, m.recv)
+                       self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
+                       m.compile_to_c(compiler)
                end
                self.toolcontext.info("Total methods to compile to C: {compiler.visitors.length}", 2)
 
+               var time1 = get_time
+               self.toolcontext.info("*** END VISITING: {time1-time0} ***", 2)
+               write_and_make(compiler)
+       end
+
+       private fun write_and_make(compiler: GlobalCompiler)
+       do
+               var mainmodule = compiler.mainmodule
+
                # Generate the .h and .c files
+               # A single C file regroups many compiled rumtime functions
+               # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files
+               var time0 = get_time
 
                var outname = self.toolcontext.opt_output.value
                if outname == null then
@@ -178,13 +202,23 @@ redef class ModelBuilder
                makefile.write("all: {outname}\n\n")
 
                var ofiles = new Array[String]
+               # Compile each generated file
                for f in cfiles do
                        var o = f.strip_extension(".c") + ".o"
-                       makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -I .nit_compile -I ../clib -c -o {o} {f}\n\n")
+                       makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
                        ofiles.add(o)
                end
-
-               makefile.write("{outname}: {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\t$(CC) -Wl,--warn-unresolved-symbols $(CFLAGS) $(LDFLAGS) $(LDLIBS) -I .nit_compile -I ../clib -o {outname} {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\n")
+               # Compile each required extern body into a specific .o
+               for f in compiler.extern_bodies do
+                       i += 1
+                       var o = ".nit_compile/{mainmodule.name}.{i}.o"
+                       makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
+                       ofiles.add(o)
+               end
+               # Link edition
+               makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outname} {ofiles.join(" ")} $(LDLIBS)\n\n")
+               # Clean
+               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
                makefile.close
                self.toolcontext.info("Generated makefile: {makename}", 2)
 
@@ -197,13 +231,13 @@ redef class ModelBuilder
 
                time0 = time1
                self.toolcontext.info("*** COMPILING C ***", 1)
-               self.toolcontext.info("make -f {makename} -j 4", 2)
+               self.toolcontext.info("make -B -f {makename} -j 4", 2)
 
                var res
                if self.toolcontext.verbose_level >= 3 then
-                       res = sys.system("make -f {makename} -j 4 2>&1")
+                       res = sys.system("make -B -f {makename} -j 4 2>&1")
                else
-                       res = sys.system("make -f {makename} -j 4 2>&1 >/dev/null")
+                       res = sys.system("make -B -f {makename} -j 4 2>&1 >/dev/null")
                end
                if res != 0 then
                        toolcontext.error(null, "make failed! Error code: {res}.")
@@ -225,7 +259,7 @@ private class GlobalCompiler
        # The modeulbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder
 
-       # Is hardening asked (see --hardening)
+       # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
        init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis, modelbuilder: ModelBuilder)
@@ -242,6 +276,7 @@ private class GlobalCompiler
        end
 
        # Subset of runtime_type_analysis.live_types that contains only primitive types
+       # Used to implement the equal test
        var live_primitive_types: Array[MClassType]
 
        # runtime_functions that need to be compiled
@@ -259,11 +294,12 @@ private class GlobalCompiler
        # Where global declaration are stored (the main .h)
        #
        # FIXME: should not be a vistor but just somewhere to store lines
-       # FIXME: should not have a global .h since its does no helps recompilation
+       # FIXME: should not have a global .h since it does not help recompilations
        var header: nullable GlobalCompilerVisitor = null
 
        # The list of all associated visitors
        # Used to generate .c files
+       # FIXME: should not be vistors but just somewhere to store lines
        private var visitors: List[GlobalCompilerVisitor] = new List[GlobalCompilerVisitor]
 
        # List of additional .c files required to compile (native interface)
@@ -292,11 +328,19 @@ private class GlobalCompiler
                var idname = "ID_" + mtype.c_name
                self.classids[mtype] = idname
                v.add_decl("#define {idname} {idnum} /* {mtype} */")
+
                v.add_decl("struct {mtype.c_name} \{")
                v.add_decl("int classid; /* must be {idname} */")
+
                if mtype.ctype != "val*" then
+                       # Is the Nit type is native then the struct is a box with two fields:
+                       # * the `classid` to be polymorph
+                       # * the `value` that contains the native value.
                        v.add_decl("{mtype.ctype} value;")
                end
+
+               # Collect all attributes and associate them a field in the structure.
+               # Note: we do not try to optimize the order and helps CC to optimize the client code.
                for cd in mtype.collect_mclassdefs(self.mainmodule) do
                        for p in cd.intro_mproperties do
                                if not p isa MAttribute then continue
@@ -336,6 +380,23 @@ private class GlobalCompiler
                v.add("\}")
        end
 
+       fun generate_box_instance(mtype: MClassType)
+       do
+               assert self.runtime_type_analysis.live_types.has(mtype)
+               assert mtype.ctype != "val*"
+               var v = new GlobalCompilerVisitor(self)
+
+               self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});")
+               v.add_decl("/* allocate {mtype} */")
+               v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
+               v.add("struct {mtype.c_name}*res = GC_MALLOC(sizeof(struct {mtype.c_name}));")
+               v.add("res->classid = {self.classid(mtype)};")
+               v.add("res->value = value;")
+               v.add("return (val*)res;")
+               v.add("\}")
+
+       end
+
        # look for a needed .h and .c file for a given .nit source-file
        # FIXME: bad API, parameter should be a MModule, not its source-file
        fun add_extern(file: String)
@@ -357,8 +418,9 @@ private class GlobalCompiler
                if tryfile.file_exists then
                        self.extern_bodies.add(tryfile)
                end
-               #(new OFStream.open("{file.basename("")}._nitni.h")).close
        end
+
+       private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
 end
 
 redef class String
@@ -491,16 +553,38 @@ end
 
 # A C function associated to a Nit method
 # Because of customization, a given Nit method can be compiler more that once
-private class RuntimeFunction
+private abstract class RuntimeFunction
        # The associated Nit method
        var mmethoddef: MMethodDef
 
+       # The mangled c name of the runtime_function
+       fun c_name: String is abstract
+
+       # Implements a call of the runtime_function
+       # May inline the body or generate a C function call
+       fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
+
+       # Generate the code for the RuntimeFunction
+       # Warning: compile more than once compilation makes CC unhappy
+       fun compile_to_c(compiler: GlobalCompiler) is abstract
+end
+
+# A runtime function customized on a specific monomrph receiver type
+private class CustomizedRuntimeFunction
+       super RuntimeFunction
+
        # The considered reciever
        # (usually is a live type but no strong guarantee)
        var recv: MClassType
 
+       init(mmethoddef: MMethodDef, recv: MClassType)
+       do
+               super(mmethoddef)
+               self.recv = recv
+       end
+
        # The mangled c name of the runtime_function
-       fun c_name: String
+       redef fun c_name: String
        do
                var res = self.c_name_cache
                if res != null then return res
@@ -518,7 +602,7 @@ private class RuntimeFunction
        redef fun ==(o)
        # used in the compiler worklist
        do
-               if not o isa RuntimeFunction then return false
+               if not o isa CustomizedRuntimeFunction then return false
                if self.mmethoddef != o.mmethoddef then return false
                if self.recv != o.recv then return false
                return true
@@ -540,13 +624,81 @@ private class RuntimeFunction
                end
        end
 
-       # Implements a call of the runtime_function
-       # May inline the body
-       fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
+       # compile the code customized for the reciever
+       redef fun compile_to_c(compiler)
+       do
+               var recv = self.recv
+               var mmethoddef = self.mmethoddef
+               if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then
+                       print("problem: why do we compile {self} for {recv}?")
+                       abort
+               end
+
+               var v = new GlobalCompilerVisitor(compiler)
+               var selfvar = new RuntimeVariable("self", recv, recv)
+               if compiler.runtime_type_analysis.live_types.has(recv) then
+                       selfvar.is_exact = true
+               end
+               var arguments = new Array[RuntimeVariable]
+               var frame = new Frame(v, mmethoddef, recv, arguments)
+               v.frame = frame
+
+               var sig = new Buffer
+               var comment = new Buffer
+               var ret = mmethoddef.msignature.return_mtype
+               if ret != null then
+                       ret = v.resolve_for(ret, selfvar)
+                       sig.append("{ret.ctype} ")
+               else if mmethoddef.mproperty.is_new then
+                       ret = recv
+                       sig.append("{ret.ctype} ")
+               else
+                       sig.append("void ")
+               end
+               sig.append(self.c_name)
+               sig.append("({recv.ctype} self")
+               comment.append("(self: {recv}")
+               arguments.add(selfvar)
+               for i in [0..mmethoddef.msignature.arity[ do
+                       var mtype = mmethoddef.msignature.mparameters[i].mtype
+                       if i == mmethoddef.msignature.vararg_rank then
+                               mtype = v.get_class("Array").get_mtype([mtype])
+                       end
+                       mtype = v.resolve_for(mtype, selfvar)
+                       comment.append(", {mtype}")
+                       sig.append(", {mtype.ctype} p{i}")
+                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
+                       arguments.add(argvar)
+               end
+               sig.append(")")
+               comment.append(")")
+               if ret != null then
+                       comment.append(": {ret}")
+               end
+               compiler.header.add_decl("{sig};")
+
+               v.add_decl("/* method {self} for {comment} */")
+               v.add_decl("{sig} \{")
+               #v.add("printf(\"method {self} for {comment}\\n\");")
+               if ret != null then
+                       frame.returnvar = v.new_var(ret)
+               end
+               frame.returnlabel = v.get_name("RET_LABEL")
+
+               mmethoddef.compile_inside_to_c(v, arguments)
+
+               v.add("{frame.returnlabel.as(not null)}:;")
+               if ret != null then
+                       v.add("return {frame.returnvar.as(not null)};")
+               end
+               v.add("\}")
+       end
+
+       redef fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var ret = self.mmethoddef.msignature.return_mtype
                if self.mmethoddef.mproperty.is_new then
-                       ret = arguments.first.mtype
+                       ret = recv
                end
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
@@ -579,8 +731,10 @@ private class RuntimeFunction
        end
 end
 
-# A runtime variable hold a runtime value in C
-# Runtime variables are associated to local variables and intermediate results
+# A runtime variable hold a runtime value in C.
+# Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
+#
+# The tricky point is that a single C variable can be associated to more than one RuntimeVariable because the static knowledge of the type of an expression can vary in the C code.
 private class RuntimeVariable
        # The name of the variable in the C code
        var name: String
@@ -595,6 +749,15 @@ private class RuntimeVariable
        # false (usual value) means that the variable is a mcasttype or a subtype.
        var is_exact: Bool = false
 
+       init(name: String, mtype: MType, mcasttype: MType)
+       do
+               self.name = name
+               self.mtype = mtype
+               self.mcasttype = mcasttype
+               assert not mtype.need_anchor
+               assert not mcasttype.need_anchor
+       end
+
        redef fun to_s do return name
 
        redef fun inspect
@@ -611,11 +774,11 @@ private class RuntimeVariable
                else
                        type_str = "{mtype}({mcasttype}{exact_str})"
                end
-               return "<{inspect_head} {name}:{type_str}>"
+               return "<{name}:{type_str}>"
        end
 end
 
-# Visit the AST to generate the C code.
+# A visitor on the AST of property definition that generate the C code.
 # Because of inlining, a visitor can visit more than one property.
 private class GlobalCompilerVisitor
        # The associated compiler
@@ -642,7 +805,7 @@ private class GlobalCompilerVisitor
        # Force to get the primitive property named `name' in the instance `recv' or abort
        fun get_property(name: String, recv: MType): MMethod
        do
-               return self.compiler.mainmodule.force_get_primitive_method(name, recv)
+               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule)
        end
 
        # The current Frame
@@ -675,7 +838,8 @@ private class GlobalCompilerVisitor
        # The current visited AST node
        var current_node: nullable AExpr = null
 
-       # Compile an expression an return its
+       # Compile an expression an return its result
+       # `mtype` is the expected return type, pass null if no specific type is expected.
        fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
        do
                var old = self.current_node
@@ -690,20 +854,22 @@ private class GlobalCompilerVisitor
        end
 
        # Unsafely cast a value to a new type
+       # ie the result share the same C variable but my have a different mcasttype
+       # NOTE: if the adaptation is useless then `value' is returned as it.
        # ENSURE: return.name == value.name
        fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable
        do
                mtype = self.anchor(mtype)
-               if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
+               var valmtype = value.mcasttype
+               if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                        return value
                end
 
-               var valmtype = value.mtype
                if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
-                       var res = new RuntimeVariable(value.name, value.mtype, valmtype.mtype)
+                       var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
                        return res
                else
-                       var res = new RuntimeVariable(value.name, value.mtype, mtype)
+                       var res = new RuntimeVariable(value.name, valmtype, mtype)
                        return res
                end
        end
@@ -724,12 +890,10 @@ private class GlobalCompilerVisitor
                                self.add("printf(\"Dead code executed!\\n\"); exit(1);")
                                return res
                        end
-                       self.add("{res} = GC_MALLOC(sizeof(struct {valtype.c_name})); /* autobox from {value.mtype} to {mtype} */")
-                       self.add("{res}->classid = {self.compiler.classid(valtype)};")
-                       self.add("((struct {valtype.c_name}*){res})->value = {value};")
+                       self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
                else
-                       # Bad things will append!
+                       # Bad things will appen!
                        var res = self.new_var(mtype)
                        self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
                        self.add("printf(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); exit(1);")
@@ -752,7 +916,7 @@ private class GlobalCompilerVisitor
                return expr(nexpr, bool_type)
        end
 
-       # Compile statement
+       # Compile a statement (if any)
        fun stmt(nexpr: nullable AExpr)
        do
                if nexpr == null then return
@@ -762,7 +926,7 @@ private class GlobalCompilerVisitor
                self.current_node = old
        end
 
-       # Safely show a debug message on the current node and repeat the message in the C code
+       # Safely show a debug message on the current node and repeat the message in the C code as a comment
        fun debug(message: String)
        do
                var node = self.current_node
@@ -777,11 +941,7 @@ private class GlobalCompilerVisitor
        # Return a new uninitialized local runtime_variable
        fun new_var(mtype: MType): RuntimeVariable
        do
-               if mtype isa MNullType then
-                       mtype = self.object_type
-               else
-                       mtype = self.anchor(mtype)
-               end
+               mtype = self.anchor(mtype)
                var name = self.get_name("var")
                var res = new RuntimeVariable(name, mtype, mtype)
                self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
@@ -814,6 +974,20 @@ private class GlobalCompilerVisitor
 
        private var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
 
+       # Return an unique and stable identifier associated with an escapemark
+       fun escapemark_name(e: nullable EscapeMark): String
+       do
+               assert e != null
+               if escapemark_names.has_key(e) then return escapemark_names[e]
+               var name = e.name
+               if name == null then name = "label"
+               name = get_name(name)
+               escapemark_names[e] = name
+               return name
+       end
+
+       private var escapemark_names = new HashMap[EscapeMark, String]
+
        # Return a new name based on `s' and unique in the visitor
        fun get_name(s: String): String
        do
@@ -854,7 +1028,7 @@ private class GlobalCompilerVisitor
                        var types = [mtype]
                        return types
                end
-               var cache = self.collect_types_cache
+               var cache = self.compiler.collect_types_cache
                if cache.has_key(mtype) then
                        return cache[mtype]
                end
@@ -868,8 +1042,6 @@ private class GlobalCompilerVisitor
                return types
        end
 
-       private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
-
        fun resolve_for(mtype: MType, recv: RuntimeVariable): MType
        do
                if not mtype.need_anchor then return mtype
@@ -913,18 +1085,23 @@ private class GlobalCompilerVisitor
                        return res
                end
                if args.first.mcasttype isa MNullableType then
+                       # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException
                        self.add("if ({args.first} == NULL) \{ /* Special null case */")
                        if m.name == "==" then
                                assert res != null
-                               if args[1].mcasttype.ctype == "val*" then
+                               if args[1].mcasttype isa MNullableType then
                                        self.add("{res} = ({args[1]} == NULL);")
+                               else if args[1].mcasttype isa MNullType then
+                                       self.add("{res} = 1; /* is null */")
                                else
                                        self.add("{res} = 0; /* {args[1].inspect} cannot be null */")
                                end
                        else if m.name == "!=" then
                                assert res != null
-                               if args[1].mcasttype.ctype == "val*" then
+                               if args[1].mcasttype isa MNullableType then
                                        self.add("{res} = ({args[1]} != NULL);")
+                               else if args[1].mcasttype isa MNullType then
+                                       self.add("{res} = 0; /* is null */")
                                else
                                        self.add("{res} = 1; /* {args[1].inspect} cannot be null */")
                                end
@@ -942,6 +1119,9 @@ private class GlobalCompilerVisitor
                                self.add("/* skip {t}, no method {m} */")
                                continue
                        end
+                       if propdefs.length > 1 then
+                               self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
+                       end
                        var propdef = propdefs.first
                        if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
                                defaultpropdef = propdef
@@ -968,6 +1148,21 @@ private class GlobalCompilerVisitor
                return res
        end
 
+       # Generate a monomorphic send for the method `m', the type `t' and the arguments `args'
+       fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               assert t isa MClassType
+               var propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
+               if propdefs.length == 0 then
+                       abort
+               end
+               if propdefs.length > 1 then
+                       self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
+               end
+               var propdef = propdefs.first
+               return self.call(propdef, t, args)
+       end
+
        fun check_valid_reciever(recvtype: MClassType)
        do
                if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return
@@ -975,6 +1170,7 @@ private class GlobalCompilerVisitor
                abort
        end
 
+       # Generate a static call on a method definition
        fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                check_valid_reciever(recvtype)
@@ -1003,7 +1199,7 @@ private class GlobalCompilerVisitor
                                vararg.add(rawargs[i+1])
                        end
                        # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis
-                       var elttype = m.msignature.parameter_mtypes[vararg_rank]
+                       var elttype = m.msignature.mparameters[vararg_rank].mtype
                        elttype = self.resolve_for(elttype, recv)
                        args.add(self.array_instance(vararg, elttype))
 
@@ -1013,10 +1209,14 @@ private class GlobalCompilerVisitor
                else
                        args = args.to_a
                end
-               assert args.length == m.msignature.arity + 1 # because of self
+               if args.length != m.msignature.arity + 1 then # because of self
+                       add("printf(\"NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.\\n\"); exit(1);")
+                       debug("NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.")
+                       return null
+               end
 
                args.first = recv
-               var rm = new RuntimeFunction(m, recvtype)
+               var rm = new CustomizedRuntimeFunction(m, recvtype)
                return rm.call(self, args)
        end
 
@@ -1024,7 +1224,7 @@ private class GlobalCompilerVisitor
        do
                var recv = args.first
                for i in [0..m.msignature.arity[ do
-                       var t = m.msignature.parameter_mtypes[i]
+                       var t = m.msignature.mparameters[i].mtype
                        if i == m.msignature.vararg_rank then
                                t = args[i+1].mtype
                        end
@@ -1217,7 +1417,8 @@ private class GlobalCompilerVisitor
        fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable
        do
                elttype = self.anchor(elttype)
-               var res = self.init_instance(self.get_class("Array").get_mtype([elttype]))
+               var arraytype = self.get_class("Array").get_mtype([elttype])
+               var res = self.init_instance(arraytype)
                self.add("\{ /* {res} = array_instance Array[{elttype}] */")
                var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype]))
                nat.is_exact = true
@@ -1227,7 +1428,7 @@ private class GlobalCompilerVisitor
                        self.add("{nat}[{i}] = {r};")
                end
                var length = self.int_instance(array.length)
-               self.send(self.get_property("with_native", res.mtype), [res, nat, length])
+               self.send(self.get_property("with_native", arraytype), [res, nat, length])
                self.check_init_instance(res)
                self.add("\}")
                return res
@@ -1248,7 +1449,7 @@ private class GlobalCompilerVisitor
                var res2 = self.init_instance(mtype)
                self.add("{res} = {res2};")
                var length = self.int_instance(string.length)
-               self.send(self.get_property("with_native", res.mtype), [res, nat, length])
+               self.send(self.get_property("with_native", mtype), [res, nat, length])
                self.check_init_instance(res)
                self.add("{name} = {res};")
                self.add("\}")
@@ -1260,9 +1461,9 @@ private class GlobalCompilerVisitor
        fun add_abort(message: String)
        do
                if self.current_node != null and self.current_node.location.file != null then
-                       self.add("fprintf(stderr, \"%s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
+                       self.add("fprintf(stderr, \"Runtime error: %s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
                else
-                       self.add("fprintf(stderr, \"%s\\n\", \"{message.escape_to_c}\");")
+                       self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");")
                end
                self.add("exit(1);")
        end
@@ -1336,91 +1537,12 @@ redef class MMethodDef
                end
                return null
        end
-
-       # Compile the body in a new visitor
-       private fun compile_to_c(compiler: GlobalCompiler, modelbuilder: ModelBuilder, recv: MClassType)
-       do
-               if not recv.is_subtype(compiler.mainmodule, null, self.mclassdef.bound_mtype) then
-                       print("problem: why do we compile {self} for {recv}?")
-                       abort
-               end
-
-               var v = new GlobalCompilerVisitor(compiler)
-               var selfvar = new RuntimeVariable("self", recv, recv)
-               if compiler.runtime_type_analysis.live_types.has(recv) then
-                       selfvar.is_exact = true
-               end
-               var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, self, recv, arguments)
-               v.frame = frame
-
-               var sig = new Buffer
-               var comment = new Buffer
-               var ret = self.msignature.return_mtype
-               if ret != null then
-                       ret = v.resolve_for(ret, selfvar)
-                       sig.append("{ret.ctype} ")
-               else if self.mproperty.is_new then
-                       ret = recv
-                       sig.append("{ret.ctype} ")
-               else
-                       sig.append("void ")
-               end
-               sig.append(self.c_name)
-               if recv != self.mclassdef.bound_mtype then
-                       sig.append("__{recv.c_name}")
-               end
-               sig.append("({recv.ctype} self")
-               comment.append("(self: {recv}")
-               arguments.add(selfvar)
-               for i in [0..self.msignature.arity[ do
-                       var mtype = self.msignature.parameter_mtypes[i]
-                       if i == self.msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
-                       end
-                       mtype = v.resolve_for(mtype, selfvar)
-                       comment.append(", {mtype}")
-                       sig.append(", {mtype.ctype} p{i}")
-                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
-                       arguments.add(argvar)
-               end
-               sig.append(")")
-               comment.append(")")
-               if ret != null then
-                       comment.append(": {ret}")
-               end
-               compiler.header.add_decl("{sig};")
-
-               v.add_decl("/* method {self} for {comment} */")
-               v.add_decl("{sig} \{")
-               #v.add("printf(\"method {self} for {comment}\\n\");")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
-
-               if modelbuilder.mpropdef2npropdef.has_key(self) then
-                       var npropdef = modelbuilder.mpropdef2npropdef[self]
-                       npropdef.compile_to_c(v, self, arguments)
-               else if self.mproperty.name == "init" then
-                       var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
-                       nclassdef.compile_to_c(v, self, arguments)
-               else
-                       abort
-               end
-
-               v.add("{frame.returnlabel.as(not null)}:;")
-               if ret != null then
-                       v.add("return {frame.returnvar.as(not null)};")
-               end
-               v.add("\}")
-       end
 end
 
 redef class APropdef
        private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
-               v.add("printf(\"Not implemented {class_name} {mpropdef} at {location.to_s}\\n\");")
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
                debug("Not yet implemented")
        end
 
@@ -1724,7 +1846,7 @@ redef class AInternMethPropdef
                        end
                        return
                end
-               v.add("printf(\"Not implemented {class_name}:{mpropdef} at {location.to_s}\\n\");")
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
                debug("Not implemented {mpropdef}")
        end
 end
@@ -1851,7 +1973,7 @@ redef class AExpr
        private fun expr(v: GlobalCompilerVisitor): nullable RuntimeVariable
        do
                debug("Unimplemented expr {class_name}")
-               v.add("printf(\"Not implemented {class_name}:{location.to_s}\\n\");")
+               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
                var mtype = self.mtype
                if mtype == null then
                        return null
@@ -1933,14 +2055,14 @@ end
 redef class AContinueExpr
        redef fun stmt(v)
        do
-               v.add("goto CONTINUE_{self.escapemark.object_id};")
+               v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
        end
 end
 
 redef class ABreakExpr
        redef fun stmt(v)
        do
-               v.add("goto BREAK_{self.escapemark.object_id};")
+               v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
        end
 end
 
@@ -1996,7 +2118,7 @@ redef class ADoExpr
                v.stmt(self.n_block)
                var escapemark = self.escapemark
                if escapemark != null then
-                       v.add("BREAK_{escapemark.object_id}: (void)0;")
+                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
                end
        end
 end
@@ -2008,9 +2130,9 @@ redef class AWhileExpr
                var cond = v.expr_bool(self.n_expr)
                v.add("if (!{cond}) break;")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
                v.add("\}")
-               v.add("BREAK_{escapemark.object_id}: (void)0;")
+               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
        end
 end
 
@@ -2019,9 +2141,9 @@ redef class ALoopExpr
        do
                v.add("for(;;) \{")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
                v.add("\}")
-               v.add("BREAK_{escapemark.object_id}: (void)0;")
+               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
        end
 end
 
@@ -2035,14 +2157,25 @@ redef class AForExpr
                var ok = v.send(v.get_property("is_ok", it.mtype), [it])
                assert ok != null
                v.add("if(!{ok}) break;")
-               var i = v.send(v.get_property("item", it.mtype), [it])
-               assert i != null
-               v.assign(v.variable(variables.first), i)
+               if self.variables.length == 1 then
+                       var i = v.send(v.get_property("item", it.mtype), [it])
+                       assert i != null
+                       v.assign(v.variable(variables.first), i)
+               else if self.variables.length == 2 then
+                       var i = v.send(v.get_property("key", it.mtype), [it])
+                       assert i != null
+                       v.assign(v.variable(variables[0]), i)
+                       i = v.send(v.get_property("item", it.mtype), [it])
+                       assert i != null
+                       v.assign(v.variable(variables[1]), i)
+               else
+                       abort
+               end
                v.stmt(self.n_block)
-               v.add("CONTINUE_{escapemark.object_id}: (void)0;")
+               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
                v.send(v.get_property("next", it.mtype), [it])
                v.add("\}")
-               v.add("BREAK_{escapemark.object_id}: (void)0;")
+               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
        end
 end
 
@@ -2170,6 +2303,7 @@ redef class ASuperstringExpr
        do
                var array = new Array[RuntimeVariable]
                for ne in self.n_exprs do
+                       if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
                        var i = v.expr(ne, null)
                        array.add(i)
                end
@@ -2289,7 +2423,7 @@ redef class ASendExpr
        do
                var recv = v.expr(self.n_expr, null)
                var args = [recv]
-               for a in compute_raw_arguments do
+               for a in self.raw_arguments.as(not null) do
                        args.add(v.expr(a, null))
                end
                var mproperty = self.mproperty.as(not null)
@@ -2302,7 +2436,7 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr, null)
                var args = [recv]
-               for a in compute_raw_arguments do
+               for a in self.raw_arguments.as(not null) do
                        args.add(v.expr(a, null))
                end
                var value = v.expr(self.n_value, null)
@@ -2346,6 +2480,7 @@ redef class ASuperExpr
                # FIXME: we do not want an ugly static call!
                var mpropdefs = mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype)
                if mpropdefs.length != 1 then
+                       v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
                        debug("MPRODFEFS for super {mpropdef} for {recv}: {mpropdefs.join(", ")}")
                end
                mpropdef = mpropdefs.first