model: rename `as_notnullable` to `undecorate`
[nit.git] / src / compiler / separate_compiler.nit
index 93508a9..34e7be4 100644 (file)
@@ -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")
+       # --type-poset
+       var opt_type_poset = new OptionBool("Build a poset of types to create more condensed tables.", "--type-poset")
 
        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_type_poset)
        end
 
        redef fun process_options(args)
@@ -146,8 +149,6 @@ 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
-       protected var method_colors: Map[PropertyLayoutElement, Int] is noinit
-       protected var attr_colors: Map[MAttribute, Int] is noinit
 
        init do
                var file = new_file("nit.common")
@@ -268,7 +269,7 @@ class SeparateCompiler
                if mclass.mclass_type.ctype_extern == "val*" then
                        return 0
                else if mclass.kind == extern_kind and mclass.name != "NativeString" then
-                       return self.box_kinds[self.mainmodule.get_primitive_class("Pointer")]
+                       return self.box_kinds[self.mainmodule.pointer_type.mclass]
                else
                        return self.box_kinds[mclass]
                end
@@ -305,170 +306,147 @@ class SeparateCompiler
 
        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
 
-               # Layouts
-               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]
+               # Class graph
+               var mclasses = mainmodule.flatten_mclass_hierarchy
+               class_conflict_graph = mclasses.to_conflict_graph
 
-               # 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]]
+
+               # 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 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
 
-               # 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 ad 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
+                               else if p isa MAttribute then
+                                       mattributes[mclass].add p
                                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
-               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)
 
-               # 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)
-       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
-                       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
 
-                       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
-                               var mmethoddef = supercall.lookup_next_definition(mainmodule, mtype)
-                               table[color] = mmethoddef
                        end
 
+                       # Do not need to resolve attributes as only the position is used
+                       attr_tables[mclass] = attr_colorer.build_layout(mclass)
                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
-       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
 
-               # 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.compile_resolution_tables(live_types)
+               self.compute_resolution_tables(live_types)
 
-               return poset
+               return res
        end
 
        private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
@@ -480,14 +458,14 @@ class SeparateCompiler
 
                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
-                       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
@@ -527,29 +505,75 @@ class SeparateCompiler
                return tables
        end
 
-       protected fun compile_resolution_tables(mtypes: Set[MType]) do
-               # resolution_tables is used to perform a type resolution at runtime in O(1)
 
+       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
                # and associated to
                # 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
-                       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
-                       mtype2unresolved[mtype] = set
                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)
-               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)
@@ -574,9 +598,10 @@ class SeparateCompiler
                #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]]
-               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]
@@ -717,7 +742,7 @@ class SeparateCompiler
 
                # 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*/")
@@ -750,7 +775,7 @@ class SeparateCompiler
 
        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};")
@@ -789,12 +814,10 @@ class SeparateCompiler
                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 is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" and mclass.name != "Pointer"
+               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"
 
                v.add_decl("/* runtime class {c_name} */")
 
@@ -804,7 +827,8 @@ class SeparateCompiler
                        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 */")
@@ -823,7 +847,7 @@ class SeparateCompiler
                        v.add_decl("\};")
                end
 
-               if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
+               if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
                        # Is a primitive type or the Pointer class, not any other extern class
 
                        if mtype.is_tagged then return
@@ -933,13 +957,20 @@ class SeparateCompiler
                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
+                               v.add("{res} = nit_alloc(sizeof(struct instance));")
+                       else
+                               v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
+                       end
                        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("\}")
@@ -1018,7 +1049,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]]
-       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
@@ -1154,9 +1185,9 @@ class SeparateCompilerVisitor
        do
                if value.mtype == mtype then
                        return value
-               else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive and not mtype.is_c_primitive then
                        return value
-               else if value.mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive then
                        if mtype.is_tagged then
                                if mtype.name == "Int" then
                                        return self.new_expr("(long)({value})>>2", mtype)
@@ -1169,7 +1200,7 @@ class SeparateCompilerVisitor
                                end
                        end
                        return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
-               else if mtype.ctype == "val*" then
+               else if not mtype.is_c_primitive then
                        if value.mtype.is_tagged then
                                if value.mtype.name == "Int" then
                                        return self.new_expr("(val*)({value}<<2|1)", mtype)
