sepcomp: shortcut an inline most intern methods of Object
[nit.git] / src / compiler / separate_compiler.nit
index 3645f54..a33c098 100644 (file)
@@ -29,12 +29,26 @@ redef class ToolContext
        var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
        # --no-shortcut-equate
        var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
+
+       # --colors-are-symbols
+       var opt_colors_are_symbols = new OptionBool("Store colors as symbols (link-boost)", "--colors-are-symbols")
+       # --trampoline-call
+       var opt_trampoline_call = new OptionBool("Use an indirection when calling", "--trampoline-call")
+       # --guard-call
+       var opt_guard_call = new OptionBool("Guard VFT calls with a direct call", "--guard-call")
+       # --substitute-monomorph
+       var opt_substitute_monomorph = new OptionBool("Replace monomorph trampoline with direct call (link-boost)", "--substitute-monomorph")
+       # --link-boost
+       var opt_link_boost = new OptionBool("Enable all link-boost optimizations", "--link-boost")
+
        # --inline-coloring-numbers
        var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
        # --inline-some-methods
        var opt_inline_some_methods = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
        # --direct-call-monomorph
        var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
+       # --direct-call-monomorph0
+       var opt_direct_call_monomorph0 = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph0")
        # --skip-dead-methods
        var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
        # --semi-global
@@ -51,6 +65,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(opt_colors_are_symbols, opt_trampoline_call, opt_guard_call, opt_direct_call_monomorph0, opt_substitute_monomorph, opt_link_boost)
                self.option_context.add_option(self.opt_inline_coloring_numbers, opt_inline_some_methods, opt_direct_call_monomorph, opt_skip_dead_methods, opt_semi_global)
                self.option_context.add_option(self.opt_colo_dead_methods)
                self.option_context.add_option(self.opt_tables_metrics)
@@ -67,6 +82,13 @@ redef class ToolContext
                        tc.opt_direct_call_monomorph.value = true
                        tc.opt_skip_dead_methods.value = true
                end
+               if tc.opt_link_boost.value then
+                       tc.opt_colors_are_symbols.value = true
+                       tc.opt_substitute_monomorph.value = true
+               end
+               if tc.opt_substitute_monomorph.value then
+                       tc.opt_trampoline_call.value = true
+               end
        end
 
        var separate_compiler_phase = new SeparateCompilerPhase(self, null)
@@ -90,45 +112,7 @@ redef class ModelBuilder
                self.toolcontext.info("*** GENERATING C ***", 1)
 
                var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis)
-               compiler.compile_header
-
-               # compile class structures
-               self.toolcontext.info("Property coloring", 2)
-               compiler.new_file("{mainmodule.name}.classes")
-               compiler.do_property_coloring
-               for m in mainmodule.in_importation.greaters do
-                       for mclass in m.intro_mclasses do
-                               if mclass.kind == abstract_kind or mclass.kind == interface_kind then continue
-                               compiler.compile_class_to_c(mclass)
-                       end
-               end
-
-               # The main function of the C
-               compiler.new_file("{mainmodule.name}.main")
-               compiler.compile_nitni_global_ref_functions
-               compiler.compile_main_function
-               compiler.compile_finalizer_function
-
-               # compile methods
-               for m in mainmodule.in_importation.greaters do
-                       self.toolcontext.info("Generate C for module {m}", 2)
-                       compiler.new_file("{m.name}.sep")
-                       compiler.compile_module_to_c(m)
-               end
-
-               # compile live & cast type structures
-               self.toolcontext.info("Type coloring", 2)
-               compiler.new_file("{mainmodule.name}.types")
-               var mtypes = compiler.do_type_coloring
-               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.do_compilation
                compiler.display_stats
 
                var time1 = get_time
@@ -156,20 +140,73 @@ class SeparateCompiler
        private var undead_types: Set[MType] = new HashSet[MType]
        private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
 
-       private var type_ids: Map[MType, Int]
-       private var type_colors: Map[MType, Int]
-       private var opentype_colors: Map[MType, Int]
-       protected var method_colors: Map[PropertyLayoutElement, Int]
-       protected var attr_colors: Map[MAttribute, Int]
+       private var type_ids: Map[MType, Int] is noinit
+       private var type_colors: Map[MType, Int] is noinit
+       private var opentype_colors: Map[MType, Int] is noinit
+       protected var method_colors: Map[PropertyLayoutElement, Int] is noinit
+       protected var attr_colors: Map[MAttribute, Int] is noinit
 
