nitg & lib: intro `Finalizable` to be called when an object is freed
[nit.git] / src / separate_compiler.nit
index 714cff8..aab5468 100644 (file)
@@ -16,7 +16,7 @@
 module separate_compiler
 
 import abstract_compiler
-import layout_builders
+import coloring
 import rapid_type_analysis
 
 # Add separate compiler specific options
@@ -40,13 +40,7 @@ redef class ToolContext
        # --semi-global
        var opt_semi_global = new OptionBool("Enable all semi-global optimizations", "--semi-global")
        # --no-colo-dead-methods
-       var opt_no_colo_dead_methods = new OptionBool("Do not colorize dead methods", "--no-colo-dead-methods")
-       # --use-naive-coloring
-       var opt_bm_typing: OptionBool = new OptionBool("Colorize items incrementaly, used to simulate binary matrix typing", "--bm-typing")
-       # --use-mod-perfect-hashing
-       var opt_phmod_typing: OptionBool = new OptionBool("Replace coloration by perfect hashing (with mod operator)", "--phmod-typing")
-       # --use-and-perfect-hashing
-       var opt_phand_typing: OptionBool = new OptionBool("Replace coloration by perfect hashing (with and operator)", "--phand-typing")
+       var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods")
        # --tables-metrics
        var opt_tables_metrics: OptionBool = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
 
@@ -58,10 +52,7 @@ redef class ToolContext
                self.option_context.add_option(self.opt_no_union_attribute)
                self.option_context.add_option(self.opt_no_shortcut_equate)
                self.option_context.add_option(self.opt_inline_coloring_numbers, opt_inline_some_methods, opt_direct_call_monomorph, opt_skip_dead_methods, opt_semi_global)
-               self.option_context.add_option(self.opt_no_colo_dead_methods)
-               self.option_context.add_option(self.opt_bm_typing)
-               self.option_context.add_option(self.opt_phmod_typing)
-               self.option_context.add_option(self.opt_phand_typing)
+               self.option_context.add_option(self.opt_colo_dead_methods)
                self.option_context.add_option(self.opt_tables_metrics)
        end
 
@@ -77,6 +68,19 @@ redef class ToolContext
                        tc.opt_skip_dead_methods.value = true
                end
        end
+
+       var separate_compiler_phase = new SeparateCompilerPhase(self, null)
+end
+
+class SeparateCompilerPhase
+       super Phase
+       redef fun process_mainmodule(mainmodule, given_mmodules) do
+               if not toolcontext.opt_separate.value then return
+
+               var modelbuilder = toolcontext.modelbuilder
+               var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
+               modelbuilder.run_separate_compiler(mainmodule, analysis)
+       end
 end
 
 redef class ModelBuilder
@@ -101,7 +105,9 @@ redef class ModelBuilder
 
                # The main function of the C
                compiler.new_file("{mainmodule.name}.main")
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
+               compiler.compile_finalizer_function
 
                # compile methods
                for m in mainmodule.in_importation.greaters do
@@ -150,10 +156,11 @@ class SeparateCompiler
        private var undead_types: Set[MType] = new HashSet[MType]
        private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
 
-       private var type_layout: nullable Layout[MType]
-       private var resolution_layout: nullable Layout[MType]
-       protected var method_layout: nullable Layout[PropertyLayoutElement]
-       protected var attr_layout: nullable Layout[MAttribute]
+       private var type_ids: Map[MType, Int]
+       private var type_colors: Map[MType, Int]
+       private var opentype_colors: Map[MType, Int]
+       protected var method_colors: Map[PropertyLayoutElement, Int]
+       protected var attr_colors: Map[MAttribute, Int]
 
        init(mainmodule: MModule, mmbuilder: ModelBuilder, runtime_type_analysis: nullable RapidTypeAnalysis) do
                super(mainmodule, mmbuilder)
@@ -171,19 +178,7 @@ class SeparateCompiler
                # With resolution_table_table, all live type resolution are stored in a big table: resolution_table
                self.header.add_decl("struct type \{ int id; const char *name; int color; short int is_nullable; const struct types *resolution_table; int table_size; int type_table[]; \}; /* general C type representing a Nit type. */")
                self.header.add_decl("struct instance \{ const struct type *type; const struct class *class; nitattribute_t attrs[]; \}; /* general C type representing a Nit instance. */")
