nitgs: add --direct-call-monomorph and --inline-some-methods
[nit.git] / src / separate_compiler.nit
index a683a35..cd5692f 100644 (file)
@@ -18,7 +18,6 @@ module separate_compiler
 import abstract_compiler
 import layout_builders
 import rapid_type_analysis
-import collect_super_sends
 import compiler_ffi
 
 # Add separate compiler specific options
@@ -32,7 +31,11 @@ redef class ToolContext
        # --no-shortcut-equate
        var opt_no_shortcut_equate: OptionBool = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
        # --inline-coloring-numbers
-       var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids", "--inline-coloring-numbers")
+       var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
+       # --inline-some-methods
+       var opt_inline_some_methods: OptionBool = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
+       # --direct-call-monomorph
+       var opt_direct_call_monomorph: OptionBool = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
        # --use-naive-coloring
        var opt_bm_typing: OptionBool = new OptionBool("Colorize items incrementaly, used to simulate binary matrix typing", "--bm-typing")
        # --use-mod-perfect-hashing
@@ -49,7 +52,7 @@ redef class ToolContext
                self.option_context.add_option(self.opt_no_inline_intern)
                self.option_context.add_option(self.opt_no_union_attribute)
                self.option_context.add_option(self.opt_no_shortcut_equate)
-               self.option_context.add_option(self.opt_inline_coloring_numbers)
+               self.option_context.add_option(self.opt_inline_coloring_numbers, opt_inline_some_methods, opt_direct_call_monomorph)
                self.option_context.add_option(self.opt_bm_typing)
                self.option_context.add_option(self.opt_phmod_typing)
                self.option_context.add_option(self.opt_phand_typing)
@@ -95,6 +98,11 @@ redef class ModelBuilder
                for t in mtypes do
                        compiler.compile_type_to_c(t)
                end
+               # compile remaining types structures (useless but needed for the symbol resolution at link-time)
+               for t in compiler.undead_types do
+                       if mtypes.has(t) then continue
+                       compiler.compile_type_to_c(t)
+               end
 
                compiler.display_stats
 
@@ -102,6 +110,13 @@ redef class ModelBuilder
                self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
                write_and_make(compiler)
        end
+
+       # Count number of invocations by VFT
+       private var nb_invok_by_tables = 0
+       # Count number of invocations by direct call
+       private var nb_invok_by_direct = 0
+       # Count number of invocations by inlining
+       private var nb_invok_by_inline = 0
 end
 
 # Singleton that store the knowledge about the separate compilation process
@@ -114,7 +129,6 @@ class SeparateCompiler
        var runtime_type_analysis: nullable RapidTypeAnalysis
 
        private var undead_types: Set[MType] = new HashSet[MType]
-       private var partial_types: Set[MType] = new HashSet[MType]
        private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
 
        private var type_layout: nullable Layout[MType]
@@ -271,13 +285,27 @@ class SeparateCompiler
                        end
                end
 
+               # Collect all super calls (dead or not)
+               var all_super_calls = new HashSet[MMethodDef]
+               for mmodule in self.mainmodule.in_importation.greaters do
+                       for mclassdef in mmodule.mclassdefs do
+                               for mpropdef in mclassdef.mpropdefs do
+                                       if not mpropdef isa MMethodDef then continue
+                                       if mpropdef.has_supercall then
+                                               all_super_calls.add(mpropdef)
+                                       end
+                               end
+                       end
+               end
+
                # lookup super calls and add it to the list of mmethods to build layout with
                var super_calls
                if runtime_type_analysis != null then
                        super_calls = runtime_type_analysis.live_super_sends
                else
-                       super_calls = modelbuilder.collect_super_sends
+                       super_calls = all_super_calls
                end
+
                for mmethoddef in super_calls do
                        var mclass = mmethoddef.mclassdef.mclass
                        mmethods[mclass].add(mmethoddef)
@@ -292,14 +320,9 @@ class SeparateCompiler
                self.compile_color_consts(method_layout.pos)
 
                # attribute null color to dead supercalls