-       init(mainmodule: MModule, mmbuilder: ModelBuilder, runtime_type_analysis: nullable RapidTypeAnalysis) do
-               super(mainmodule, mmbuilder)
+       init do
                var file = new_file("nit.common")
                self.header = new CodeWriter(file)
-               self.runtime_type_analysis = runtime_type_analysis
                self.compile_box_kinds
        end
 
+       redef fun do_compilation
+       do
+               var compiler = self
+               compiler.compile_header
+
+               var c_name = mainmodule.c_name
+
+               # compile class structures
+               modelbuilder.toolcontext.info("Property coloring", 2)
+               compiler.new_file("{c_name}.classes")
+               compiler.do_property_coloring
+               for m in mainmodule.in_importation.greaters do
+                       for mclass in m.intro_mclasses do
+                               #if mclass.kind == abstract_kind or mclass.kind == interface_kind then continue
+                               compiler.compile_class_to_c(mclass)
+                       end
+               end
+
+               # The main function of the C
+               compiler.new_file("{c_name}.main")
+               compiler.compile_nitni_global_ref_functions
+               compiler.compile_main_function
+               compiler.compile_finalizer_function
+               compiler.link_mmethods
+
+               # compile methods
+               for m in mainmodule.in_importation.greaters do
+                       modelbuilder.toolcontext.info("Generate C for module {m.full_name}", 2)
+                       compiler.new_file("{m.c_name}.sep")
+                       compiler.compile_module_to_c(m)
+               end
+
+               # compile live & cast type structures
+               modelbuilder.toolcontext.info("Type coloring", 2)
+               compiler.new_file("{c_name}.types")
+               compiler.compile_types
+       end
+
+       # Color and compile type structures and cast information
+       fun compile_types
+       do
+               var compiler = self
+
+               var mtypes = compiler.do_type_coloring
+               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
+
+       end
+
        redef fun compile_header_structs do
                self.header.add_decl("typedef void(*nitmethod_t)(void); /* general C type representing a Nit method. */")
                self.compile_header_attribute_structs
@@ -238,27 +275,21 @@ class SeparateCompiler
 
        fun compile_color_const(v: SeparateCompilerVisitor, m: Object, color: Int) do
                if color_consts_done.has(m) then return
-               if m isa MProperty then
-                       if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
-                               self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
-                       else
-                               self.provide_declaration(m.const_color, "extern const int {m.const_color};")
-                               v.add("const int {m.const_color} = {color};")
-                       end
-               else if m isa MPropDef then
+               if m isa MEntity then
                        if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
                                self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
-                       else
+                       else if not modelbuilder.toolcontext.opt_colors_are_symbols.value or not v.compiler.target_platform.supports_linker_script then
                                self.provide_declaration(m.const_color, "extern const int {m.const_color};")
                                v.add("const int {m.const_color} = {color};")
-                       end
-               else if m isa MType then
-                       if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
-                               self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
                        else
-                               self.provide_declaration(m.const_color, "extern const int {m.const_color};")
-                               v.add("const int {m.const_color} = {color};")
+                               # The color 'C' is the ``address'' of a false static variable 'XC'
+                               self.provide_declaration(m.const_color, "#define {m.const_color} ((long)&X{m.const_color})\nextern const void X{m.const_color};")
+                               if color == -1 then color = 0 # Symbols cannot be negative, so just use 0 for dead things
+                               # Teach the linker that the address of 'XC' is `color`.
+                               linker_script.add("X{m.const_color} = {color};")
                        end
+               else
+                       abort
                end
                color_consts_done.add(m)
        end
@@ -418,13 +449,12 @@ class SeparateCompiler
                var live_cast_types = runtime_type_analysis.live_cast_types
                var mtypes = new HashSet[MType]
                mtypes.add_all(live_types)
-               mtypes.add_all(live_cast_types)
                for c in self.box_kinds.keys do
                        mtypes.add(c.mclass_type)
                end
 
                # Compute colors
