Updated `tests/syntax_callref.nit`
[nit.git] / src / compiler / separate_compiler.nit
index 424b849..9c98d07 100644 (file)
@@ -26,20 +26,20 @@ redef class ToolContext
        # --no-inline-intern
        var opt_no_inline_intern = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
        # --no-union-attribute
        # --no-inline-intern
        var opt_no_inline_intern = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
        # --no-union-attribute
-       var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
+       var opt_no_union_attribute = new OptionBool("Put primitive attributes 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")
        # --no-tag-primitives
        var opt_no_tag_primitives = new OptionBool("Use only boxes for primitive types", "--no-tag-primitives")
 
        # --colors-are-symbols
        # --no-shortcut-equate
        var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
        # --no-tag-primitives
        var opt_no_tag_primitives = new OptionBool("Use only boxes for primitive types", "--no-tag-primitives")
 
        # --colors-are-symbols
-       var opt_colors_are_symbols = new OptionBool("Store colors as symbols (link-boost)", "--colors-are-symbols")
+       var opt_colors_are_symbols = new OptionBool("Store colors as symbols instead of static data (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
        # --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")
+       var opt_substitute_monomorph = new OptionBool("Replace monomorphic trampolines with direct calls (link-boost)", "--substitute-monomorph")
        # --link-boost
        var opt_link_boost = new OptionBool("Enable all link-boost optimizations", "--link-boost")
 
        # --link-boost
        var opt_link_boost = new OptionBool("Enable all link-boost optimizations", "--link-boost")
 
@@ -48,9 +48,9 @@ redef class ToolContext
        # --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
        # --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")
+       var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorphic sites (semi-global)", "--direct-call-monomorph")
        # --direct-call-monomorph0
        # --direct-call-monomorph0
-       var opt_direct_call_monomorph0 = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph0")
+       var opt_direct_call_monomorph0 = new OptionBool("Allow the separate compiler to direct call monomorphic 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
        # --skip-dead-methods
        var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
        # --semi-global
@@ -59,6 +59,8 @@ redef class ToolContext
        var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods")
        # --tables-metrics
        var opt_tables_metrics = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
        var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods")
        # --tables-metrics
        var opt_tables_metrics = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
+       # --type-poset
+       var opt_type_poset = new OptionBool("Build a poset of types to create more condensed tables", "--type-poset")
 
        redef init
        do
 
        redef init
        do
@@ -72,6 +74,7 @@ redef class ToolContext
                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)
                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)
+               self.option_context.add_option(self.opt_type_poset)
        end
 
        redef fun process_options(args)
        end
 
        redef fun process_options(args)
@@ -146,8 +149,7 @@ class SeparateCompiler
        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
        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
+       private var thunks_to_compile: Set[SeparateRuntimeFunction] = new HashSet[SeparateRuntimeFunction]
 
        init do
                var file = new_file("nit.common")
 
        init do
                var file = new_file("nit.common")
@@ -194,6 +196,15 @@ class SeparateCompiler
                compiler.compile_types
        end
 
                compiler.compile_types
        end
 
+       fun thunk_todo(thunk: SeparateRuntimeFunction)
+       do
+               # Concrete instance of `SeparateRuntimeFunction` are already
+               # handled by the compiler. Avoid duplicate compilation.
+               if thunk isa SeparateThunkFunction then
+                       thunks_to_compile.add(thunk)
+               end
+       end
+
        # Color and compile type structures and cast information
        fun compile_types
        do
        # Color and compile type structures and cast information
        fun compile_types
        do
@@ -251,10 +262,11 @@ class SeparateCompiler
        do
                # Collect all bas box class
                # FIXME: this is not completely fine with a separate compilation scheme
        do
                # Collect all bas box class
                # FIXME: this is not completely fine with a separate compilation scheme
-               for classname in ["Int", "Bool", "Char", "Float", "NativeString", "Pointer"] do
+               for classname in ["Int", "Bool", "Byte", "Char", "Float", "CString",
+                                "Pointer", "Int8", "Int16", "UInt16", "Int32", "UInt32"] do
                        var classes = self.mainmodule.model.get_mclasses_by_name(classname)
                        if classes == null then continue
                        var classes = self.mainmodule.model.get_mclasses_by_name(classname)
                        if classes == null then continue
-                       assert classes.length == 1 else print classes.join(", ")
+                       assert classes.length == 1 else print_error classes.join(", ")
                        self.box_kinds[classes.first] = self.box_kinds.length + 1
                end
        end
                        self.box_kinds[classes.first] = self.box_kinds.length + 1
                end
        end
@@ -267,12 +279,11 @@ class SeparateCompiler
                #if mclass.mclass_type.ctype == "val*" or mclass.mclass_type.is_subtype(self.mainmodule, mclass.mclass_type pointer_type) then
                if mclass.mclass_type.ctype_extern == "val*" then
                        return 0
                #if mclass.mclass_type.ctype == "val*" or mclass.mclass_type.is_subtype(self.mainmodule, mclass.mclass_type pointer_type) then
                if mclass.mclass_type.ctype_extern == "val*" then
                        return 0
-               else if mclass.kind == extern_kind and mclass.name != "NativeString" then
+               else if mclass.kind == extern_kind and mclass.name != "CString" then
                        return self.box_kinds[self.mainmodule.pointer_type.mclass]
                else
                        return self.box_kinds[mclass]
                end
                        return self.box_kinds[self.mainmodule.pointer_type.mclass]
                else
                        return self.box_kinds[mclass]
                end
-
        end
 
        fun compile_color_consts(colors: Map[Object, Int]) do
        end
 
        fun compile_color_consts(colors: Map[Object, Int]) do
@@ -305,170 +316,146 @@ class SeparateCompiler
 
        private var color_consts_done = new HashSet[Object]
 
 
        private var color_consts_done = new HashSet[Object]
 
+       # The conflict graph of classes used for coloration
+       var class_conflict_graph: POSetConflictGraph[MClass] is noinit
+
        # colorize classe properties
        fun do_property_coloring do
 
                var rta = runtime_type_analysis
 
        # colorize classe properties
        fun do_property_coloring do
 
                var rta = runtime_type_analysis
 
-               # Layouts
-               var poset = mainmodule.flatten_mclass_hierarchy
-               var mclasses = new HashSet[MClass].from(poset)
-               var colorer = new POSetColorer[MClass]
-               colorer.colorize(poset)
+               # Class graph
+               var mclasses = mainmodule.flatten_mclass_hierarchy
+               class_conflict_graph = mclasses.to_conflict_graph
 
 
-               # The dead methods, still need to provide a dead color symbol
-               var dead_methods = new Array[MMethod]
-
-               # lookup properties to build layout with
+               # Prepare to collect elements to color and build layout with
                var mmethods = new HashMap[MClass, Set[PropertyLayoutElement]]
                var mattributes = new HashMap[MClass, Set[MAttribute]]
                var mmethods = new HashMap[MClass, Set[PropertyLayoutElement]]
                var mattributes = new HashMap[MClass, Set[MAttribute]]
+
+               # The dead methods and super-call, still need to provide a dead color symbol
+               var dead_methods = new Array[PropertyLayoutElement]
+
                for mclass in mclasses do
                        mmethods[mclass] = new HashSet[PropertyLayoutElement]
                        mattributes[mclass] = new HashSet[MAttribute]
                for mclass in mclasses do
                        mmethods[mclass] = new HashSet[PropertyLayoutElement]
                        mattributes[mclass] = new HashSet[MAttribute]