@@ -1188,7 +1219,7 @@ 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("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
+                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
                                return res
                        end
                        self.require_declaration("BOX_{valtype.c_name}")
@@ -1202,7 +1233,7 @@ class SeparateCompilerVisitor
                        # Bad things will appen!
                        var res = self.new_var(mtype)
                        self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
-                       self.add("PRINT_ERROR(\"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}\"); fatal_exit(1);")
                        return res
                end
        end
@@ -1228,7 +1259,7 @@ class SeparateCompilerVisitor
                        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\"); show_backtrace(1);")
+                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
                                return res
                        end
                        self.require_declaration("BOX_{valtype.c_name}")
@@ -1249,7 +1280,7 @@ class SeparateCompilerVisitor
        # Thus the expression can be used as a condition.
        fun extract_tag(value: RuntimeVariable): String
        do
-               assert value.mtype.ctype == "val*"
+               assert not value.mtype.is_c_primitive
                return "((long){value}&3)" # Get the two low bits
        end
 
@@ -1257,7 +1288,7 @@ class SeparateCompilerVisitor
        # The point of the method is to work also with primitive types.
        fun class_info(value: RuntimeVariable): String
        do
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
                                var tag = extract_tag(value)
                                return "({tag}?class_info[{tag}]:{value}->class)"
@@ -1274,7 +1305,7 @@ class SeparateCompilerVisitor
        # The point of the method is to work also with primitive types.
        fun type_info(value: RuntimeVariable): String
        do
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
                                var tag = extract_tag(value)
                                return "({tag}?type_info[{tag}]:{value}->type)"
@@ -1323,7 +1354,7 @@ class SeparateCompilerVisitor
        end
        redef fun send(mmethod, arguments)
        do
-               if arguments.first.mcasttype.ctype != "val*" then
+               if arguments.first.mcasttype.is_c_primitive then
                        # In order to shortcut the primitive, we need to find the most specific method
                        # Howverr, because of performance (no flattening), we always work on the realmainmodule
                        var m = self.compiler.mainmodule
@@ -1530,7 +1561,7 @@ class SeparateCompilerVisitor
 
        redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
-               if arguments.first.mcasttype.ctype != "val*" then
+               if arguments.first.mcasttype.is_c_primitive then
                        # In order to shortcut the primitive, we need to find the most specific method
                        # However, because of performance (no flattening), we always work on the realmainmodule
                        var main = self.compiler.mainmodule
@@ -1581,7 +1612,7 @@ class SeparateCompilerVisitor
                        self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
                else
 
-                       if mtype.ctype == "val*" then
+                       if not mtype.is_c_primitive and not mtype.is_tagged then
                                self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
                        else
                                self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
@@ -1633,7 +1664,7 @@ 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_attr_isset.value then
+                       if not ret.is_c_primitive 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("\}")
@@ -1662,7 +1693,11 @@ class SeparateCompilerVisitor
                self.require_declaration(a.const_color)
                if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
                        var attr = "{recv}->attrs[{a.const_color}]"
-                       if mtype.ctype != "val*" then
+                       if mtype.is_tagged then
+                               # The attribute is not primitive, thus store it as tagged
+                               var tv = autobox(value, compiler.mainmodule.object_type)
+                               self.add("{attr} = {tv}; /* {a} on {recv.inspect} */")
+                       else if mtype.is_c_primitive then
                                assert mtype isa MClassType
                                # The attribute is primitive, thus we store it in a box
                                # The trick is to create the box the first time then resuse the box
@@ -1788,7 +1823,7 @@ class SeparateCompilerVisitor
                                self.add("count_type_test_resolved_{tag}++;")
                        end
                else
-                       self.add("PRINT_ERROR(\"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}\"); fatal_exit(1);")
                end
 
                # check color is in table
@@ -1814,15 +1849,15 @@ class SeparateCompilerVisitor
        do
                var res = self.new_var(bool_type)
                # Swap values to be symetric
-               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+               if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
                        var tmp = value1
                        value1 = value2
                        value2 = tmp
                end
-               if value1.mtype.ctype != "val*" then
+               if value1.mtype.is_c_primitive then
                        if value2.mtype == value1.mtype then
                                self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
-                       else if value2.mtype.ctype != "val*" then
+                       else if value2.mtype.is_c_primitive then
                                self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
                        else
                                var mtype1 = value1.mtype.as(MClassType)
@@ -1839,7 +1874,7 @@ class SeparateCompilerVisitor
        do
                var res = self.get_name("var_class_name")
                self.add_decl("const char* {res};")
-               if value.mtype.ctype == "val*" then
+               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
@@ -1854,15 +1889,15 @@ class SeparateCompilerVisitor
        redef fun equal_test(value1, value2)
        do
                var res = self.new_var(bool_type)