-               var poset = poset_from_mtypes(mtypes)
+               var poset = poset_from_mtypes(mtypes, live_cast_types)
                var colorer = new POSetColorer[MType]
                colorer.colorize(poset)
                type_ids = colorer.ids
@@ -437,12 +467,13 @@ class SeparateCompiler
                return poset
        end
 
-       private fun poset_from_mtypes(mtypes: Set[MType]): POSet[MType] do
+       private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
                var poset = new POSet[MType]
                for e in mtypes do
                        poset.add_node(e)
-                       for o in mtypes do
+                       for o in cast_types do
                                if e == o then continue
+                               poset.add_node(o)
                                if e.is_subtype(mainmodule, null, o) then
                                        poset.add_edge(e, o)
                                end
@@ -549,12 +580,75 @@ class SeparateCompiler
                                var r = pd.separate_runtime_function
                                r.compile_to_c(self)
                                var r2 = pd.virtual_runtime_function
-                               r2.compile_to_c(self)
+                               if r2 != r then r2.compile_to_c(self)
+
+                               # Generate trampolines
+                               if modelbuilder.toolcontext.opt_trampoline_call.value then
+                                       r2.compile_trampolines(self)
+                               end
                        end
                end
                self.mainmodule = old_module
        end
 
+       # Process all introduced methods and compile some linking information (if needed)
+       fun link_mmethods
+       do
+               if not modelbuilder.toolcontext.opt_substitute_monomorph.value and not modelbuilder.toolcontext.opt_guard_call.value then return
+
+               for mmodule in mainmodule.in_importation.greaters do
+                       for cd in mmodule.mclassdefs do
+                               for m in cd.intro_mproperties do
+                                       if not m isa MMethod then continue
+                                       link_mmethod(m)
+                               end
+                       end
+               end
+       end
+
+       # Compile some linking information (if needed)
+       fun link_mmethod(m: MMethod)
+       do
+               var n2 = "CALL_" + m.const_color
+
+               # Replace monomorphic call by a direct call to the virtual implementation
+               var md = is_monomorphic(m)
+               if md != null then
+                       linker_script.add("{n2} = {md.virtual_runtime_function.c_name};")
+               end
+
+               # If opt_substitute_monomorph then a trampoline is used, else a weak symbol is used
+               if modelbuilder.toolcontext.opt_guard_call.value then
+                       var r = m.intro.virtual_runtime_function
+                       provide_declaration(n2, "{r.c_ret} {n2}{r.c_sig} __attribute__((weak));")
+               end
+       end
+
+       # The single mmethodef called in case of monomorphism.
+       # Returns nul if dead or polymorphic.
+       fun is_monomorphic(m: MMethod): nullable MMethodDef
+       do
+               var rta = runtime_type_analysis
+               if rta == null then
+                       # Without RTA, monomorphic means alone (uniq name)
+                       if m.mpropdefs.length == 1 then
+                               return m.mpropdefs.first
+                       else
+                               return null
+                       end
+               else
+                       # With RTA, monomorphic means only live methoddef
+                       var res: nullable MMethodDef = null
+                       for md in m.mpropdefs do
+                               if rta.live_methoddefs.has(md) then
+                                       if res != null then return null
+                                       res = md
+                               end
+                       end
+                       return res
+               end
+       end
+
        # Globaly compile the type structure of a live type
        fun compile_type_to_c(mtype: MType)
        do
@@ -1062,25 +1156,37 @@ class SeparateCompilerVisitor
        redef fun compile_callsite(callsite, args)
        do
                var rta = compiler.runtime_type_analysis
-               var mmethod = callsite.mproperty
-               # TODO: Inlining of new-style constructors
-               if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and not mmethod.is_root_init then
+               # TODO: Inlining of new-style constructors with initializers
+               if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
                        var tgs = rta.live_targets(callsite)
                        if tgs.length == 1 then
-                               # DIRECT CALL
-                               var res0 = before_send(mmethod, args)
-                               var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
-                               if res0 != null then
-                                       assert res != null
-                                       self.assign(res0, res)
-                                       res = res0
-                               end
-                               add("\}") # close the before_send
-                               return res
+                               return direct_call(tgs.first, args)
                        end
                end