-
-               if modelbuilder.toolcontext.opt_phmod_typing.value or modelbuilder.toolcontext.opt_phand_typing.value then
-                       self.header.add_decl("struct types \{ int mask; const struct type *types[]; \}; /* a list types (used for vts, fts and unresolved lists). */")
-               else
-                       self.header.add_decl("struct types \{ int dummy; const struct type *types[]; \}; /* a list types (used for vts, fts and unresolved lists). */")
-               end
-
-               if modelbuilder.toolcontext.opt_phmod_typing.value then
-                       self.header.add_decl("#define HASH(mask, id) ((mask)%(id))")
-               else if modelbuilder.toolcontext.opt_phand_typing.value then
-                       self.header.add_decl("#define HASH(mask, id) ((mask)&(id))")
-               end
-
+               self.header.add_decl("struct types \{ int dummy; const struct type *types[]; \}; /* a list types (used for vts, fts and unresolved lists). */")
                self.header.add_decl("typedef struct instance val; /* general C type representing a Nit instance. */")
        end
 
@@ -266,30 +261,14 @@ class SeparateCompiler
 
        # colorize classe properties
        fun do_property_coloring do
-               var mclasses = new HashSet[MClass].from(modelbuilder.model.mclasses)
 
                var rta = runtime_type_analysis
 
                # Layouts
-               var method_layout_builder: PropertyLayoutBuilder[PropertyLayoutElement]
-               var attribute_layout_builder: PropertyLayoutBuilder[MAttribute]
-               #FIXME PH and BM layouts too slow for large programs
-               #if modelbuilder.toolcontext.opt_bm_typing.value then
-               #       method_layout_builder = new MMethodBMizer(self.mainmodule)
-               #       attribute_layout_builder = new MAttributeBMizer(self.mainmodule)
-               #else if modelbuilder.toolcontext.opt_phmod_typing.value then
-               #       method_layout_builder = new MMethodHasher(new PHModOperator, self.mainmodule)
-               #       attribute_layout_builder = new MAttributeHasher(new PHModOperator, self.mainmodule)
-               #else if modelbuilder.toolcontext.opt_phand_typing.value then
-               #       method_layout_builder = new MMethodHasher(new PHAndOperator, self.mainmodule)
-               #       attribute_layout_builder = new MAttributeHasher(new PHAndOperator, self.mainmodule)
-               #else
-
-               var class_layout_builder = new MClassColorer(self.mainmodule)
-               class_layout_builder.build_layout(mclasses)
-               method_layout_builder = new MPropertyColorer[PropertyLayoutElement](self.mainmodule, class_layout_builder)
-               attribute_layout_builder = new MPropertyColorer[MAttribute](self.mainmodule, class_layout_builder)
-               #end
+               var poset = mainmodule.flatten_mclass_hierarchy
+               var mclasses = new HashSet[MClass].from(poset)
+               var colorer = new POSetColorer[MClass]
+               colorer.colorize(poset)
 
                # The dead methods, still need to provide a dead color symbol
                var dead_methods = new Array[MMethod]
@@ -302,7 +281,7 @@ class SeparateCompiler
                        mattributes[mclass] = new HashSet[MAttribute]
                        for mprop in self.mainmodule.properties(mclass) do
                                if mprop isa MMethod then
-                                       if modelbuilder.toolcontext.opt_no_colo_dead_methods.value and rta != null and not rta.live_methods.has(mprop) 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
@@ -343,9 +322,10 @@ class SeparateCompiler
                end
 
                # methods coloration
-               self.method_layout = method_layout_builder.build_layout(mmethods)
-               self.method_tables = build_method_tables(mclasses, super_calls)
-               self.compile_color_consts(method_layout.pos)
+               var meth_colorer = new POSetBucketsColorer[MClass, PropertyLayoutElement](poset, colorer.conflicts)
+               method_colors = meth_colorer.colorize(mmethods)
+               method_tables = build_method_tables(mclasses, super_calls)
+               compile_color_consts(method_colors)
 
                # attribute null color to dead methods and supercalls
                for mproperty in dead_methods do
@@ -357,168 +337,93 @@ class SeparateCompiler
                end
 
                # attributes coloration