-               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+               if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
                        var tmp = value1
                        value1 = value2
                        value2 = tmp
                end
-               if value1.mtype.ctype != "val*" then
+               if value1.mtype.is_c_primitive then
                        if value2.mtype == value1.mtype then
                                self.add("{res} = {value1} == {value2};")
-                       else if value2.mtype.ctype != "val*" then
+                       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});")
@@ -1895,11 +1930,11 @@ class SeparateCompilerVisitor
 
                var incompatible = false
                var primitive
-               if t1.ctype != "val*" then
+               if t1.is_c_primitive then
                        primitive = t1
                        if t1 == t2 then
                                # No need to compare class
-                       else if t2.ctype != "val*" then
+                       else if t2.is_c_primitive then
                                incompatible = true
                        else if can_be_primitive(value2) then
                                if t1.is_tagged then
@@ -1913,7 +1948,7 @@ class SeparateCompilerVisitor
                        else
                                incompatible = true
                        end
-               else if t2.ctype != "val*" then
+               else if t2.is_c_primitive then
                        primitive = t2
                        if can_be_primitive(value1) then
                                if t2.is_tagged then
@@ -1971,10 +2006,10 @@ class SeparateCompilerVisitor
 
        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.ctype != "val*"
+               return k == interface_kind or t.is_c_primitive
        end
 
        fun maybe_null(value: RuntimeVariable): Bool
@@ -1985,8 +2020,8 @@ class SeparateCompilerVisitor
 
        redef fun array_instance(array, elttype)
        do
-               var nclass = self.get_class("NativeArray")
-               var arrayclass = self.get_class("Array")
+               var nclass = mmodule.native_array_class
+               var arrayclass = mmodule.array_class
                var arraytype = arrayclass.get_mtype([elttype])
                var res = self.init_instance(arraytype)
                self.add("\{ /* {res} = array_instance Array[{elttype}] */")
@@ -2003,7 +2038,7 @@ class SeparateCompilerVisitor
 
        redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
        do
-               var mtype = self.get_class("NativeArray").get_mtype([elttype])
+               var mtype = mmodule.native_array_type(elttype)
                self.require_declaration("NEW_{mtype.mclass.c_name}")
                assert mtype isa MGenericType
                var compiler = self.compiler
@@ -2023,7 +2058,7 @@ class SeparateCompilerVisitor
        redef fun native_array_def(pname, ret_type, arguments)
        do
                var elttype = arguments.first.mtype
-               var nclass = self.get_class("NativeArray")
+               var nclass = mmodule.native_array_class
                var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
                if pname == "[]" then
                        # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
@@ -2044,12 +2079,20 @@ class SeparateCompilerVisitor
                end
        end
 
-       redef fun calloc_array(ret_type, arguments)
+       redef fun native_array_get(nat, i)
        do
-               var mclass = self.get_class("ArrayCapable")
-               var ft = mclass.mparameters.first
-               var res = self.native_array_instance(ft, arguments[1])
-               self.ret(res)
+               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
 
        fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
@@ -2162,7 +2205,7 @@ class SeparateRuntimeFunction
                for i in [0..called_signature.arity[ do
                        var mtype = called_signature.mparameters[i].mtype
                        if i == called_signature.vararg_rank then
-                               mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
+                               mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
                        end
                        sig.append(", {mtype.ctype} p{i}")
                end
@@ -2201,7 +2244,7 @@ class SeparateRuntimeFunction
                for i in [0..msignature.arity[ do
                        var mtype = msignature.mparameters[i].mtype
                        if i == msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
+                               mtype = v.mmodule.array_type(mtype)
                        end
                        comment.append(", {mtype}")
                        var argvar = new RuntimeVariable("p{i}", mtype, mtype)
@@ -2248,14 +2291,14 @@ class SeparateRuntimeFunction
                var selfvar = arguments.first
                var ret = called_signature.return_mtype
 
-               if mmethoddef.is_intro and recv.ctype == "val*" then
+               if mmethoddef.is_intro and not recv.is_c_primitive then
                        var m = mmethoddef.mproperty
                        var n2 = "CALL_" + m.const_color
                        compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
-                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
@@ -2265,14 +2308,14 @@ class SeparateRuntimeFunction
                        v2.add "\}"
 
                end
-               if mmethoddef.has_supercall and recv.ctype == "val*" then
+               if mmethoddef.has_supercall and not recv.is_c_primitive then
                        var m = mmethoddef
                        var n2 = "CALL_" + m.const_color
                        compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
-                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
@@ -2313,3 +2356,14 @@ redef class AMethPropdef
                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