+               # Shortcut intern methods as they are not usually redefinable
+               if callsite.mpropdef.is_intern and callsite.mproperty.name != "object_id" then
+                       # `object_id` is the only redefined intern method, so it can not be directly called.
+                       # TODO find a less ugly approach?
+                       return direct_call(callsite.mpropdef, args)
+               end
                return super
        end
+
+       # Fully and directly call a mpropdef
+       #
+       # This method is used by `compile_callsite`
+       private fun direct_call(mpropdef: MMethodDef, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               var res0 = before_send(mpropdef.mproperty, args)
+               var res = call(mpropdef, mpropdef.mclassdef.bound_mtype, args)
+               if res0 != null then
+                       assert res != null
+                       self.assign(res0, res)
+                       res = res0
+               end
+               add("\}") # close the before_send
+               return res
+       end
        redef fun send(mmethod, arguments)
        do
                if arguments.first.mcasttype.ctype != "val*" then
@@ -1093,7 +1199,7 @@ class SeparateCompilerVisitor
                        return res
                end
 
-               return table_send(mmethod, arguments, mmethod.const_color)
+               return table_send(mmethod, arguments, mmethod)
        end
 
        # Handle common special cases before doing the effective method invocation
@@ -1112,10 +1218,10 @@ class SeparateCompilerVisitor
                var res: nullable RuntimeVariable = null
                var recv = arguments.first
                var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
-               var maybenull = recv.mcasttype isa MNullableType and consider_null
+               var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null
                if maybenull then
                        self.add("if ({recv} == NULL) \{")
-                       if mmethod.name == "==" then
+                       if mmethod.name == "==" or mmethod.name == "is_same_instance" then
                                res = self.new_var(bool_type)
                                var arg = arguments[1]
                                if arg.mcasttype isa MNullableType then
@@ -1142,15 +1248,15 @@ class SeparateCompilerVisitor
                else
                        self.add("\{")
                end
-               if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
-                       if res == null then res = self.new_var(bool_type)
-                       # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants)
+               if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
+                       # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
                        var arg = arguments[1]
                        if arg.mcasttype isa MNullType then
-                               if mmethod.name == "==" then
-                                       self.add("{res} = 0; /* arg is null but recv is not */")
-                               else
+                               if res == null then res = self.new_var(bool_type)
+                               if mmethod.name == "!=" then
                                        self.add("{res} = 1; /* arg is null and recv is not */")
+                               else # `==` and `is_same_instance`
+                                       self.add("{res} = 0; /* arg is null but recv is not */")
                                end
                                self.add("\}") # closes the null case
                                self.add("if (0) \{") # what follow is useless, CC will drop it
@@ -1159,7 +1265,7 @@ class SeparateCompilerVisitor
                return res
        end
 
-       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
        do
                compiler.modelbuilder.nb_invok_by_tables += 1
                if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
@@ -1169,22 +1275,19 @@ class SeparateCompilerVisitor
 
                var res0 = before_send(mmethod, arguments)
 
+               var runtime_function = mmethod.intro.virtual_runtime_function
+               var msignature = runtime_function.called_signature
+
                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
-               if mmethod.is_new then
-                       ret = arguments.first.mtype
-                       res = self.new_var(ret)
-               else if ret == null then
+               if ret == null then
                        res = null
                else
                        res = self.new_var(ret)
                end
 
-               var s = new FlatBuffer
                var ss = new FlatBuffer
 
-               s.append("val*")
                ss.append("{recv}")
                for i in [0..msignature.arity[ do
                        var a = arguments[i+1]
@@ -1192,21 +1295,46 @@ class SeparateCompilerVisitor
                        if i == msignature.vararg_rank then
                                t = arguments[i+1].mcasttype
                        end
-                       s.append(", {t.ctype}")
                        a = self.autobox(a, t)
                        ss.append(", {a}")
                end
 
-
-               var r
-               if ret == null then r = "void" else r = ret.ctype
-               self.require_declaration(const_color)
-               var call = "(({r} (*)({s}))({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
-
+               var const_color = mentity.const_color
+               var ress
                if res != null then
-                       self.add("{res} = {call};")
+                       ress = "{res} = "
                else
-                       self.add("{call};")
+                       ress = ""
+               end
+               if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_direct_call_monomorph0.value then
+                       # opt_direct_call_monomorph0 is used to compare the efficiency of the alternative lookup implementation, ceteris paribus.
+                       # The difference with the non-zero option is that the monomorphism is looked-at on the mmethod level and not at the callsite level.
+                       # TODO: remove this mess and use per callsite service to detect monomorphism in a single place.
+                       var md = compiler.is_monomorphic(mentity)
+                       if md != null then
+                               var callsym = md.virtual_runtime_function.c_name
+                               self.require_declaration(callsym)
+                               self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       else
+                               self.require_declaration(const_color)
+                               self.add "{ress}(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       end
+               else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_guard_call.value then
+                       var callsym = "CALL_" + const_color
+                       self.require_declaration(callsym)
+                       self.add "if (!{callsym}) \{"
+                       self.require_declaration(const_color)
+                       self.add "{ress}(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       self.add "\} else \{"
+                       self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       self.add "\}"
+               else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
+                       var callsym = "CALL_" + const_color
+                       self.require_declaration(callsym)
+                       self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+               else
+                       self.require_declaration(const_color)
+                       self.add "{ress}(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
                end
 
                if res0 != null then
@@ -1226,10 +1354,7 @@ class SeparateCompilerVisitor
 
                var res: nullable RuntimeVariable
                var ret = mmethoddef.msignature.return_mtype
-               if mmethoddef.mproperty.is_new then
-                       ret = arguments.first.mtype
-                       res = self.new_var(ret)
-               else if ret == null then
+               if ret == null then
                        res = null
                else
                        ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
@@ -1240,7 +1365,7 @@ class SeparateCompilerVisitor
                        (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
                        compiler.modelbuilder.nb_invok_by_inline += 1
                        if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
-                       var frame = new Frame(self, mmethoddef, recvtype, arguments)
+                       var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
                        frame.returnlabel = self.get_name("RET_LABEL")
                        frame.returnvar = res
                        var old_frame = self.frame
@@ -1280,7 +1405,7 @@ class SeparateCompilerVisitor
                        self.compiler.mainmodule = main
                        return res
                end
-               return table_send(m.mproperty, arguments, m.const_color)
+               return table_send(m.mproperty, arguments, m)
        end
 
        redef fun vararg_instance(mpropdef, recv, varargs, elttype)
@@ -1290,11 +1415,11 @@ class SeparateCompilerVisitor
                # of the method (ie recv) if the static type is unresolved
                # This is more complex than usual because the unresolved type must not be resolved
                # with the current receiver (ie self).
-               # Therefore to isolate the resolution from self, a local Frame is created.
+               # Therefore to isolate the resolution from self, a local StaticFrame is created.
                # One can see this implementation as an inlined method of the receiver whose only
                # job is to allocate the array
                var old_frame = self.frame
-               var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
+               var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
                self.frame = frame
                #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
                var res = self.array_instance(varargs, elttype)
@@ -1739,7 +1864,10 @@ class SeparateCompilerVisitor
                var nclass = self.get_class("NativeArray")
                var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
                if pname == "[]" then
-                       self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
+                       # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
+                       var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
+                       res.mcasttype = ret_type.as(not null)
+                       self.ret(res)
                        return
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
@@ -1773,111 +1901,118 @@ class SeparateCompilerVisitor
 end
 
 redef class MMethodDef
-       fun separate_runtime_function: AbstractRuntimeFunction
+       # The C function associated to a mmethoddef
+       fun separate_runtime_function: SeparateRuntimeFunction
        do
                var res = self.separate_runtime_function_cache
                if res == null then
-                       res = new SeparateRuntimeFunction(self)
+                       var recv = mclassdef.bound_mtype
+                       var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
+                       res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
                        self.separate_runtime_function_cache = res
                end
                return res
        end
        private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
 
-       fun virtual_runtime_function: AbstractRuntimeFunction
+       # The C function associated to a mmethoddef, that can be stored into a VFT of a class
+       # The first parameter (the reciever) is always typed by val* in order to accept an object value
+       # The C-signature is always compatible with the intro
+       fun virtual_runtime_function: SeparateRuntimeFunction
        do
                var res = self.virtual_runtime_function_cache
                if res == null then
-                       res = new VirtualRuntimeFunction(self)
+                       # Because the function is virtual, the signature must match the one of the original class
+                       var intromclassdef = mproperty.intro.mclassdef
+                       var recv = intromclassdef.bound_mtype
+
+                       res = separate_runtime_function
+                       if res.called_recv == recv then
+                               self.virtual_runtime_function_cache = res
+                               return res
+                       end
+
+                       var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
+
+                       if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
+                               self.virtual_runtime_function_cache = res
+                               return res
+                       end
+
+                       res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
                        self.virtual_runtime_function_cache = res
+                       res.is_thunk = true
                end
                return res
        end
-       private var virtual_runtime_function_cache: nullable VirtualRuntimeFunction
+       private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
+end
+
+redef class MSignature
+       # Does the C-version of `self` the same than the C-version of `other`?
+       fun c_equiv(other: MSignature): Bool
+       do
+               if self == other then return true
+               if arity != other.arity then return false
+               for i in [0..arity[ do
+                       if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
+               end
+               if return_mtype != other.return_mtype then
+                       if return_mtype == null or other.return_mtype == null then return false
+                       if return_mtype.ctype != other.return_mtype.ctype then return false
+               end
+               return true
+       end
 end
 
 # The C function associated to a methoddef separately compiled
 class SeparateRuntimeFunction
        super AbstractRuntimeFunction
 
-       redef fun build_c_name: String do return "{mmethoddef.c_name}"
+       # The call-side static receiver
+       var called_recv: MType
 
-       redef fun to_s do return self.mmethoddef.to_s
+       # The call-side static signature
+       var called_signature: MSignature
 
-       redef fun compile_to_c(compiler)
-       do
-               var mmethoddef = self.mmethoddef
+       # The name on the compiled method
+       redef var build_c_name: String
 
-               var recv = self.mmethoddef.mclassdef.bound_mtype
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", recv, recv)
-               var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
-               v.frame = frame
+       # Statically call the original body instead
+       var is_thunk = false
 
-               var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
+       redef fun to_s do return self.mmethoddef.to_s
 
-               var sig = new FlatBuffer
-               var comment = new FlatBuffer
-               var ret = msignature.return_mtype
+       # The C return type (something or `void`)
+       var c_ret: String is lazy do
+               var ret = called_signature.return_mtype
                if ret != null then
-                       sig.append("{ret.ctype} ")
-               else if mmethoddef.mproperty.is_new then
-                       ret = recv
-                       sig.append("{ret.ctype} ")
+                       return ret.ctype
                else
-                       sig.append("void ")
+                       return "void"
                end
-               sig.append(self.c_name)
-               sig.append("({selfvar.mtype.ctype} {selfvar}")
-               comment.append("({selfvar}: {selfvar.mtype}")
-               arguments.add(selfvar)
-               for i in [0..msignature.arity[ do
-                       var mtype = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
+       end
+
+       # The C signature (only the parmeter part)
+       var c_sig: String is lazy do
+               var sig = new FlatBuffer
+               sig.append("({called_recv.ctype} self")
+               for i in [0..called_signature.arity[ do
+                       var mtype = called_signature.mparameters[i].mtype
+                       if i == called_signature.vararg_rank then
+                               mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
                        end
-                       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.provide_declaration(self.c_name, "{sig};")
-
-               v.add_decl("/* method {self} for {comment} */")
-               v.add_decl("{sig} \{")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
-
-               if recv != arguments.first.mtype then
-                       #print "{self} {recv} {arguments.first}"
-               end
-               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("\}")
-               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})"
+               return sig.to_s
        end
-end
-
-# The C function associated to a methoddef on a primitive type, stored into a VFT of a class
-# The first parameter (the reciever) is always typed by val* in order to accept an object value
-class VirtualRuntimeFunction
-       super AbstractRuntimeFunction
 
-       redef fun build_c_name: String do return "VIRTUAL_{mmethoddef.c_name}"
+       # The C type for the function pointer.
+       var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
 
-       redef fun to_s do return self.mmethoddef.to_s
+       # The arguments, as generated by `compile_to_c`
+       private var arguments: Array[RuntimeVariable] is noinit
 
        redef fun compile_to_c(compiler)
        do
@@ -1885,28 +2020,20 @@ class VirtualRuntimeFunction
 
                var recv = self.mmethoddef.mclassdef.bound_mtype
                var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", v.object_type, recv)
+               var selfvar = new RuntimeVariable("self", called_recv, recv)
                var arguments = new Array[RuntimeVariable]
-               var frame = new Frame(v, mmethoddef, recv, arguments)
+               var frame = new StaticFrame(v, mmethoddef, recv, arguments)
                v.frame = frame
 
+               var msignature = called_signature
+               var ret = called_signature.return_mtype
+
                var sig = new FlatBuffer
                var comment = new FlatBuffer
-
-               # Because the function is virtual, the signature must match the one of the original class
-               var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef
-               var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
-               var ret = msignature.return_mtype
-               if ret != null then
-                       sig.append("{ret.ctype} ")
-               else if mmethoddef.mproperty.is_new then
-                       ret = recv
-                       sig.append("{ret.ctype} ")
-               else
-                       sig.append("void ")
-               end
+               sig.append(c_ret)
+               sig.append(" ")
                sig.append(self.c_name)
-               sig.append("({selfvar.mtype.ctype} {selfvar}")
+               sig.append(c_sig)
                comment.append("({selfvar}: {selfvar.mtype}")
                arguments.add(selfvar)
                for i in [0..msignature.arity[ do
@@ -1915,16 +2042,15 @@ class VirtualRuntimeFunction
                                mtype = v.get_class("Array").get_mtype([mtype])
                        end
                        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.provide_declaration(self.c_name, "{sig};")
+               self.arguments = arguments.to_a
 
                v.add_decl("/* method {self} for {comment} */")
                v.add_decl("{sig} \{")
@@ -1933,10 +2059,14 @@ class VirtualRuntimeFunction
                end
                frame.returnlabel = v.get_name("RET_LABEL")
 
-               var subret = v.call(mmethoddef, recv, arguments)
-               if ret != null then
-                       assert subret != null
-                       v.assign(frame.returnvar.as(not null), subret)
+               if is_thunk then
+                       var subret = v.call(mmethoddef, recv, arguments)
+                       if ret != null then
+                               assert subret != null
+                               v.assign(frame.returnvar.as(not null), subret)
+                       end
+               else
+                       mmethoddef.compile_inside_to_c(v, arguments)
                end
 
                v.add("{frame.returnlabel.as(not null)}:;")
@@ -1944,27 +2074,66 @@ class VirtualRuntimeFunction
                        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})"
+               compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
 
-       # TODO ?
-       redef fun call(v, arguments) do abort
+       # Compile the trampolines used to implement late-binding.
+       #
+       # See `opt_trampoline_call`.
+       fun compile_trampolines(compiler: SeparateCompiler)
+       do
+               var recv = self.mmethoddef.mclassdef.bound_mtype
+               var selfvar = arguments.first
+               var ret = called_signature.return_mtype
+
+               if mmethoddef.is_intro and recv.ctype == "val*" then
+                       var m = mmethoddef.mproperty
+                       var n2 = "CALL_" + m.const_color
+                       compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
+                       var v2 = compiler.new_visitor
+                       v2.add "{c_ret} {n2}{c_sig} \{"
+                       v2.require_declaration(m.const_color)
+                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       if ret != null then
+                               v2.add "return {call}"
+                       else
+                               v2.add call
+                       end
+
+                       v2.add "\}"
+
+               end
+               if mmethoddef.has_supercall and recv.ctype == "val*" then
+                       var m = mmethoddef
+                       var n2 = "CALL_" + m.const_color
+                       compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
+                       var v2 = compiler.new_visitor
+                       v2.add "{c_ret} {n2}{c_sig} \{"
+                       v2.require_declaration(m.const_color)
+                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       if ret != null then
+                               v2.add "return {call}"
+                       else
+                               v2.add call
+                       end
+
+                       v2.add "\}"
+               end
+       end
 end
 
-redef class MType
-       fun const_color: String do return "COLOR_{c_name}"
+redef class MEntity
+       var const_color: String is lazy do return "COLOR_{c_name}"
 end
 
 interface PropertyLayoutElement end
 
 redef class MProperty
        super PropertyLayoutElement
-       fun const_color: String do return "COLOR_{c_name}"
 end
 
 redef class MPropDef
        super PropertyLayoutElement
-       fun const_color: String do return "COLOR_{c_name}"
 end
 
 redef class AMethPropdef