-                       for mprop in self.mainmodule.properties(mclass) do
-                               if mprop isa MMethod then
-                                       if not modelbuilder.toolcontext.opt_colo_dead_methods.value and rta != null and not rta.live_methods.has(mprop) then
-                                               dead_methods.add(mprop)
-                                               continue
-                                       end
-                                       mmethods[mclass].add(mprop)
-                               else if mprop isa MAttribute then
-                                       mattributes[mclass].add(mprop)
-                               end
+               end
+
+               # Pre-collect known live things
+               if rta != null then
+                       for m in rta.live_methods do
+                               mmethods[m.intro_mclassdef.mclass].add m
+                       end
+                       for m in rta.live_super_sends do
+                               var mclass = m.mclassdef.mclass
+                               mmethods[mclass].add m
                        end
                end
 
                        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)
+               for m in mainmodule.in_importation.greaters do for cd in m.mclassdefs do
+                       var mclass = cd.mclass
+                       # Collect methods and attributes
+                       for p in cd.intro_mproperties do
+                               if p isa MMethod then
+                                       if rta == null then
+                                               mmethods[mclass].add p
+                                       else if not rta.live_methods.has(p) then
+                                               dead_methods.add p
                                        end
                                        end
+                               else if p isa MAttribute then
+                                       mattributes[mclass].add p
                                end
                        end
                                end
                        end
-               end
-
-               # lookup super calls and add it to the list of mmethods to build layout with
-               var super_calls
-               if rta != null then
-                       super_calls = rta.live_super_sends
-               else
-                       super_calls = all_super_calls
-               end
 
 
-               for mmethoddef in super_calls do
-                       var mclass = mmethoddef.mclassdef.mclass
-                       mmethods[mclass].add(mmethoddef)
-                       for descendant in mclass.in_hierarchy(self.mainmodule).smallers do
-                               mmethods[descendant].add(mmethoddef)
+                       # Collect all super calls (dead or not)
+                       for mpropdef in cd.mpropdefs do
+                               if not mpropdef isa MMethodDef then continue
+                               if mpropdef.has_supercall then
+                                       if rta == null then
+                                               mmethods[mclass].add mpropdef
+                                       else if not rta.live_super_sends.has(mpropdef) then
+                                               dead_methods.add mpropdef
+                                       end
+                               end
                        end
                end
 
                # methods coloration
                        end
                end
 
                # methods coloration
-               var meth_colorer = new POSetBucketsColorer[MClass, PropertyLayoutElement](poset, colorer.conflicts)
-               method_colors = meth_colorer.colorize(mmethods)
-               method_tables = build_method_tables(mclasses, super_calls)
+               var meth_colorer = new POSetGroupColorer[MClass, PropertyLayoutElement](class_conflict_graph, mmethods)
+               var method_colors = meth_colorer.colors
                compile_color_consts(method_colors)
 
                compile_color_consts(method_colors)
 
-               # attribute null color to dead methods and supercalls
-               for mproperty in dead_methods do
-                       compile_color_const(new_visitor, mproperty, -1)
-               end
-               for mpropdef in all_super_calls do
-                       if super_calls.has(mpropdef) then continue
-                       compile_color_const(new_visitor, mpropdef, -1)
-               end
+               # give null color to dead methods and supercalls
+               for mproperty in dead_methods do compile_color_const(new_visitor, mproperty, -1)
 
 
-               # attributes coloration
-               var attr_colorer = new POSetBucketsColorer[MClass, MAttribute](poset, colorer.conflicts)
-               attr_colors = attr_colorer.colorize(mattributes)
-               attr_tables = build_attr_tables(mclasses)
+               # attribute coloration
+               var attr_colorer = new POSetGroupColorer[MClass, MAttribute](class_conflict_graph, mattributes)
+               var attr_colors = attr_colorer.colors#ize(poset, mattributes)
                compile_color_consts(attr_colors)
                compile_color_consts(attr_colors)
-       end
 
 
-       fun build_method_tables(mclasses: Set[MClass], super_calls: Set[MMethodDef]): Map[MClass, Array[nullable MPropDef]] do
-               var tables = new HashMap[MClass, Array[nullable MPropDef]]
+               # Build method and attribute tables
+               method_tables = new HashMap[MClass, Array[nullable MPropDef]]
+               attr_tables = new HashMap[MClass, Array[nullable MProperty]]
                for mclass in mclasses do
                for mclass in mclasses do
-                       var table = new Array[nullable MPropDef]
-                       tables[mclass] = table
+                       if not mclass.has_new_factory and (mclass.kind == abstract_kind or mclass.kind == interface_kind) then continue
+                       if rta != null and not rta.live_classes.has(mclass) then continue
 
 
-                       var mproperties = self.mainmodule.properties(mclass)
                        var mtype = mclass.intro.bound_mtype
 
                        var mtype = mclass.intro.bound_mtype
 
