nitg & lib: intro `Finalizable` to be called when an object is freed
[nit.git] / src / global_compiler.nit
index 13ed9d2..3f8b422 100644 (file)
@@ -26,6 +26,29 @@ module global_compiler
 import abstract_compiler
 import rapid_type_analysis
 
+redef class ToolContext
+       # option --global
+       var opt_global = new OptionBool("Use global compilation", "--global")
+
+       var global_compiler_phase = new GlobalCompilerPhase(self, null)
+
+       redef init do
+               super
+               option_context.add_option(opt_global)
+       end
+end
+
+class GlobalCompilerPhase
+       super Phase
+       redef fun process_mainmodule(mainmodule, given_mmodules) do
+               if not toolcontext.opt_global.value then return
+
+               var modelbuilder = toolcontext.modelbuilder
+               var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
+               modelbuilder.run_global_compiler(mainmodule, analysis)
+       end
+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
@@ -33,14 +56,13 @@ redef class ModelBuilder
        fun run_global_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis)
        do
                var time0 = get_time
-               self.toolcontext.info("*** COMPILING TO C ***", 1)
+               self.toolcontext.info("*** GENERATING C ***", 1)
 
                var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis)
                compiler.compile_header
-               var v = compiler.header
 
                for t in runtime_type_analysis.live_types do
-                       compiler.declare_runtimeclass(v, t)
+                       compiler.declare_runtimeclass(t)
                end
 
                compiler.compile_class_names
@@ -49,13 +71,13 @@ redef class ModelBuilder
                for t in runtime_type_analysis.live_types do
                        if t.ctype == "val*" then
                                compiler.generate_init_instance(t)
-                               compiler.generate_check_init_instance(t)
                        else
                                compiler.generate_box_instance(t)
                        end
                end
 
                # The main function of the C
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
 
                # Compile until all runtime_functions are visited
@@ -64,12 +86,12 @@ redef class ModelBuilder
                        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.writers.length}", 2)
+               self.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2)
 
                compiler.display_stats
 
                var time1 = get_time
-               self.toolcontext.info("*** END VISITING: {time1-time0} ***", 2)
+               self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
                write_and_make(compiler)
        end
 end
@@ -89,6 +111,8 @@ class GlobalCompiler
        init(mainmodule: MModule, modelbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis)
        do
                super(mainmodule, modelbuilder)
+               var file = new_file("{mainmodule.name}.nitgg")
+               self.header = new CodeWriter(file)
                self.runtime_type_analysis = runtime_type_analysis
                self.live_primitive_types = new Array[MClassType]
                for t in runtime_type_analysis.live_types do
@@ -100,12 +124,13 @@ class GlobalCompiler
 
        # Compile class names (for the class_name and output_class_name methods)
        protected fun compile_class_names do
-               self.header.add_decl("extern const char const * class_names[];")
-               self.header.add("const char const * class_names[] = \{")
+               var v = new_visitor
+               self.header.add_decl("extern const char *class_names[];")
+               v.add("const char *class_names[] = \{")
                for t in self.runtime_type_analysis.live_types do
-                       self.header.add("\"{t}\", /* {self.classid(t)} */")
+                       v.add("\"{t}\", /* {self.classid(t)} */")
                end
-               self.header.add("\};")
+               v.add("\};")
        end
 
        # Return the C symbol associated to a live type runtime
@@ -123,9 +148,9 @@ class GlobalCompiler
        protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
 
        # Declaration of structures the live Nit types
-       # Each live type is generated as an independent C `struct' type.
+       # 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.
+       # Usualy, all C variables that refers to a Nit object are typed on the abstract struct `val` that contains only the `classid` field.
        redef fun compile_header_structs do
                self.header.add_decl("typedef struct \{int classid;\} val; /* general C type representing a Nit instance. */")
        end
@@ -149,8 +174,9 @@ class GlobalCompiler
        private var seen: HashSet[AbstractRuntimeFunction] = new HashSet[AbstractRuntimeFunction]
 
        # Declare C structures and identifiers for a runtime class
-       fun declare_runtimeclass(v: CodeWriter, mtype: MClassType)
+       fun declare_runtimeclass(mtype: MClassType)
        do
+               var v = self.header
                assert self.runtime_type_analysis.live_types.has(mtype)
                v.add_decl("/* runtime class {mtype} */")
                var idnum = classids.length
@@ -163,6 +189,7 @@ class GlobalCompiler
 
                if mtype.mclass.name == "NativeArray" then
                        # NativeArrays are just a instance header followed by an array of values