-               for mmodule in self.mainmodule.in_importation.greaters do
-                       for mclassdef in mmodule.mclassdefs do
-                               for mpropdef in mclassdef.mpropdefs do
-                                       if mpropdef.has_supercall then
-                                               compile_color_const(new_visitor, mpropdef, -1)
-                                       end
-                               end
-                       end
+               for mpropdef in all_super_calls do
+                       if super_calls.has(mpropdef) then continue
+                       compile_color_const(new_visitor, mpropdef, -1)
                end
 
                # attributes coloration
@@ -443,16 +466,10 @@ class SeparateCompiler
                var mtypes = new HashSet[MType]
                mtypes.add_all(self.runtime_type_analysis.live_types)
                mtypes.add_all(self.runtime_type_analysis.live_cast_types)
-               mtypes.add_all(self.undead_types)
                for c in self.box_kinds.keys do
                        mtypes.add(c.mclass_type)
                end
 
-               for mtype in mtypes do
-                       retrieve_partial_types(mtype)
-               end
-               mtypes.add_all(self.partial_types)
-
                # Typing Layout
                var layout_builder: TypingLayoutBuilder[MType]
                if modelbuilder.toolcontext.opt_bm_typing.value then
@@ -581,34 +598,6 @@ class SeparateCompiler
                return tables
        end
 
-       fun retrieve_partial_types(mtype: MType) do
-               # add formal types arguments to mtypes
-               if mtype isa MGenericType then
-                       for ft in mtype.arguments do
-                               if ft.need_anchor then
-                                       print("Why do we need anchor here ?")
-                                       abort
-                               end
-                               self.partial_types.add(ft)
-                               retrieve_partial_types(ft)
-                       end
-               end
-               var mclass_type: MClassType
-               if mtype isa MNullableType then
-                       mclass_type = mtype.mtype.as(MClassType)
-               else
-                       mclass_type = mtype.as(MClassType)
-               end
-
-               # add virtual types to mtypes
-               for vt in self.mainmodule.properties(mclass_type.mclass) do
-                       if vt isa MVirtualTypeProp then
-                               var anchored = vt.mvirtualtype.lookup_bound(self.mainmodule, mclass_type).anchor_to(self.mainmodule, mclass_type)
-                               self.partial_types.add(anchored)
-                       end
-               end
-       end
-
        # Separately compile all the method definitions of the module
        fun compile_module_to_c(mmodule: MModule)
        do
@@ -757,6 +746,7 @@ class SeparateCompiler
        do
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
+               var c_instance_name = mclass.c_instance_name
 
                var vft = self.method_tables[mclass]
                var attrs = self.attr_tables[mclass]
@@ -788,20 +778,22 @@ class SeparateCompiler
                end
 
                if mtype.ctype != "val*" then
-                       #Build instance struct
-                       self.header.add_decl("struct instance_{c_name} \{")
-                       self.header.add_decl("const struct type *type;")
-                       self.header.add_decl("const struct class *class;")
-                       self.header.add_decl("{mtype.ctype} value;")
-                       self.header.add_decl("\};")
+                       if mtype.mclass.name == "Pointer" or mtype.mclass.kind != extern_kind then
+                               #Build instance struct
+                               self.header.add_decl("struct instance_{c_instance_name} \{")
+                               self.header.add_decl("const struct type *type;")
+                               self.header.add_decl("const struct class *class;")
+                               self.header.add_decl("{mtype.ctype} value;")
+                               self.header.add_decl("\};")
+                       end
 
                        if not self.runtime_type_analysis.live_types.has(mtype) then return
 
                        #Build BOX
-                       self.header.add_decl("val* BOX_{c_name}({mtype.ctype});")
+                       self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype});")
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
-                       v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
+                       v.add("struct instance_{c_instance_name}*res = nit_alloc(sizeof(struct instance_{c_instance_name}));")
                        v.require_declaration("type_{c_name}")
                        v.add("res->type = &type_{c_name};")
                        v.require_declaration("class_{c_name}")
@@ -812,7 +804,7 @@ class SeparateCompiler
                        return
                else if mclass.name == "NativeArray" then
                        #Build instance struct