-               self.attr_layout = attribute_layout_builder.build_layout(mattributes)
-               self.attr_tables = build_attr_tables(mclasses)
-               self.compile_color_consts(attr_layout.pos)
+               var attr_colorer = new POSetBucketsColorer[MClass, MAttribute](poset, colorer.conflicts)
+               attr_colors = attr_colorer.colorize(mattributes)
+               attr_tables = build_attr_tables(mclasses)
+               compile_color_consts(attr_colors)
        end
 
        fun build_method_tables(mclasses: Set[MClass], super_calls: Set[MMethodDef]): Map[MClass, Array[nullable MPropDef]] do
-               var layout = self.method_layout
                var tables = new HashMap[MClass, Array[nullable MPropDef]]
                for mclass in mclasses do
                        var table = new Array[nullable MPropDef]
-                       var supercalls = new List[MMethodDef]
-
-                       # first, fill table from parents by reverse linearization order
-                       var parents = new Array[MClass]
-                       if mainmodule.flatten_mclass_hierarchy.has(mclass) then
-                               parents = mclass.in_hierarchy(mainmodule).greaters.to_a
-                               self.mainmodule.linearize_mclasses(parents)
-                       end
-
-                       for parent in parents do
-                               if parent == mclass then continue
-                               for mproperty in self.mainmodule.properties(parent) do
-                                       if not mproperty isa MMethod then continue
-                                       if not layout.pos.has_key(mproperty) then continue
-                                       var color = layout.pos[mproperty]
-                                       if table.length <= color then
-                                               for i in [table.length .. color[ do
-                                                       table[i] = null
-                                               end
-                                       end
-                                       for mpropdef in mproperty.mpropdefs do
-                                               if mpropdef.mclassdef.mclass == parent then
-                                                       table[color] = mpropdef
-                                               end
-                                       end
-                               end
+                       tables[mclass] = table
 
-                               # lookup for super calls in super classes
-                               for mmethoddef in super_calls do
-                                       for mclassdef in parent.mclassdefs do
-                                               if mclassdef.mpropdefs.has(mmethoddef) then
-                                                       supercalls.add(mmethoddef)
-                                               end
-                                       end
-                               end
-                       end
+                       var mproperties = self.mainmodule.properties(mclass)
+                       var mtype = mclass.intro.bound_mtype
 
-                       # then override with local properties
-                       for mproperty in self.mainmodule.properties(mclass) do
+                       for mproperty in mproperties do
                                if not mproperty isa MMethod then continue
-                               if not layout.pos.has_key(mproperty) then continue
-                               var color = layout.pos[mproperty]
+                               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
-                               for mpropdef in mproperty.mpropdefs do
-                                       if mpropdef.mclassdef.mclass == mclass then
-                                               table[color] = mpropdef
-                                       end
-                               end
+                               table[color] = mproperty.lookup_first_definition(mainmodule, mtype)
                        end
 
-                       # lookup for super calls in local class
-                       for mmethoddef in super_calls do
-                               for mclassdef in mclass.mclassdefs do
-                                       if mclassdef.mpropdefs.has(mmethoddef) then
-                                               supercalls.add(mmethoddef)
-                                       end
-                               end
-                       end
-                       # insert super calls in table according to receiver
-                       for supercall in supercalls do
-                               var color = layout.pos[supercall]
+                       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
                                end
-                               var mmethoddef = supercall.lookup_next_definition(self.mainmodule, mclass.intro.bound_mtype)
+                               var mmethoddef = supercall.lookup_next_definition(mainmodule, mtype)
                                table[color] = mmethoddef
                        end
-                       tables[mclass] = table
+
                end
                return tables
        end
 
        fun build_attr_tables(mclasses: Set[MClass]): Map[MClass, Array[nullable MPropDef]] do
-               var layout = self.attr_layout
                var tables = new HashMap[MClass, Array[nullable MPropDef]]
                for mclass in mclasses do
                        var table = new Array[nullable MPropDef]
-                       # first, fill table from parents by reverse linearization order
-                       var parents = new Array[MClass]
-                       if mainmodule.flatten_mclass_hierarchy.has(mclass) then
-                               parents = mclass.in_hierarchy(mainmodule).greaters.to_a
-                               self.mainmodule.linearize_mclasses(parents)
-                       end
-                       for parent in parents do
-                               if parent == mclass then continue
-                               for mproperty in self.mainmodule.properties(parent) do
-                                       if not mproperty isa MAttribute then continue
-                                       var color = layout.pos[mproperty]
-                                       if table.length <= color then
-                                               for i in [table.length .. color[ do
-                                                       table[i] = null
-                                               end
-                                       end
-                                       for mpropdef in mproperty.mpropdefs do
-                                               if mpropdef.mclassdef.mclass == parent then
-                                                       table[color] = mpropdef
-                                               end
-                                       end
-                               end
-                       end
+                       tables[mclass] = table
+
+                       var mproperties = self.mainmodule.properties(mclass)
+                       var mtype = mclass.intro.bound_mtype
 
-                       # then override with local properties
-                       for mproperty in self.mainmodule.properties(mclass) do
+                       for mproperty in mproperties do
                                if not mproperty isa MAttribute then continue
-                               var color = layout.pos[mproperty]
+                               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
-                               for mpropdef in mproperty.mpropdefs do
-                                       if mpropdef.mclassdef.mclass == mclass then
-                                               table[color] = mpropdef
-                                       end
-                               end
+                               table[color] = mproperty.lookup_first_definition(mainmodule, mtype)
                        end
-                       tables[mclass] = table
                end
                return tables
        end
 
        # colorize live types of the program
        private fun do_type_coloring: POSet[MType] do
+               # Collect types to colorize
+               var live_types = runtime_type_analysis.live_types
+               var live_cast_types = runtime_type_analysis.live_cast_types
                var mtypes = new HashSet[MType]
-               mtypes.add_all(self.runtime_type_analysis.live_types)
-               mtypes.add_all(self.runtime_type_analysis.live_cast_types)
+               mtypes.add_all(live_types)
+               mtypes.add_all(live_cast_types)
                for c in self.box_kinds.keys do
                        mtypes.add(c.mclass_type)
                end
 
-               # Typing Layout
-               var layout_builder: TypingLayoutBuilder[MType]
-               if modelbuilder.toolcontext.opt_bm_typing.value then
-                       layout_builder = new MTypeBMizer(self.mainmodule)
-               else if modelbuilder.toolcontext.opt_phmod_typing.value then
-                       layout_builder = new MTypeHasher(new PHModOperator, self.mainmodule)
-               else if modelbuilder.toolcontext.opt_phand_typing.value then
-                       layout_builder = new MTypeHasher(new PHAndOperator, self.mainmodule)
-               else
-                       layout_builder = new MTypeColorer(self.mainmodule)
-               end
-
-               # colorize types
-               self.type_layout = layout_builder.build_layout(mtypes)
-               var poset = layout_builder.poset.as(not null)
-               self.type_tables = self.build_type_tables(poset)
+               # Compute colors
+               var poset = poset_from_mtypes(mtypes)
+               var colorer = new POSetColorer[MType]
+               colorer.colorize(poset)
+               type_ids = colorer.ids
+               type_colors = colorer.colors
+               type_tables = build_type_tables(poset)
 
                # VT and FT are stored with other unresolved types in the big resolution_tables
                self.compile_resolution_tables(mtypes)
@@ -526,19 +431,27 @@ class SeparateCompiler
                return poset
        end
 
+       private fun poset_from_mtypes(mtypes: Set[MType]): POSet[MType] do
+               var poset = new POSet[MType]
+               for e in mtypes do
+                       poset.add_node(e)
+                       for o in mtypes do
+                               if e == o then continue
+                               if e.is_subtype(mainmodule, null, o) then
+                                       poset.add_edge(e, o)
+                               end
+                       end
+               end
+               return poset
+       end
+
        # Build type tables
        fun build_type_tables(mtypes: POSet[MType]): Map[MType, Array[nullable MType]] do
                var tables = new HashMap[MType, Array[nullable MType]]
-               var layout = self.type_layout
                for mtype in mtypes do
                        var table = new Array[nullable MType]
                        for sup in mtypes[mtype].greaters do
-                               var color: Int
-                               if layout isa PHLayout[MType, MType] then
-                                       color = layout.hashes[mtype][sup]
-                               else
-                                       color = layout.pos[sup]
-                               end
+                               var color = type_colors[sup]
                                if table.length <= color then
                                        for i in [table.length .. color[ do
                                                table[i] = null
@@ -571,18 +484,9 @@ class SeparateCompiler
                end
 
                # Compute the table layout with the prefered method
-               var resolution_builder: ResolutionLayoutBuilder
-               if modelbuilder.toolcontext.opt_bm_typing.value then
-                       resolution_builder = new ResolutionBMizer
-               else if modelbuilder.toolcontext.opt_phmod_typing.value then
-                       resolution_builder = new ResolutionHasher(new PHModOperator)
-               else if modelbuilder.toolcontext.opt_phand_typing.value then
-                       resolution_builder = new ResolutionHasher(new PHAndOperator)
-               else
-                       resolution_builder = new ResolutionColorer
-               end
-               self.resolution_layout = resolution_builder.build_layout(mtype2unresolved)
-               self.resolution_tables = self.build_resolution_tables(mtype2unresolved)
+               var colorer = new BucketsColorer[MType, MType]
+               opentype_colors = colorer.colorize(mtype2unresolved)
+               resolution_tables = self.build_resolution_tables(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)
@@ -592,8 +496,8 @@ class SeparateCompiler
                end
                var all_unresolved_types_colors = new HashMap[MType, Int]
                for t in all_unresolved do
-                       if self.resolution_layout.pos.has_key(t) then
-                               all_unresolved_types_colors[t] = self.resolution_layout.pos[t]
+                       if opentype_colors.has_key(t) then
+                               all_unresolved_types_colors[t] = opentype_colors[t]
                        else
                                all_unresolved_types_colors[t] = -1
                        end
@@ -609,16 +513,10 @@ class SeparateCompiler
 
        fun build_resolution_tables(elements: Map[MClassType, Set[MType]]): Map[MClassType, Array[nullable MType]] do
                var tables = new HashMap[MClassType, Array[nullable MType]]
-               var layout = self.resolution_layout
                for mclasstype, mtypes in elements do
                        var table = new Array[nullable MType]
                        for mtype in mtypes do
-                               var color: Int
-                               if layout isa PHLayout[MClassType, MType] then
-                                       color = layout.hashes[mclasstype][mtype]
-                               else
-                                       color = layout.pos[mtype]
-                               end
+                               var color = opentype_colors[mtype]
                                if table.length <= color then
                                        for i in [table.length .. color[ do
                                                table[i] = null
@@ -655,7 +553,6 @@ class SeparateCompiler
        fun compile_type_to_c(mtype: MType)
        do
                assert not mtype.need_anchor
-               var layout = self.type_layout
                var is_live = mtype isa MClassType and runtime_type_analysis.live_types.has(mtype)
                var is_cast_live = runtime_type_analysis.live_cast_types.has(mtype)
                var c_name = mtype.c_name
@@ -670,7 +567,7 @@ class SeparateCompiler
 
                # type id (for cast target)
                if is_cast_live then
-                       v.add_decl("{layout.ids[mtype]},")
+                       v.add_decl("{type_ids[mtype]},")
                else
                        v.add_decl("-1, /*CAST DEAD*/")
                end
@@ -680,11 +577,7 @@ class SeparateCompiler
 
                # type color (for cast target)
                if is_cast_live then
-                       if layout isa PHLayout[MType, MType] then
-                               v.add_decl("{layout.masks[mtype]},")
-                       else
-                               v.add_decl("{layout.pos[mtype]},")
-                       end
+                       v.add_decl("{type_colors[mtype]},")
                else
                        v.add_decl("-1, /*CAST DEAD*/")
                end
@@ -698,8 +591,7 @@ class SeparateCompiler
 
                # resolution table (for receiver)
                if is_live then
-                       var mclass_type = mtype
-                       if mclass_type isa MNullableType then mclass_type = mclass_type.mtype
+                       var mclass_type = mtype.as_notnullable
                        assert mclass_type isa MClassType
                        if resolution_tables[mclass_type].is_empty then
                                v.add_decl("NULL, /*NO RESOLUTIONS*/")
@@ -720,7 +612,7 @@ class SeparateCompiler
                                if stype == null then
                                        v.add_decl("-1, /* empty */")
                                else
-                                       v.add_decl("{layout.ids[stype]}, /* {stype} */")
+                                       v.add_decl("{type_ids[stype]}, /* {stype} */")
                                end
                        end
                        v.add_decl("\},")
@@ -732,14 +624,7 @@ class SeparateCompiler
 
        fun compile_type_resolution_table(mtype: MType) do
 
-               var mclass_type: MClassType
-               if mtype isa MNullableType then
-                       mclass_type = mtype.mtype.as(MClassType)
-               else
-                       mclass_type = mtype.as(MClassType)
-               end
-
-               var layout = self.resolution_layout
+               var mclass_type = mtype.as_notnullable.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};")
@@ -747,11 +632,7 @@ class SeparateCompiler
                # const struct fts_table_X fts_table_X
                var v = new_visitor
                v.add_decl("const struct types resolution_table_{mtype.c_name} = \{")
-               if layout isa PHLayout[MClassType, MType] then
-                       v.add_decl("{layout.masks[mclass_type]},")
-               else
-                       v.add_decl("0, /* dummy */")
-               end
+               v.add_decl("0, /* dummy */")
                v.add_decl("\{")
                for t in self.resolution_tables[mclass_type] do
                        if t == null then
@@ -762,7 +643,7 @@ class SeparateCompiler
                                # the value stored is tv.
                                var tv = t.resolve_for(mclass_type, mclass_type, self.mainmodule, true)
                                # FIXME: What typeids means here? How can a tv not be live?
-                               if self.type_layout.ids.has_key(tv) then
+                               if type_ids.has_key(tv) then
                                        v.require_declaration("type_{tv.c_name}")
                                        v.add_decl("&type_{tv.c_name}, /* {t}: {tv} */")
                                else
@@ -885,6 +766,7 @@ class SeparateCompiler
                        v.require_declaration("class_{c_name}")
                        v.add("{res}->class = &class_{c_name};")
                        self.generate_init_attr(v, res, mtype)
+                       v.set_finalizer res
                        v.add("return {res};")
                end
                v.add("\}")
@@ -898,7 +780,7 @@ class SeparateCompiler
                v.add_abort("type null")
                v.add("\}")
                v.add("if({t}->table_size == 0) \{")
-               v.add("fprintf(stderr, \"Insantiation of a dead type: %s\\n\", {t}->name);")
+               v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
                v.add_abort("type dead")
                v.add("\}")
        end
@@ -918,7 +800,9 @@ class SeparateCompiler
                if self.modelbuilder.toolcontext.opt_tables_metrics.value then
                        display_sizes
                end
-
+               if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+                       display_isset_checks
+               end
                var tc = self.modelbuilder.toolcontext
                tc.info("# implementation of method invocation",2)
                var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
@@ -971,11 +855,29 @@ class SeparateCompiler
                print "\t{total}\t{holes}"
        end
 
+       protected var isset_checks_count = 0
+       protected var attr_read_count = 0
+
+       fun display_isset_checks do
+               print "# total number of compiled attribute reads"
+               print "\t{attr_read_count}"
+               print "# total number of compiled isset-checks"
+               print "\t{isset_checks_count}"
+       end
+
        redef fun compile_nitni_structs
        do
-               self.header.add_decl("struct nitni_instance \{struct instance *value;\};")
+               self.header.add_decl """
+struct nitni_instance \{
+       struct nitni_instance *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+       struct instance *value;
+\};
+"""
+               super
        end
-       
+
        redef fun finalize_ffi_for_module(mmodule)
        do
                var old_module = self.mainmodule
@@ -1020,19 +922,21 @@ class SeparateCompilerVisitor
                        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("printf(\"Dead code executed!\\n\"); show_backtrace(1);")
+                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
                                return res
                        end
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
-               else if value.mtype.ctype == "void*" and mtype.ctype == "void*" then
+               else if (value.mtype.ctype == "void*" and mtype.ctype == "void*") or
+                       (value.mtype.ctype == "char*" and mtype.ctype == "void*") or
+                       (value.mtype.ctype == "void*" and mtype.ctype == "char*") then
                        return value
                else
                        # Bad things will appen!
                        var res = self.new_var(mtype)
                        self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
-                       self.add("printf(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
+                       self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
                        return res
                end
        end
@@ -1334,6 +1238,11 @@ class SeparateCompilerVisitor
                var intromclassdef = a.intro.mclassdef
                ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
 
+               if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+                       self.compiler.attr_read_count += 1
+                       self.add("count_attr_reads++;")
+               end
+
                self.require_declaration(a.const_color)
                if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
                        # Get the attribute or a box (ie. always a val*)
@@ -1344,10 +1253,15 @@ class SeparateCompilerVisitor
                        self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
 
                        # Check for Uninitialized attribute
-                       if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+                       if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
                                self.add("if (unlikely({res} == NULL)) \{")
                                self.add_abort("Uninitialized attribute {a.name}")
                                self.add("\}")
+
+                               if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+                                       self.compiler.isset_checks_count += 1
+                                       self.add("count_isset_checks++;")
+                               end
                        end
 
                        # Return the attribute or its unboxed version
@@ -1358,10 +1272,14 @@ class SeparateCompilerVisitor
                        self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
 
                        # Check for Uninitialized attribute
-                       if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+                       if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
                                self.add("if (unlikely({res} == NULL)) \{")
                                self.add_abort("Uninitialized attribute {a.name}")
                                self.add("\}")
+                               if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+                                       self.compiler.isset_checks_count += 1
+                                       self.add("count_isset_checks++;")
+                               end
                        end
 
                        return res
@@ -1409,7 +1327,7 @@ class SeparateCompilerVisitor
                self.require_declaration(mtype.const_color)
                var col = mtype.const_color
                self.add("if({col} == -1) \{")
-               self.add("fprintf(stderr, \"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
+               self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
                self.add_abort("open type dead")
                self.add("\}")
        end
@@ -1422,7 +1340,7 @@ class SeparateCompilerVisitor
                add_abort("cast type null")
                add("\}")
                add("if({t}->id == -1 || {t}->color == -1) \{")
-               add("fprintf(stderr, \"Try to cast on a dead cast type: %s\\n\", {t}->name);")
+               add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
                add_abort("cast type dead")
                add("\}")
        end
@@ -1437,11 +1355,7 @@ class SeparateCompilerVisitor
                        var recv = self.frame.arguments.first
                        var recv_type_info = self.type_info(recv)
                        self.require_declaration(mtype.const_color)
-                       if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
-                               return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[HASH({recv_type_info}->resolution_table->mask, {mtype.const_color})])", mtype)
-                       else
-                               return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
-                       end
+                       return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
                end
                compiler.undead_types.add(mtype)
                self.require_declaration("type_{mtype.c_name}")
@@ -1488,11 +1402,7 @@ class SeparateCompilerVisitor
                        hardening_live_open_type(mtype)
                        link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
                        self.require_declaration(mtype.const_color)
-                       if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
-                               self.add("{type_struct} = {recv_type_info}->resolution_table->types[HASH({recv_type_info}->resolution_table->mask, {mtype.const_color})];")
-                       else
-                               self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
-                       end
+                       self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
                        if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
                                self.compiler.count_type_test_unresolved[tag] += 1
                                self.add("count_type_test_unresolved_{tag}++;")
@@ -1517,7 +1427,7 @@ class SeparateCompilerVisitor
                                self.add("count_type_test_resolved_{tag}++;")
                        end
                else
-                       self.add("printf(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);")
+                       self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);")
                end
 
                # check color is in table
@@ -1527,9 +1437,6 @@ class SeparateCompilerVisitor
                        self.add("\} else \{")
                end
                var value_type_info = self.type_info(value)
-               if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
-                       self.add("{cltype} = HASH({value_type_info}->color, {idtype});")
-               end
                self.add("if({cltype} >= {value_type_info}->table_size) \{")
                self.add("{res} = 0;")
                self.add("\} else \{")
@@ -1674,8 +1581,7 @@ class SeparateCompilerVisitor
 
        fun can_be_primitive(value: RuntimeVariable): Bool
        do
-               var t = value.mcasttype
-               if t isa MNullableType then t = t.mtype
+               var t = value.mcasttype.as_notnullable
                if not t isa MClassType then return false
                var k = t.mclass.kind
                return k == interface_kind or t.ctype != "val*"
@@ -1705,7 +1611,7 @@ class SeparateCompilerVisitor
                return res
        end
 
-       fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
+       redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
        do
                var mtype = self.get_class("NativeArray").get_mtype([elttype])
                self.require_declaration("NEW_{mtype.mclass.c_name}")
@@ -1717,11 +1623,7 @@ class SeparateCompilerVisitor
                        var recv = self.frame.arguments.first
                        var recv_type_info = self.type_info(recv)
                        self.require_declaration(mtype.const_color)
-                       if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
-                               return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[HASH({recv_type_info}->resolution_table->mask, {mtype.const_color})])", mtype)
-                       else
-                               return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
-                       end
+                       return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
                end
                compiler.undead_types.add(mtype)
                self.require_declaration("type_{mtype.c_name}")
@@ -1967,10 +1869,14 @@ redef class MClass
        end
 end
 
+interface PropertyLayoutElement end
+
 redef class MProperty
+       super PropertyLayoutElement
        fun const_color: String do return "COLOR_{c_name}"
 end
 
 redef class MPropDef
+       super PropertyLayoutElement
        fun const_color: String do return "COLOR_{c_name}"
 end