-                       for mproperty in mproperties do
-                               if not mproperty isa MMethod then continue
-                               if not method_colors.has_key(mproperty) then continue
-                               var color = method_colors[mproperty]
-                               if table.length <= color then
-                                       for i in [table.length .. color[ do
-                                               table[i] = null
-                                       end
-                               end
-                               table[color] = mproperty.lookup_first_definition(mainmodule, mtype)
-                       end
-
-                       for supercall in super_calls do
-                               if not mtype.collect_mclassdefs(mainmodule).has(supercall.mclassdef) then continue
-
-                               var color = method_colors[supercall]
-                               if table.length <= color then
-                                       for i in [table.length .. color[ do
-                                               table[i] = null
-                                       end
+                       # Resolve elements in the layout to get the final table
+                       var meth_layout = meth_colorer.build_layout(mclass)
+                       var meth_table = new Array[nullable MPropDef].with_capacity(meth_layout.length)
+                       method_tables[mclass] = meth_table
+                       for e in meth_layout do
+                               if e == null then
+                                       meth_table.add null
+                               else if e isa MMethod then
+                                       # Standard method call of `e`
+                                       meth_table.add e.lookup_first_definition(mainmodule, mtype)
+                               else if e isa MMethodDef then
+                                       # Super-call in the methoddef `e`
+                                       meth_table.add e.lookup_next_definition(mainmodule, mtype)
+                               else
+                                       abort
                                end
                                end
-                               var mmethoddef = supercall.lookup_next_definition(mainmodule, mtype)
-                               table[color] = mmethoddef
                        end
 
                        end
 
+                       # Do not need to resolve attributes as only the position is used
+                       attr_tables[mclass] = attr_colorer.build_layout(mclass)
                end
                end
-               return tables
-       end
-
-       fun build_attr_tables(mclasses: Set[MClass]): Map[MClass, Array[nullable MPropDef]] do
-               var tables = new HashMap[MClass, Array[nullable MPropDef]]
-               for mclass in mclasses do
-                       var table = new Array[nullable MPropDef]
-                       tables[mclass] = table
-
-                       var mproperties = self.mainmodule.properties(mclass)
-                       var mtype = mclass.intro.bound_mtype
 
 
-                       for mproperty in mproperties do
-                               if not mproperty isa MAttribute then continue
-                               if not attr_colors.has_key(mproperty) then continue
-                               var color = attr_colors[mproperty]
-                               if table.length <= color then
-                                       for i in [table.length .. color[ do
-                                               table[i] = null
-                                       end
-                               end
-                               table[color] = mproperty.lookup_first_definition(mainmodule, mtype)
-                       end
-               end
-               return tables
        end
 
        # colorize live types of the program
        end
 
        # colorize live types of the program
-       private fun do_type_coloring: POSet[MType] do
+       private fun do_type_coloring: Collection[MType] do
                # Collect types to colorize
                var live_types = runtime_type_analysis.live_types
                var live_cast_types = runtime_type_analysis.live_cast_types
 
                # Collect types to colorize
                var live_types = runtime_type_analysis.live_types
                var live_cast_types = runtime_type_analysis.live_cast_types
 
-               # Compute colors
-               var poset = poset_from_mtypes(live_types, live_cast_types)
-               var colorer = new POSetColorer[MType]
-               colorer.colorize(poset)
-               type_ids = colorer.ids
-               type_colors = colorer.colors
-               type_tables = build_type_tables(poset)
+               var res = new HashSet[MType]
+               res.add_all live_types
+               res.add_all live_cast_types
+
+               if modelbuilder.toolcontext.opt_type_poset.value then
+                       # Compute colors with a type poset
+                       var poset = poset_from_mtypes(live_types, live_cast_types)
+                       var colorer = new POSetColorer[MType]
+                       colorer.colorize(poset)
+                       type_ids = colorer.ids
+                       type_colors = colorer.colors
+                       type_tables = build_type_tables(poset)
+               else
+                       # Compute colors using the class poset
+                       # Faster to compute but the number of holes can degenerate
+                       compute_type_test_layouts(live_types, live_cast_types)
+
+                       type_ids = new HashMap[MType, Int]
+                       for x in res do type_ids[x] = type_ids.length + 1
+               end
 
                # VT and FT are stored with other unresolved types in the big resolution_tables
                self.compute_resolution_tables(live_types)
 
 
                # VT and FT are stored with other unresolved types in the big resolution_tables
                self.compute_resolution_tables(live_types)
 
-               return poset
+               return res
        end
 
        private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
        end
 
        private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
@@ -480,14 +467,14 @@ class SeparateCompiler
 
                var mtypes_by_class = new MultiHashMap[MClass, MType]
                for e in mtypes do
 
                var mtypes_by_class = new MultiHashMap[MClass, MType]
                for e in mtypes do
-                       var c = e.as_notnullable.as(MClassType).mclass
+                       var c = e.undecorate.as(MClassType).mclass
                        mtypes_by_class[c].add(e)
                        poset.add_node(e)
                end
 
                var casttypes_by_class = new MultiHashMap[MClass, MType]
                for e in cast_types do
                        mtypes_by_class[c].add(e)
                        poset.add_node(e)
                end
 
                var casttypes_by_class = new MultiHashMap[MClass, MType]
                for e in cast_types do
-                       var c = e.as_notnullable.as(MClassType).mclass
+                       var c = e.undecorate.as(MClassType).mclass
                        casttypes_by_class[c].add(e)
                        poset.add_node(e)
                end
                        casttypes_by_class[c].add(e)
                        poset.add_node(e)
                end
@@ -527,6 +514,47 @@ class SeparateCompiler
                return tables
        end
 
                return tables
        end
 
+       private fun compute_type_test_layouts(mtypes: Set[MClassType], cast_types: Set[MType]) do
+               # Group cast_type by their classes
+               var bucklets = new HashMap[MClass, Set[MType]]
+               for e in cast_types do
+                       var c = e.undecorate.as(MClassType).mclass
+                       if not bucklets.has_key(c) then
+                               bucklets[c] = new HashSet[MType]
+                       end
+                       bucklets[c].add(e)
+               end
+
+               # Colorize cast_types from the class hierarchy
+               var colorer = new POSetGroupColorer[MClass, MType](class_conflict_graph, bucklets)
+               type_colors = colorer.colors
+
+               var layouts = new HashMap[MClass, Array[nullable MType]]
+               for c in runtime_type_analysis.live_classes do
+                       layouts[c] = colorer.build_layout(c)
+               end
+
+               # Build the table for each live type
+               for t in mtypes do
+                       # A live type use the layout of its class
+                       var c = t.mclass
+                       var layout = layouts[c]
+                       var table = new Array[nullable MType].with_capacity(layout.length)
+                       type_tables[t] = table
+
+                       # For each potential super-type in the layout
+                       for sup in layout do
+                               if sup == null then
+                                       table.add null
+                               else if t.is_subtype(mainmodule, null, sup) then
+                                       table.add sup
+                               else
+                                       table.add null
+                               end
+                       end
+               end
+       end
+
        # resolution_tables is used to perform a type resolution at runtime in O(1)
        private fun compute_resolution_tables(mtypes: Set[MType]) do
                # During the visit of the body of classes, live_unresolved_types are collected
        # resolution_tables is used to perform a type resolution at runtime in O(1)
        private fun compute_resolution_tables(mtypes: Set[MType]) do
                # During the visit of the body of classes, live_unresolved_types are collected
@@ -534,21 +562,26 @@ class SeparateCompiler
                # Collect all live_unresolved_types (visited in the body of classes)
 
                # Determinate fo each livetype what are its possible requested anchored types
                # Collect all live_unresolved_types (visited in the body of classes)
 
                # Determinate fo each livetype what are its possible requested anchored types
-               var mtype2unresolved = new HashMap[MClassType, Set[MType]]
+               var mtype2unresolved = new HashMap[MClass, Set[MType]]
                for mtype in self.runtime_type_analysis.live_types do
                for mtype in self.runtime_type_analysis.live_types do
-                       var set = new HashSet[MType]
+                       var mclass = mtype.mclass
+                       var set = mtype2unresolved.get_or_null(mclass)
+                       if set == null then
+                               set = new HashSet[MType]
+                               mtype2unresolved[mclass] = set
+                       end
                        for cd in mtype.collect_mclassdefs(self.mainmodule) do
                                if self.live_unresolved_types.has_key(cd) then
                                        set.add_all(self.live_unresolved_types[cd])
                                end
                        end
                        for cd in mtype.collect_mclassdefs(self.mainmodule) do
                                if self.live_unresolved_types.has_key(cd) then
                                        set.add_all(self.live_unresolved_types[cd])
                                end
                        end
-                       mtype2unresolved[mtype] = set
                end
 
                # Compute the table layout with the prefered method
                end
 
                # Compute the table layout with the prefered method
-               var colorer = new BucketsColorer[MType, MType]
+               var colorer = new BucketsColorer[MClass, MType]
+
                opentype_colors = colorer.colorize(mtype2unresolved)
                opentype_colors = colorer.colorize(mtype2unresolved)
-               resolution_tables = self.build_resolution_tables(mtype2unresolved)
+               resolution_tables = self.build_resolution_tables(self.runtime_type_analysis.live_types, mtype2unresolved)
 
                # Compile a C constant for each collected unresolved type.
                # Either to a color, or to -1 if the unresolved type is dead (no live receiver can require it)
 
                # Compile a C constant for each collected unresolved type.
                # Either to a color, or to -1 if the unresolved type is dead (no live receiver can require it)
@@ -573,9 +606,10 @@ class SeparateCompiler
                #print ""
        end
 
                #print ""
        end
 
-       fun build_resolution_tables(elements: Map[MClassType, Set[MType]]): Map[MClassType, Array[nullable MType]] do
+       fun build_resolution_tables(elements: Set[MClassType], map: Map[MClass, Set[MType]]): Map[MClassType, Array[nullable MType]] do
                var tables = new HashMap[MClassType, Array[nullable MType]]
                var tables = new HashMap[MClassType, Array[nullable MType]]
-               for mclasstype, mtypes in elements do
+               for mclasstype in elements do
+                       var mtypes = map[mclasstype.mclass]
                        var table = new Array[nullable MType]
                        for mtype in mtypes do
                                var color = opentype_colors[mtype]
                        var table = new Array[nullable MType]
                        for mtype in mtypes do
                                var color = opentype_colors[mtype]
@@ -599,6 +633,7 @@ class SeparateCompiler
                for cd in mmodule.mclassdefs do
                        for pd in cd.mpropdefs do
                                if not pd isa MMethodDef then continue
                for cd in mmodule.mclassdefs do
                        for pd in cd.mpropdefs do
                                if not pd isa MMethodDef then continue
+                               if pd.mproperty.is_broken or pd.is_broken or pd.msignature == null then continue # Skip broken method
                                var rta = runtime_type_analysis
                                if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
                                #print "compile {pd} @ {cd} @ {mmodule}"
                                var rta = runtime_type_analysis
                                if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
                                #print "compile {pd} @ {cd} @ {mmodule}"
@@ -613,6 +648,15 @@ class SeparateCompiler
                                end
                        end
                end
                                end
                        end
                end
+               var compiled_thunks = new Array[SeparateRuntimeFunction]
+               # Compile thunks here to write them in the same module they are declared.
+               for thunk in thunks_to_compile do
+                       if thunk.mmethoddef.mclassdef.mmodule == mmodule then
+                               thunk.compile_to_c(self)
+                               compiled_thunks.add(thunk)
+                       end
+               end
+               thunks_to_compile.remove_all(compiled_thunks)
                self.mainmodule = old_module
        end
 
                self.mainmodule = old_module
        end
 
@@ -716,7 +760,7 @@ class SeparateCompiler
 
                # resolution table (for receiver)
                if is_live then
 
                # resolution table (for receiver)
                if is_live then
-                       var mclass_type = mtype.as_notnullable
+                       var mclass_type = mtype.undecorate
                        assert mclass_type isa MClassType
                        if resolution_tables[mclass_type].is_empty then
                                v.add_decl("NULL, /*NO RESOLUTIONS*/")
                        assert mclass_type isa MClassType
                        if resolution_tables[mclass_type].is_empty then
                                v.add_decl("NULL, /*NO RESOLUTIONS*/")
@@ -742,14 +786,15 @@ class SeparateCompiler
                        end
                        v.add_decl("\},")
                else
                        end
                        v.add_decl("\},")
                else
-                       v.add_decl("0, \{\}, /*DEAD TYPE*/")
+                       # Use -1 to indicate dead type, the info is used by --hardening
+                       v.add_decl("-1, \{\}, /*DEAD TYPE*/")
                end
                v.add_decl("\};")
        end
 
        fun compile_type_resolution_table(mtype: MType) do
 
                end
                v.add_decl("\};")
        end
 
        fun compile_type_resolution_table(mtype: MType) do
 
-               var mclass_type = mtype.as_notnullable.as(MClassType)
+               var mclass_type = mtype.undecorate.as(MClassType)
 
                # extern const struct resolution_table_X resolution_table_X
                self.provide_declaration("resolution_table_{mtype.c_name}", "extern const struct types resolution_table_{mtype.c_name};")
 
                # extern const struct resolution_table_X resolution_table_X
                self.provide_declaration("resolution_table_{mtype.c_name}", "extern const struct types resolution_table_{mtype.c_name};")
@@ -785,25 +830,29 @@ class SeparateCompiler
        # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
        fun compile_class_to_c(mclass: MClass)
        do
        # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
        fun compile_class_to_c(mclass: MClass)
        do
+               if mclass.is_broken then return
+
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
 
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
 
-               var vft = self.method_tables[mclass]
-               var attrs = self.attr_tables[mclass]
                var v = new_visitor
 
                var rta = runtime_type_analysis
                var v = new_visitor
 
                var rta = runtime_type_analysis
-               var is_dead = rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" and mclass.name != "Pointer"
+               var is_dead = rta != null and not rta.live_classes.has(mclass)
+               # While the class may be dead, some part of separately compiled code may use symbols associated to the class, so
+               # in order to compile and link correctly the C code, these symbols should be declared and defined.
+               var need_corpse = is_dead and mtype.is_c_primitive or mclass.kind == extern_kind or mclass.kind == enum_kind
 
 
-               v.add_decl("/* runtime class {c_name} */")
+               v.add_decl("/* runtime class {c_name}: {mclass.full_name} (dead={is_dead}; need_corpse={need_corpse})*/")
 
                # Build class vft
 
                # Build class vft
-               if not is_dead then
+               if not is_dead or need_corpse then
                        self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
                        v.add_decl("const struct class class_{c_name} = \{")
                        v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
                        v.add_decl("\{")
                        self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
                        v.add_decl("const struct class class_{c_name} = \{")
                        v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
                        v.add_decl("\{")
-                       for i in [0 .. vft.length[ do
+                       var vft = self.method_tables.get_or_null(mclass)
+                       if vft != null then for i in [0 .. vft.length[ do
                                var mpropdef = vft[i]
                                if mpropdef == null then
                                        v.add_decl("NULL, /* empty */")
                                var mpropdef = vft[i]
                                if mpropdef == null then
                                        v.add_decl("NULL, /* empty */")
@@ -812,6 +861,9 @@ class SeparateCompiler
                                        if rta != null and not rta.live_methoddefs.has(mpropdef) then
                                                v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
                                                continue
                                        if rta != null and not rta.live_methoddefs.has(mpropdef) then
                                                v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
                                                continue
+                                       else if mpropdef.is_broken or mpropdef.msignature == null or mpropdef.mproperty.is_broken then
+                                               v.add_decl("NULL, /* DEAD (BROKEN) {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
+                                               continue
                                        end
                                        var rf = mpropdef.virtual_runtime_function
                                        v.require_declaration(rf.c_name)
                                        end
                                        var rf = mpropdef.virtual_runtime_function
                                        v.require_declaration(rf.c_name)
@@ -834,13 +886,15 @@ class SeparateCompiler
                        self.header.add_decl("{mtype.ctype_extern} value;")
                        self.header.add_decl("\};")
 
                        self.header.add_decl("{mtype.ctype_extern} value;")
                        self.header.add_decl("\};")
 
-                       if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
+                       # Pointer is needed by extern types, live or not
+                       if is_dead and mtype.mclass.name != "Pointer" then return
 
                        #Build BOX
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
 
                        #Build BOX
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
-                       v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
+                       v.add("struct instance_{c_name}*res = {alloc};")
                        v.compiler.undead_types.add(mtype)
                        v.require_declaration("type_{c_name}")
                        v.add("res->type = &type_{c_name};")
                        v.compiler.undead_types.add(mtype)
                        v.require_declaration("type_{c_name}")
                        v.add("res->type = &type_{c_name};")
@@ -850,6 +904,7 @@ class SeparateCompiler
                        v.add("return (val*)res;")
                        v.add("\}")
 
                        v.add("return (val*)res;")
                        v.add("\}")
 
+                       # A Pointer class also need its constructor
                        if mtype.mclass.name != "Pointer" then return
 
                        v = new_visitor
                        if mtype.mclass.name != "Pointer" then return
 
                        v = new_visitor
@@ -861,7 +916,8 @@ class SeparateCompiler
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
-                               v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
+                               alloc = v.nit_alloc("sizeof(struct instance_{mtype.c_name})", mclass.full_name)
+                               v.add("{res} = {alloc};")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
@@ -888,7 +944,8 @@ class SeparateCompiler
                        var res = v.get_name("self")
                        v.add_decl("struct instance_{c_name} *{res};")
                        var mtype_elt = mtype.arguments.first
                        var res = v.get_name("self")
                        v.add_decl("struct instance_{c_name} *{res};")
                        var mtype_elt = mtype.arguments.first
-                       v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype})", mclass.full_name)
+                       v.add("{res} = {alloc};")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
@@ -897,21 +954,45 @@ class SeparateCompiler
                        v.add("return (val*){res};")
                        v.add("\}")
                        return
                        v.add("return (val*){res};")
                        v.add("\}")
                        return
-               else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
-                       # Is an extern class (other than Pointer and NativeString)
-                       # Pointer is caught in a previous `if`, and NativeString is internal
+               else if mclass.name == "RoutineRef" then
+                       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("val* recv;")
+                       self.header.add_decl("nitmethod_t method;")
+                       self.header.add_decl("\};")
+
+                       self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class, const struct type* type);")
+                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class, const struct type* type)\{")
+                       var res = v.get_name("self")
+                       v.add_decl("struct instance_{c_name} *{res};")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
+                       v.add("{res} = {alloc};")
+                       v.add("{res}->type = type;")
+                       hardening_live_type(v, "type")
+                       v.add("{res}->class = class;")
+                       v.add("{res}->recv = recv;")
+                       v.add("{res}->method = method;")
+                       v.add("return (val*){res};")
+                       v.add("\}")
+                       return
+               else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
+                       # Is an extern class (other than Pointer and CString)
+                       # Pointer is caught in a previous `if`, and CString is internal
 
                        var pointer_type = mainmodule.pointer_type
 
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
 
                        var pointer_type = mainmodule.pointer_type
 
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
-                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("/* allocate extern {mtype} */")
                        v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
                        if is_dead then
                                v.add_abort("{mclass} is DEAD")
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
                        v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
                        if is_dead then
                                v.add_abort("{mclass} is DEAD")
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
-                               v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
+                               var alloc = v.nit_alloc("sizeof(struct instance_{pointer_type.c_name})", mclass.full_name)
+                               v.add("{res} = {alloc};")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
@@ -932,13 +1013,26 @@ class SeparateCompiler
                else
                        var res = v.new_named_var(mtype, "self")
                        res.is_exact = true
                else
                        var res = v.new_named_var(mtype, "self")
                        res.is_exact = true
-                       v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
+                       var attrs = self.attr_tables.get_or_null(mclass)
+                       if attrs == null then
+                               var alloc = v.nit_alloc("sizeof(struct instance)", mclass.full_name)
+                               v.add("{res} = {alloc};")
+                       else
+                               var alloc = v.nit_alloc("sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t)", mclass.full_name)
+                               v.add("{res} = {alloc};")
+                       end
+                       if modelbuilder.toolcontext.opt_trace.value then
+                               v.add("tracepoint(Nit_Compiler, Object_Instance,\"{mtype}\", (uintptr_t)self);")
+                               v.add("GC_register_finalizer(self, object_destroy_callback, NULL, NULL, NULL);")
+                       end
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
                        v.add("{res}->class = &class_{c_name};")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
                        v.add("{res}->class = &class_{c_name};")
-                       self.generate_init_attr(v, res, mtype)
-                       v.set_finalizer res
+                       if attrs != null then
+                               self.generate_init_attr(v, res, mtype)
+                               v.set_finalizer res
+                       end
                        v.add("return {res};")
                end
                v.add("\}")
                        v.add("return {res};")
                end
                v.add("\}")
@@ -958,10 +1052,13 @@ class SeparateCompiler
                        # use some Huffman coding.
                        if t.name == "Int" then
                                class_info[1] = t
                        # use some Huffman coding.
                        if t.name == "Int" then
                                class_info[1] = t
+                               t.mclass_type.tag_value = 1
                        else if t.name == "Char" then
                                class_info[2] = t
                        else if t.name == "Char" then
                                class_info[2] = t
+                               t.mclass_type.tag_value = 2
                        else if t.name == "Bool" then
                                class_info[3] = t
                        else if t.name == "Bool" then
                                class_info[3] = t
+                               t.mclass_type.tag_value = 3
                        else
                                continue
                        end
                        else
                                continue
                        end
@@ -1004,8 +1101,8 @@ class SeparateCompiler
                v.add("if({t} == NULL) \{")
                v.add_abort("type null")
                v.add("\}")
                v.add("if({t} == NULL) \{")
                v.add_abort("type null")
                v.add("\}")
-               v.add("if({t}->table_size == 0) \{")
-               v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
+               v.add("if({t}->table_size < 0) \{")
+               v.add("PRINT_ERROR(\"Instantiation of a dead type: %s\\n\", {t}->name);")
                v.add_abort("type dead")
                v.add("\}")
        end
                v.add_abort("type dead")
                v.add("\}")
        end
@@ -1017,7 +1114,7 @@ class SeparateCompiler
        private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
        private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
        protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
        private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
        private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
        protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
-       protected var attr_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
+       protected var attr_tables: Map[MClass, Array[nullable MProperty]] = new HashMap[MClass, Array[nullable MProperty]]
 
        redef fun display_stats
        do
 
        redef fun display_stats
        do
@@ -1126,8 +1223,9 @@ class SeparateCompilerVisitor
                        args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
                end
                for i in [0..msignature.arity[ do
                        args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
                end
                for i in [0..msignature.arity[ do
-                       var t = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
+                       var mp = msignature.mparameters[i]
+                       var t = mp.mtype
+                       if mp.is_vararg then
                                t = args[i+1].mtype
                        end
                        args[i+1] = self.autobox(args[i+1], t)
                                t = args[i+1].mtype
                        end
                        args[i+1] = self.autobox(args[i+1], t)
@@ -1141,8 +1239,9 @@ class SeparateCompilerVisitor
                        args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
                end
                for i in [0..msignature.arity[ do
                        args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
                end
                for i in [0..msignature.arity[ do
-                       var t = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
+                       var mp = msignature.mparameters[i]
+                       var t = mp.mtype
+                       if mp.is_vararg then
                                t = args[i+1].mtype
                        end
                        if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
                                t = args[i+1].mtype
                        end
                        if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
@@ -1160,7 +1259,7 @@ class SeparateCompilerVisitor
                                if mtype.name == "Int" then
                                        return self.new_expr("(long)({value})>>2", mtype)
                                else if mtype.name == "Char" then
                                if mtype.name == "Int" then
                                        return self.new_expr("(long)({value})>>2", mtype)
                                else if mtype.name == "Char" then
-                                       return self.new_expr("(char)((long)({value})>>2)", mtype)
+                                       return self.new_expr("(uint32_t)((long)({value})>>2)", mtype)
                                else if mtype.name == "Bool" then
                                        return self.new_expr("(short int)((long)({value})>>2)", mtype)
                                else
                                else if mtype.name == "Bool" then
                                        return self.new_expr("(short int)((long)({value})>>2)", mtype)
                                else
@@ -1169,27 +1268,29 @@ class SeparateCompilerVisitor
                        end
                        return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
                else if not mtype.is_c_primitive then
                        end
                        return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
                else if not mtype.is_c_primitive then
+                       assert value.mtype == value.mcasttype
                        if value.mtype.is_tagged then
                        if value.mtype.is_tagged then
+                               var res
                                if value.mtype.name == "Int" then
                                if value.mtype.name == "Int" then
-                                       return self.new_expr("(val*)({value}<<2|1)", mtype)
+                                       res = self.new_expr("(val*)({value}<<2|1)", mtype)
                                else if value.mtype.name == "Char" then
                                else if value.mtype.name == "Char" then
-                                       return self.new_expr("(val*)((long)({value})<<2|2)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|2)", mtype)
                                else if value.mtype.name == "Bool" then
                                else if value.mtype.name == "Bool" then
-                                       return self.new_expr("(val*)((long)({value})<<2|3)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|3)", mtype)
                                else
                                        abort
                                end
                                else
                                        abort
                                end
+                               # Do not loose type info
+                               res.mcasttype = value.mcasttype
+                               return res
                        end
                        var valtype = value.mtype.as(MClassType)
                        end
                        var valtype = value.mtype.as(MClassType)
-                       if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
+                       if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
                                valtype = compiler.mainmodule.pointer_type
                        end
                        var res = self.new_var(mtype)
                                valtype = compiler.mainmodule.pointer_type
                        end
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and 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("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       # Do not loose type info
+                       res.mcasttype = value.mcasttype
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
@@ -1209,7 +1310,7 @@ class SeparateCompilerVisitor
        redef fun unbox_extern(value, mtype)
        do
                if mtype isa MClassType and mtype.mclass.kind == extern_kind and
        redef fun unbox_extern(value, mtype)
        do
                if mtype isa MClassType and mtype.mclass.kind == extern_kind and
-                  mtype.mclass.name != "NativeString" then
+                  mtype.mclass.name != "CString" then
                        var pointer_type = compiler.mainmodule.pointer_type
                        var res = self.new_var_extern(mtype)
                        self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
                        var pointer_type = compiler.mainmodule.pointer_type
                        var res = self.new_var_extern(mtype)
                        self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
@@ -1222,14 +1323,10 @@ class SeparateCompilerVisitor
        redef fun box_extern(value, mtype)
        do
                if mtype isa MClassType and mtype.mclass.kind == extern_kind and
        redef fun box_extern(value, mtype)
        do
                if mtype isa MClassType and mtype.mclass.kind == extern_kind and
-                  mtype.mclass.name != "NativeString" then
+                  mtype.mclass.name != "CString" then
                        var valtype = compiler.mainmodule.pointer_type
                        var res = self.new_var(mtype)
                        var valtype = compiler.mainmodule.pointer_type
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
-                               self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
-                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       compiler.undead_types.add(mtype)
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
                        self.require_declaration("type_{mtype.c_name}")
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
                        self.require_declaration("type_{mtype.c_name}")
@@ -1351,8 +1448,7 @@ 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 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 or recv.mcasttype isa MNullType) and consider_null
-               if maybenull then
+               if maybe_null(recv) and consider_null then
                        self.add("if ({recv} == NULL) \{")
                        if mmethod.name == "==" or mmethod.name == "is_same_instance" then
                                res = self.new_var(bool_type)
                        self.add("if ({recv} == NULL) \{")
                        if mmethod.name == "==" or mmethod.name == "is_same_instance" then
                                res = self.new_var(bool_type)
@@ -1404,13 +1500,14 @@ class SeparateCompilerVisitor
                if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
 
                assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
                if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
 
                assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
-               var recv = arguments.first
 
                var res0 = before_send(mmethod, arguments)
 
                var runtime_function = mmethod.intro.virtual_runtime_function
                var msignature = runtime_function.called_signature
 
 
                var res0 = before_send(mmethod, arguments)
 
                var runtime_function = mmethod.intro.virtual_runtime_function
                var msignature = runtime_function.called_signature
 
+               adapt_signature(mmethod.intro, arguments)
+
                var res: nullable RuntimeVariable
                var ret = msignature.return_mtype
                if ret == null then
                var res: nullable RuntimeVariable
                var ret = msignature.return_mtype
                if ret == null then
@@ -1419,18 +1516,7 @@ class SeparateCompilerVisitor
                        res = self.new_var(ret)
                end
 
                        res = self.new_var(ret)
                end
 
-               var ss = new FlatBuffer
-
-               ss.append("{recv}")
-               for i in [0..msignature.arity[ do
-                       var a = arguments[i+1]
-                       var t = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               t = arguments[i+1].mcasttype
-                       end
-                       a = self.autobox(a, t)
-                       ss.append(", {a}")
-               end
+               var ss = arguments.join(", ")
 
                var const_color = mentity.const_color
                var ress
 
                var const_color = mentity.const_color
                var ress
@@ -1830,7 +1916,7 @@ class SeparateCompilerVisitor
                        else
                                var mtype1 = value1.mtype.as(MClassType)
                                self.require_declaration("class_{mtype1.c_name}")
                        else
                                var mtype1 = value1.mtype.as(MClassType)
                                self.require_declaration("class_{mtype1.c_name}")
-                               self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
+                               self.add("{res} = ({value2} != NULL) && ({class_info(value2)} == &class_{mtype1.c_name}); /* is_same_type_test */")
                        end
                else
                        self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
                        end
                else
                        self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
@@ -1845,7 +1931,7 @@ class SeparateCompilerVisitor
                if not value.mtype.is_c_primitive then
                        self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
                else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
                if not value.mtype.is_c_primitive then
                        self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
                else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
-                       value.mtype.as(MClassType).name != "NativeString" then
+                       value.mtype.as(MClassType).name != "CString" then
                        self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
                else
                        self.require_declaration("type_{value.mtype.c_name}")
                        self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
                else
                        self.require_declaration("type_{value.mtype.c_name}")
@@ -1863,20 +1949,58 @@ class SeparateCompilerVisitor
                        value2 = tmp
                end
                if value1.mtype.is_c_primitive then
                        value2 = tmp
                end
                if value1.mtype.is_c_primitive then
-                       if value2.mtype == value1.mtype then
+                       var t1 = value1.mtype
+                       assert t1 == value1.mcasttype
+
+                       # Fast case: same C type.
+                       if value2.mtype == t1 then
+                               # Same exact C primitive representation.
                                self.add("{res} = {value1} == {value2};")
                                self.add("{res} = {value1} == {value2};")
-                       else if value2.mtype.is_c_primitive then
-                               self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
-                       else if value1.mtype.is_tagged then
-                               self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});")
+                               return res
+                       end
+
+                       # Complex case: value2 has a different representation
+                       # Thus, it should be checked if `value2` is type-compatible with `value1`
+                       # This compatibility is done statically if possible and dynamically else
+
+                       # Conjunction (ands) of dynamic tests according to the static knowledge
+                       var tests = new Array[String]
+
+                       var t2 = value2.mcasttype
+                       if t2 isa MNullableType then
+                               # The destination type cannot be null
+                               tests.add("({value2} != NULL)")
+                               t2 = t2.mtype
+                       else if t2 isa MNullType then
+                               # `value2` is known to be null, thus incompatible with a primitive
+                               self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
+                               return res
+                       end
+
+                       if t2 == t1 then
+                               # Same type but different representation.
+                       else if t2.is_c_primitive then
+                               # Type of `value2` is a different primitive type, thus incompatible
+                               self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
+                               return res
+                       else if t1.is_tagged then
+                               # To be equal, `value2` should also be correctly tagged
+                               tests.add("({extract_tag(value2)} == {t1.tag_value})")
                        else
                        else
-                               var mtype1 = value1.mtype.as(MClassType)
-                               self.require_declaration("class_{mtype1.c_name}")
-                               self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
-                               self.add("if ({res}) \{")
-                               self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
-                               self.add("\}")
+                               # To be equal, `value2` should also be boxed with the same class
+                               self.require_declaration("class_{t1.c_name}")
+                               tests.add "({class_info(value2)} == &class_{t1.c_name})"
                        end
                        end
+
+                       # Compare the unboxed `value2` with `value1`
+                       if tests.not_empty then
+                               self.add "if ({tests.join(" && ")}) \{"
+                       end
+                       self.add "{res} = {self.autobox(value2, t1)} == {value1};"
+                       if tests.not_empty then
+                               self.add "\} else {res} = 0;"
+                       end
+
                        return res
                end
                var maybe_null = true
                        return res
                end
                var maybe_null = true
@@ -1974,18 +2098,12 @@ class SeparateCompilerVisitor
 
        fun can_be_primitive(value: RuntimeVariable): Bool
        do
 
        fun can_be_primitive(value: RuntimeVariable): Bool
        do
-               var t = value.mcasttype.as_notnullable
+               var t = value.mcasttype.undecorate
                if not t isa MClassType then return false
                var k = t.mclass.kind
                return k == interface_kind or t.is_c_primitive
        end
 
                if not t isa MClassType then return false
                var k = t.mclass.kind
                return k == interface_kind or t.is_c_primitive
        end
 
-       fun maybe_null(value: RuntimeVariable): Bool
-       do
-               var t = value.mcasttype
-               return t isa MNullableType or t isa MNullType
-       end
-
        redef fun array_instance(array, elttype)
        do
                var nclass = mmodule.native_array_class
        redef fun array_instance(array, elttype)
        do
                var nclass = mmodule.native_array_class
@@ -2004,23 +2122,24 @@ class SeparateCompilerVisitor
                return res
        end
 
                return res
        end
 
-       redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
+       redef fun native_array_instance(elttype, length)
        do
                var mtype = mmodule.native_array_type(elttype)
                self.require_declaration("NEW_{mtype.mclass.c_name}")
                assert mtype isa MGenericType
                var compiler = self.compiler
        do
                var mtype = mmodule.native_array_type(elttype)
                self.require_declaration("NEW_{mtype.mclass.c_name}")
                assert mtype isa MGenericType
                var compiler = self.compiler
+               length = autobox(length, compiler.mainmodule.int_type)
                if mtype.need_anchor then
                        hardening_live_open_type(mtype)
                        link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
                        var recv = self.frame.arguments.first
                        var recv_type_info = self.type_info(recv)
                        self.require_declaration(mtype.const_color)
                if mtype.need_anchor then
                        hardening_live_open_type(mtype)
                        link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
                        var recv = self.frame.arguments.first
                        var recv_type_info = self.type_info(recv)
                        self.require_declaration(mtype.const_color)
-                       return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
+                       return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
                end
                compiler.undead_types.add(mtype)
                self.require_declaration("type_{mtype.c_name}")
                end
                compiler.undead_types.add(mtype)
                self.require_declaration("type_{mtype.c_name}")
-               return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
+               return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, &type_{mtype.c_name})", mtype)
        end
 
        redef fun native_array_def(pname, ret_type, arguments)
        end
 
        redef fun native_array_def(pname, ret_type, arguments)
@@ -2033,17 +2152,153 @@ class SeparateCompilerVisitor
                        var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
                        res.mcasttype = ret_type.as(not null)
                        self.ret(res)
                        var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
                        res.mcasttype = ret_type.as(not null)
                        self.ret(res)
-                       return
+                       return true
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
-                       return
+                       return true
                else if pname == "length" then
                        self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
                else if pname == "length" then
                        self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
-                       return
+                       return true
                else if pname == "copy_to" then
                        var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
                        self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
                else if pname == "copy_to" then
                        var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
                        self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
-                       return
+                       return true
+               else if pname == "memmove" then
+                       # fun memmove(start: Int, length: Int, dest: NativeArray[E], dest_start: Int) is intern do
+                       var recv1 = "((struct instance_{nclass.c_name}*){arguments[3]})->values"
+                       self.add("memmove({recv1}+{arguments[4]}, {recv}+{arguments[1]}, {arguments[2]}*sizeof({elttype.ctype}));")
+                       return true
+               end
+               return false
+       end
+
+       redef fun native_array_get(nat, i)
+       do
+               var nclass = mmodule.native_array_class
+               var recv = "((struct instance_{nclass.c_name}*){nat})->values"
+               # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
+               var res = self.new_expr("{recv}[{i}]", compiler.mainmodule.object_type)
+               return res
+       end
+
+       redef fun native_array_set(nat, i, val)
+       do
+               var nclass = mmodule.native_array_class
+               var recv = "((struct instance_{nclass.c_name}*){nat})->values"
+               self.add("{recv}[{i}]={val};")
+       end
+
+       redef fun routine_ref_instance(routine_type, recv, mmethoddef)
+       do
+               #debug "ENTER ref_instance"
+               var mmethod = mmethoddef.mproperty
+               # routine_mclass is the specialized one, e.g: FunRef1, ProcRef2, etc..
+               var routine_mclass = routine_type.mclass
+
+               var nclasses = mmodule.model.get_mclasses_by_name("RoutineRef").as(not null)
+               var base_routine_mclass = nclasses.first
+
+               # All routine classes use the same `NEW` constructor.
+               # However, they have different declared `class` and `type` value.
+               self.require_declaration("NEW_{base_routine_mclass.c_name}")
+
+               var recv_class_cname = recv.mcasttype.as(MClassType).mclass.c_name
+               var my_recv = recv
+
+               if recv.mtype.is_c_primitive then
+                       my_recv = autobox(recv, mmodule.object_type)
+               end
+               var my_recv_mclass_type = my_recv.mtype.as(MClassType)
+
+               # The class of the concrete Routine must exist (e.g ProcRef0, FunRef0, etc.)
+               self.require_declaration("class_{routine_mclass.c_name}")
+               self.require_declaration("type_{routine_type.c_name}")
+
+               compiler.undead_types.add(routine_type)
+               self.require_declaration(mmethoddef.c_name)
+
+               var thunk_function = mmethoddef.callref_thunk(my_recv_mclass_type)
+               # If the receiver is exact, then there's no need to make a
+               # polymorph call to the underlying method.
+               thunk_function.polymorph_call_flag = not my_recv.is_exact
+               var runtime_function = mmethoddef.virtual_runtime_function
+
+               var is_c_equiv = runtime_function.msignature.c_equiv(thunk_function.msignature)
+
+               var c_ref = thunk_function.c_ref
+               if is_c_equiv then
+                       var const_color = mmethoddef.mproperty.const_color
+                       c_ref = "{class_info(my_recv)}->vft[{const_color}]"
+                       self.require_declaration(const_color)
+               else
+                       self.require_declaration(thunk_function.c_name)
+                       compiler.thunk_todo(thunk_function)
+               end
+
+               # Each RoutineRef points to a receiver AND a callref_thunk
+               var res = self.new_expr("NEW_{base_routine_mclass.c_name}({my_recv}, (nitmethod_t){c_ref}, &class_{routine_mclass.c_name}, &type_{routine_type.c_name})", routine_type)
+               #debug "LEAVING ref_instance"
+               return res
+       end
+
+       redef fun routine_ref_call(mmethoddef, arguments)
+       do
+               #debug "ENTER ref_call"
+               compiler.modelbuilder.nb_invok_by_tables += 1
+               if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
+               var nclasses = mmodule.model.get_mclasses_by_name("RoutineRef").as(not null)
+               var nclass = nclasses.first
+               var runtime_function = mmethoddef.virtual_runtime_function
+
+               # Save the current receiver since adapt_signature will autobox
+               # the routine receiver which is not the underlying receiver.
+               # The underlying receiver has already been adapted in the
+               # `routine_ref_instance` method. Here we just want to adapt the
+               # rest of the signature, but it's easier to pass the wrong
+               # receiver in adapt_signature then discards it with `shift`.
+               #
+               # ~~~~nitish
+               # class A; def toto do print "toto"; end
+               # var a = new A
+               # var f = &a.toto # `a` is the underlying receiver
+               # f.call # here `f` is the routine receiver
+               # ~~~~
+               var routine = arguments.first
+
+               # Retrieve the concrete routine type
+               var original_recv_c = "(((struct instance_{nclass.c_name}*){arguments[0]})->recv)"
+               var nitmethod = "(({runtime_function.c_funptrtype})(((struct instance_{nclass.c_name}*){arguments[0]})->method))"
+               if arguments.length > 1 then
+                       adapt_signature(mmethoddef, arguments)
+               end
+
+               var ret_mtype = runtime_function.called_signature.return_mtype
+
+               if ret_mtype != null then
+                       # `ret` is actually always nullable Object. When invoking
+                       # a callref, we don't have the original callsite information.
+                       # Thus, we need to recompute the return type of the callsite.
+                       ret_mtype = resolve_for(ret_mtype, routine)
+               end
+
+               # remove the routine's receiver
+               arguments.shift
+               var ss = arguments.join(", ")
+               # replace the receiver with the original one
+               if arguments.length > 0 then
+                       ss = "{original_recv_c}, {ss}"
+               else
+                       ss = original_recv_c
+               end
+
+               arguments.unshift routine # put back the routine ref receiver
+               add "/* {mmethoddef.mproperty} on {arguments.first.inspect}*/"
+               var callsite = "{nitmethod}({ss})"
+               if ret_mtype != null then
+                       var subres = new_expr("{callsite}", ret_mtype)
+                       ret(subres)
+               else
+                       add("{callsite};")
                end
        end
 
                end
        end
 
@@ -2070,6 +2325,43 @@ redef class MMethodDef
                end
                return res
        end
                end
                return res
        end
+
+       # Returns true if the current method definition differ from
+       # its original introduction in terms of receiver type.
+       fun recv_differ_from_intro: Bool
+       do
+               var intromclassdef = mproperty.intro.mclassdef
+               var introrecv = intromclassdef.bound_mtype
+               return self.mclassdef.bound_mtype != introrecv
+       end
+
+       # The C thunk function associated to a mmethoddef. Receives only nullable
+       # Object and cast them to the original mmethoddef signature.
+       fun callref_thunk(recv_mtype: MClassType): SeparateThunkFunction
+       do
+               var res = callref_thunk_cache
+               if res == null then
+                       var object_type = mclassdef.mmodule.object_type
+                       var nullable_object = object_type.as_nullable
+                       var ps = new Array[MParameter]
+
+                       # Replace every argument type by nullable object
+                       for p in msignature.mparameters do
+                               ps.push(new MParameter(p.name, nullable_object, p.is_vararg))
+                       end
+                       var ret: nullable MType = null
+                       if msignature.return_mtype != null then ret = nullable_object
+                       var msignature2 = new MSignature(ps, ret)
+                       var intromclassdef = mproperty.intro.mclassdef
+
+                       res = new SeparateThunkFunction(self, recv_mtype, msignature2, "THUNK_{c_name}", mclassdef.bound_mtype)
+                       res.polymorph_call_flag = true
+                       callref_thunk_cache = res
+               end
+               return res
+       end
+
+       private var callref_thunk_cache: nullable SeparateThunkFunction
        private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
 
        # The C function associated to a mmethoddef, that can be stored into a VFT of a class
        private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
 
        # The C function associated to a mmethoddef, that can be stored into a VFT of a class
@@ -2095,10 +2387,7 @@ redef class MMethodDef
                                self.virtual_runtime_function_cache = res
                                return res
                        end
                                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
+                       res = new SeparateThunkFunction(self, recv, msignature, "VIRTUAL_{c_name}", mclassdef.bound_mtype)
                end
                return res
        end
                end
                return res
        end
@@ -2135,11 +2424,23 @@ class SeparateRuntimeFunction
        # The name on the compiled method
        redef var build_c_name: String
 
        # The name on the compiled method
        redef var build_c_name: String
 
-       # Statically call the original body instead
-       var is_thunk = false
-
        redef fun to_s do return self.mmethoddef.to_s
 
        redef fun to_s do return self.mmethoddef.to_s
 
+       redef fun msignature
+       do
+               return called_signature
+       end
+
+       redef fun recv_mtype
+       do
+               return called_recv
+       end
+
+       redef fun return_mtype
+       do
+               return called_signature.return_mtype
+       end
+
        # The C return type (something or `void`)
        var c_ret: String is lazy do
                var ret = called_signature.return_mtype
        # The C return type (something or `void`)
        var c_ret: String is lazy do
                var ret = called_signature.return_mtype
@@ -2155,8 +2456,9 @@ class SeparateRuntimeFunction
                var sig = new FlatBuffer
                sig.append("({called_recv.ctype} self")
                for i in [0..called_signature.arity[ 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
+                       var mp = called_signature.mparameters[i]
+                       var mtype = mp.mtype
+                       if mp.is_vararg then
                                mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
                        end
                        sig.append(", {mtype.ctype} p{i}")
                                mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
                        end
                        sig.append(", {mtype.ctype} p{i}")
@@ -2168,80 +2470,43 @@ class SeparateRuntimeFunction
        # The C type for the function pointer.
        var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
 
        # The C type for the function pointer.
        var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
 
-       # The arguments, as generated by `compile_to_c`
-       private var arguments: Array[RuntimeVariable] is noinit
-
-       redef fun compile_to_c(compiler)
+       redef fun declare_signature(v, sig)
        do
        do
-               var mmethoddef = self.mmethoddef
-
-               var recv = self.mmethoddef.mclassdef.bound_mtype
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", called_recv, recv)
-               var arguments = new Array[RuntimeVariable]
-               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
-               sig.append(c_ret)
-               sig.append(" ")
-               sig.append(self.c_name)
-               sig.append(c_sig)
-               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.mmodule.array_type(mtype)
-                       end
-                       comment.append(", {mtype}")
-                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
-                       arguments.add(argvar)
-               end
-               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} \{")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
+               v.compiler.provide_declaration(c_name, "{sig};")
+       end
 
 
-               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
+       redef fun body_to_c(v)
+       do
+               var rta = v.compiler.as(SeparateCompiler).runtime_type_analysis
+               if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
+                       v.add_abort("FATAL: Dead method executed.")
                else
                else
-                       mmethoddef.compile_inside_to_c(v, arguments)
+                       super
                end
                end
+       end
 
 
-               v.add("{frame.returnlabel.as(not null)}:;")
-               if ret != null then
-                       v.add("return {frame.returnvar.as(not null)};")
-               end
-               v.add("\}")
+       redef fun end_compile_to_c(v)
+       do
+               var compiler = v.compiler
                compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
 
                compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
 
+       redef fun build_frame(v, arguments)
+       do
+               var recv = mmethoddef.mclassdef.bound_mtype
+               return new StaticFrame(v, mmethoddef, recv, arguments)
+       end
+
        # 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
        # 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 selfvar = new RuntimeVariable("self", called_recv, recv)
                var ret = called_signature.return_mtype
                var ret = called_signature.return_mtype
+               var arguments = ["self"]
+               for i in [0..called_signature.arity[ do arguments.add "p{i}"
 
                if mmethoddef.is_intro and not recv.is_c_primitive then
                        var m = mmethoddef.mproperty
 
                if mmethoddef.is_intro and not recv.is_c_primitive then
                        var m = mmethoddef.mproperty
@@ -2250,7 +2515,7 @@ class SeparateRuntimeFunction
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
                        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(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
                        if ret != null then
                                v2.add "return {call}"
                        else
@@ -2267,7 +2532,7 @@ class SeparateRuntimeFunction
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
                        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(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
                        if ret != null then
                                v2.add "return {call}"
                        else
@@ -2279,10 +2544,22 @@ class SeparateRuntimeFunction
        end
 end
 
        end
 end
 
+class SeparateThunkFunction
+       super ThunkFunction
+       super SeparateRuntimeFunction
+       redef var target_recv
+end
+
 redef class MType
        # Are values of `self` tagged?
        # If false, it means that the type is not primitive, or is boxed.
        var is_tagged = false
 redef class MType
        # Are values of `self` tagged?
        # If false, it means that the type is not primitive, or is boxed.
        var is_tagged = false
+
+       # The tag value of the type
+       #
+       # ENSURE `is_tagged == (tag_value > 0)`
+       # ENSURE `not is_tagged == (tag_value == 0)`
+       var tag_value = 0
 end
 
 redef class MEntity
 end
 
 redef class MEntity
@@ -2308,3 +2585,14 @@ redef class AMethPropdef
                return super
        end
 end
                return super
        end
 end
+
+redef class AAttrPropdef
+       redef fun init_expr(v, recv)
+       do
+               super
+               if is_lazy and v.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+                       var guard = self.mlazypropdef.mproperty
+                       v.write_attribute(guard, recv, v.bool_instance(false))
+               end
+       end
+end