-                       self.header.add_decl("struct instance_{c_name} \{")
+                       self.header.add_decl("struct instance_{c_instance_name} \{")
                        self.header.add_decl("const struct type *type;")
                        self.header.add_decl("const struct class *class;")
                        # NativeArrays are just a instance header followed by an array of values
@@ -826,7 +818,7 @@ class SeparateCompiler
                        var res = v.new_named_var(mtype, "self")
                        res.is_exact = true
                        var mtype_elt = mtype.arguments.first
-                       v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
+                       v.add("{res} = nit_alloc(sizeof(struct instance_{c_instance_name}) + length*sizeof({mtype_elt.ctype}));")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
@@ -884,6 +876,14 @@ class SeparateCompiler
                if self.modelbuilder.toolcontext.opt_tables_metrics.value then
                        display_sizes
                end
+
+               var tc = self.modelbuilder.toolcontext
+               tc.info("# implementation of method invocation",2)
+               var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
+               tc.info("total number of invocations: {nb_invok_total}",2)
+               tc.info("invocations by VFT send:     {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
+               tc.info("invocations by direct call:  {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
+               tc.info("invocations by inlining:     {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
        end
 
        fun display_sizes
@@ -936,8 +936,10 @@ class SeparateCompiler
        
        redef fun finalize_ffi_for_module(nmodule)
        do
+               var old_module = self.mainmodule
                self.mainmodule = nmodule.mmodule.as(not null)
                super
+               self.mainmodule = old_module
        end
 end
 
@@ -970,7 +972,7 @@ class SeparateCompilerVisitor
                else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
                        return value
                else if value.mtype.ctype == "val*" then
-                       return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
+                       return self.new_expr("((struct instance_{mtype.c_instance_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
                else if mtype.ctype == "val*" then
                        var valtype = value.mtype.as(MClassType)
                        var res = self.new_var(mtype)
@@ -979,6 +981,7 @@ class SeparateCompilerVisitor
                                self.add("printf(\"Dead code executed!\\n\"); show_backtrace(1);")
                                return res
                        end
+                       self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
                else if value.mtype.cname_blind == "void*" and mtype.cname_blind == "void*" then
@@ -999,11 +1002,27 @@ class SeparateCompilerVisitor
                if value.mtype.ctype == "val*" then
                        return "{value}->type"
                else
+                       compiler.undead_types.add(value.mtype)
                        self.require_declaration("type_{value.mtype.c_name}")
                        return "(&type_{value.mtype.c_name})"
                end
        end
 
+       redef fun compile_callsite(callsite, args)
+       do
+               var rta = compiler.runtime_type_analysis
+               var recv = args.first.mtype
+               if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and recv isa MClassType then
+                       var tgs = rta.live_targets(callsite)
+                       if tgs.length == 1 then
+                               # DIRECT CALL
+                               var mmethod = callsite.mproperty
+                               self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
+                               return call(tgs.first, recv, args)
+                       end
+               end
+               return super
+       end
        redef fun send(mmethod, arguments)
        do
                self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
@@ -1023,6 +1042,10 @@ class SeparateCompilerVisitor
 
        private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
        do
+               compiler.modelbuilder.nb_invok_by_tables += 1
+
+               assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
+
                var res: nullable RuntimeVariable
                var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true)
                var ret = msignature.return_mtype
@@ -1077,7 +1100,7 @@ class SeparateCompilerVisitor
                                        self.add("{res} = 1; /* {arg.inspect} cannot be null */")
                                end
                        else
-                               self.add_abort("Reciever is null")
+                               self.add_abort("Receiver is null")
                        end
                        self.add("\} else \{")
                end
@@ -1118,6 +1141,8 @@ class SeparateCompilerVisitor
 
        redef fun call(mmethoddef, recvtype, arguments)
        do
+               assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
+
                var res: nullable RuntimeVariable
                var ret = mmethoddef.msignature.return_mtype
                if mmethoddef.mproperty.is_new then
@@ -1130,28 +1155,29 @@ class SeparateCompilerVisitor
                        res = self.new_var(ret)
                end
 
-               if self.compiler.modelbuilder.mpropdef2npropdef.has_key(mmethoddef) and
-               self.compiler.modelbuilder.mpropdef2npropdef[mmethoddef] isa AInternMethPropdef and
-               not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value then
+               if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
+                       (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
+                       compiler.modelbuilder.nb_invok_by_inline += 1
                        var frame = new Frame(self, mmethoddef, recvtype, arguments)
                        frame.returnlabel = self.get_name("RET_LABEL")
                        frame.returnvar = res
                        var old_frame = self.frame
                        self.frame = frame
-                       self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) */")
+                       self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
                        mmethoddef.compile_inside_to_c(self, arguments)
                        self.add("{frame.returnlabel.as(not null)}:(void)0;")
                        self.add("\}")
                        self.frame = old_frame
                        return res
                end
+               compiler.modelbuilder.nb_invok_by_direct += 1
 
                # Autobox arguments
                self.adapt_signature(mmethoddef, arguments)
 
                self.require_declaration(mmethoddef.c_name)
                if res == null then
-                       self.add("{mmethoddef.c_name}({arguments.join(", ")});")
+                       self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
                        return null
                else
                        self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
@@ -1242,7 +1268,7 @@ class SeparateCompilerVisitor
 
                        # Check for Uninitialized attribute
                        if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
-                               self.add("if ({res} == NULL) \{")
+                               self.add("if (unlikely({res} == NULL)) \{")
                                self.add_abort("Uninitialized attribute {a.name}")
                                self.add("\}")
                        end
@@ -1256,7 +1282,7 @@ class SeparateCompilerVisitor
 
                        # Check for Uninitialized attribute
                        if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
-                               self.add("if ({res} == NULL) \{")
+                               self.add("if (unlikely({res} == NULL)) \{")
                                self.add_abort("Uninitialized attribute {a.name}")
                                self.add("\}")
                        end
@@ -1285,7 +1311,7 @@ class SeparateCompilerVisitor
                                # The attribute is primitive, thus we store it in a box
                                # The trick is to create the box the first time then resuse the box
                                self.add("if ({attr} != NULL) \{")
-                               self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
+                               self.add("((struct instance_{mtype.c_instance_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
                                self.add("\} else \{")
                                value = self.autobox(value, self.object_type.as_nullable)
                                self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
@@ -1553,12 +1579,12 @@ class SeparateCompilerVisitor
                        end
                end
                if primitive != null then
-                       test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
+                       test.add("((struct instance_{primitive.c_instance_name}*){value1})->value == ((struct instance_{primitive.c_instance_name}*){value2})->value")
                else if can_be_primitive(value1) and can_be_primitive(value2) then
                        test.add("{value1}->class == {value2}->class")
                        var s = new Array[String]
                        for t, v in self.compiler.box_kinds do
-                               s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
+                               s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_instance_name}*){value1})->value == ((struct instance_{t.c_instance_name}*){value2})->value)"
                        end
                        test.add("({s.join(" || ")})")
                else
@@ -1629,7 +1655,7 @@ class SeparateCompilerVisitor
        do
                var elttype = arguments.first.mtype
                var nclass = self.get_class("NativeArray")
-               var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
+               var recv = "((struct instance_{nclass.c_instance_name}*){arguments[0]})->values"
                if pname == "[]" then
                        self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
                        return
@@ -1637,7 +1663,7 @@ class SeparateCompilerVisitor
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
                        return
                else if pname == "copy_to" then
-                       var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
+                       var recv1 = "((struct instance_{nclass.c_instance_name}*){arguments[1]})->values"
                        self.add("memcpy({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
                        return
                end
@@ -1842,6 +1868,23 @@ end
 
 redef class MType
        fun const_color: String do return "COLOR_{c_name}"
+
+       # C name of the instance type to use
+       fun c_instance_name: String do return c_name
+end
+
+redef class MClassType
+       redef fun c_instance_name do return mclass.c_instance_name
+end
+
+redef class MClass
+       # Extern classes use the C instance of kernel::Pointer
+       fun c_instance_name: String
+       do
+               if kind == extern_kind then
+                       return "kernel__Pointer"
+               else return c_name
+       end
 end
 
 redef class MProperty