+                       v.add_decl("int length;")
                        v.add_decl("{mtype.arguments.first.ctype} values[1];")
                end
 
@@ -209,30 +236,19 @@ class GlobalCompiler
                res.is_exact = true
                if is_native_array then
                        var mtype_elt = mtype.arguments.first
-                       v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));")
+                       v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));")
+                       v.add("((struct {mtype.c_name}*){res})->length = length;")
                else
-                       v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));")
+                       v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}));")
                end
                v.add("{res}->classid = {self.classid(mtype)};")
 
                self.generate_init_attr(v, res, mtype)
+               v.set_finalizer res
                v.add("return {res};")
                v.add("\}")
        end
 
-       redef fun generate_check_init_instance(mtype)
-       do
-               if self.modelbuilder.toolcontext.opt_no_check_initialization.value then return
-
-               var v = self.new_visitor
-               var res = new RuntimeVariable("self", mtype, mtype)
-               self.header.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype});")
-               v.add_decl("/* allocate {mtype} */")
-               v.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype} {res}) \{")
-               self.generate_check_attr(v, res, mtype)
-               v.add("\}")
-       end
-
        fun generate_box_instance(mtype: MClassType)
        do
                assert self.runtime_type_analysis.live_types.has(mtype)
@@ -242,17 +258,28 @@ class GlobalCompiler
                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("struct {mtype.c_name}*res = nit_alloc(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
 
        redef fun new_visitor do return new GlobalCompilerVisitor(self)
 
        private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
+
+       redef fun compile_nitni_structs
+       do
+               self.header.add_decl """
+struct nitni_instance \{
+       struct nitni_instance *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+       val *value;
+\};"""
+               super
+       end
 end
 
 # A visitor on the AST of property definition that generate the C code.
@@ -275,16 +302,18 @@ class GlobalCompilerVisitor
                        var res = self.new_var(mtype)
                        if not compiler.runtime_type_analysis.live_types.has(valtype) then
                                self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
-                               self.add("printf(\"Dead code executed!\\n\"); exit(1);")
+                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
                                return res
                        end
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
+               else if value.mtype.ctype == "void*" and mtype.ctype == "void*" then
+                       return value
                else
                        # 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);")
+                       self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
                        return res
                end
        end
@@ -323,6 +352,9 @@ class GlobalCompilerVisitor
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
                        return
+               else if pname == "length" then
+                       self.ret(self.new_expr("((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->length", ret_type.as(not null)))
+                       return
                else if pname == "copy_to" then
                        var recv1 = "((struct {arguments[1].mcasttype.c_name}*){arguments[1]})->values"
                        self.add("memcpy({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));")
@@ -330,6 +362,12 @@ class GlobalCompilerVisitor
                end
        end
 
+       redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
+       do
+               var ret_type = self.get_class("NativeArray").get_mtype([elttype])
+               return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type)
+       end
+
        redef fun calloc_array(ret_type, arguments)
        do
                self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
@@ -386,7 +424,7 @@ class GlobalCompilerVisitor
                                        self.add("{res} = 1; /* {args[1].inspect} cannot be null */")
                                end
                        else
-                               self.add_abort("Reciever is null")
+                               self.add_abort("Receiver is null")
                        end
                        self.add "\} else"
                end
@@ -435,29 +473,56 @@ class GlobalCompilerVisitor
                abort
        end
 
-       redef fun call(m, recvtype, args)
+       # Subpart of old call function
+       #
+       # Checks if the type of the receiver is valid and corrects it if necessary
+       private fun get_recvtype(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): MClassType
        do
                check_valid_reciever(recvtype)
                #debug("call {m} on {recvtype} on {args.first}:{args.first.mtype}")
                if m.mclassdef.mclass.name == "Object" and recvtype.ctype == "val*" then
                        recvtype = m.mclassdef.bound_mtype
                end
-               var recv = self.autobox(args.first, recvtype)
-               recv = self.autoadapt(recv, recvtype)
+               return recvtype
+       end
 
-               args = args.to_a
-               self.varargize(m, m.msignature.as(not null), args)
-               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
+       # Subpart of old call function
+       # Gets the receiver boxed and casted if necessary
+       private fun get_recv(recvtype: MClassType, args: Array[RuntimeVariable]): RuntimeVariable
+       do
+               return self.autoadapt(self.autobox(args.first, recvtype), recvtype)
+       end
+
+       # Finalizes a call to a method ´m´ on type ´recvtype´ with arguments ´args´
+       private fun finalize_call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               assert args.length == m.msignature.arity + 1 else debug("Invalid arity for {m}. {args.length} arguments given.")
 
-               args.first = recv
                var rm = new CustomizedRuntimeFunction(m, recvtype)
                return rm.call(self, args)
        end
 
+       redef fun call(m, recvtype, args)
+       do
+               var recv_type = get_recvtype(m, recvtype, args)
+               var recv = get_recv(recv_type, args)
+               var new_args = args.to_a
+               self.varargize(m, m.msignature.as(not null), new_args)
+               new_args.first = recv
+               return finalize_call(m, recv_type, new_args)
+       end
+
+       # Does a call without encapsulating varargs into an array
+       # Avoids multiple encapsulation when calling a super in a variadic function
+       fun call_without_varargize(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               var recv_type = get_recvtype(m, recvtype, args)
+               var recv = get_recv(recv_type, args)
+               var new_args = args.to_a
+               new_args.first = recv
+               return finalize_call(m, recv_type, new_args)
+       end
+
        redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var types = self.collect_types(args.first)
@@ -479,7 +544,7 @@ class GlobalCompilerVisitor
                                return res
                        end
                        var propdef = m.lookup_next_definition(self.compiler.mainmodule, mclasstype)
-                       var res2 = self.call(propdef, mclasstype, args)
+                       var res2 = self.call_without_varargize(propdef, mclasstype, args)
                        if res != null then self.assign(res, res2.as(not null))
                        return res
                end
@@ -501,7 +566,7 @@ class GlobalCompilerVisitor
                        else
                                self.add("case {self.compiler.classid(t)}: /* test {t} */")
                        end
-                       var res2 = self.call(propdef, t, args)
+                       var res2 = self.call_without_varargize(propdef, t, args)
                        if res != null then self.assign(res, res2.as(not null))
                        self.add "break;"
                end
@@ -536,8 +601,8 @@ class GlobalCompilerVisitor
        fun bugtype(recv: RuntimeVariable)
        do
                if recv.mtype.ctype != "val*" then return
-               self.add("fprintf(stderr, \"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
-               self.add("exit(1);")
+               self.add("PRINT_ERROR(\"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
+               self.add("show_backtrace(1);")
        end
 
        redef fun isset_attribute(a, recv)
@@ -679,10 +744,7 @@ class GlobalCompilerVisitor
        redef fun type_test(value, mtype, tag)
        do
                mtype = self.anchor(mtype)
-               var mclasstype = mtype
-               if mtype isa MNullableType then mclasstype = mtype.mtype
-               assert mclasstype isa MClassType
-               if not self.compiler.runtime_type_analysis.live_cast_types.has(mclasstype) then
+               if not self.compiler.runtime_type_analysis.live_cast_types.has(mtype) then
                        debug "problem: {mtype} was detected cast-dead"
                        abort
                end
@@ -792,18 +854,6 @@ class GlobalCompilerVisitor
                return res
        end
 
-       redef fun check_init_instance(recv, mtype)
-       do
-               if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return
-
-               mtype = self.anchor(mtype).as(MClassType)
-               if not self.compiler.runtime_type_analysis.live_types.has(mtype) then
-                       debug "problem: {mtype} was detected dead"
-               end
-
-               self.add("CHECK_NEW_{mtype.c_name}({recv});")
-       end
-
        redef fun array_instance(array, elttype)
        do
                elttype = self.anchor(elttype)
@@ -819,7 +869,6 @@ class GlobalCompilerVisitor
                end
                var length = self.int_instance(array.length)
                self.send(self.get_property("with_native", arraytype), [res, nat, length])
-               self.check_init_instance(res, arraytype)
                self.add("\}")
                return res
        end
@@ -895,8 +944,8 @@ private class CustomizedRuntimeFunction
                var frame = new Frame(v, mmethoddef, recv, arguments)
                v.frame = frame
 
-               var sig = new Buffer
-               var comment = new Buffer
+               var sig = new FlatBuffer
+               var comment = new FlatBuffer
                var ret = mmethoddef.msignature.return_mtype
                if ret != null then
                        ret = v.resolve_for(ret, selfvar)
@@ -944,6 +993,7 @@ private class CustomizedRuntimeFunction
                        v.add("return {frame.returnvar.as(not null)};")
                end
                v.add("\}")
+               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
 
        redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable