Merge: Rewrite the coloration for properties and types.
authorJean Privat <jean@pryen.org>
Tue, 31 Mar 2015 03:21:57 +0000 (10:21 +0700)
committerJean Privat <jean@pryen.org>
Tue, 31 Mar 2015 03:21:57 +0000 (10:21 +0700)
This introduce a new colorer, `POSetGroupColorer` that colors elements introduced by the classes in a class-hierarchy.
The advantage of this new colorer is that it uses conflict graphs of classes to colorize elements.
By comparison, the previously used colorers work with conflict graphs of elements; that are therefore much larger.

An other advantage is that only the introductions of elements are needed by the colorer, so filling the information from the model is far more easier.

The construction of the poset of types is also removed.
Instead, subtyping tables are computed with a more standard way:

* target cast types are grouped by classes: a map class->types is created
* the map is colored: a table layout by class is computed
* for each live type, the table layout of the associated class is used to build the type table

Results are so good that most of the time of the coloring is removed.

For nitc/nitc/nit

Before:

user: 0m6.044s
total: 15096 MIr
do_property_coloring: 1420 MIr
do_type_coloring: 2600 MIr

After:

user: 0m5.608s (-7%)
total: 12800 MIr (-15%)
do_property_coloring: 452 MIr (-68%)
do_type_coloring: 895 MIr (-65%)

note that in the previous numbers, most of the time is done in the model to inherit or resolve things.
Pure coloring algorithm is now negligible:

conflict_graph: 34 MIr (<1% of the total Ir)
coloring: 60 MIr (<1% of the total Ir)

Unfortunately, with types the coloring can degenerate and produce big tables. If this is an issue, the options `--type-poset` use the previous coloring method for types

Pull-Request: #1215
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

1  2 
src/compiler/abstract_compiler.nit
src/compiler/separate_compiler.nit
src/compiler/separate_erasure_compiler.nit
src/model/model.nit

@@@ -23,6 -23,7 +23,7 @@@ import platfor
  import c_tools
  private import annotation
  import mixin
+ import counter
  
  # Add compiling options
  redef class ToolContext
@@@ -121,30 -122,20 +122,30 @@@ redef class ModelBuilde
        protected fun write_and_make(compiler: AbstractCompiler)
        do
                var platform = compiler.target_platform
 -              var toolchain = platform.toolchain(toolcontext)
 +              var toolchain = platform.toolchain(toolcontext, compiler)
                compile_dir = toolchain.compile_dir
 -              toolchain.write_and_make compiler
 +              toolchain.write_and_make
        end
  end
  
  redef class Platform
        # The specific tool-chain associated to the platform
 -      fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext)
 +      fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
 +      do
 +              return new MakefileToolchain(toolcontext, compiler)
 +      end
  end
  
 +# Build toolchain for a specific target program, varies per `Platform`
  class Toolchain
 +
 +      # Toolcontext
        var toolcontext: ToolContext
  
 +      # Compiler of the target program
 +      var compiler: AbstractCompiler
 +
 +      # Directory where to generate all C files
        fun compile_dir: String
        do
                var compile_dir = toolcontext.opt_compile_dir.value
                return compile_dir
        end
  
 -      fun write_and_make(compiler: AbstractCompiler) is abstract
 +      # Write all C files and compile them
 +      fun write_and_make is abstract
  end
  
 +# Default toolchain using a Makefile
  class MakefileToolchain
        super Toolchain
  
 -      redef fun write_and_make(compiler)
 +      redef fun write_and_make
        do
                var compile_dir = compile_dir
  
                compile_dir.mkdir
  
                var cfiles = new Array[String]
 -              write_files(compiler, compile_dir, cfiles)
 +              write_files(compile_dir, cfiles)
  
                # Generate the Makefile
  
 -              write_makefile(compiler, compile_dir, cfiles)
 +              write_makefile(compile_dir, cfiles)
  
                var time1 = get_time
                self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
                time0 = time1
                self.toolcontext.info("*** COMPILING C ***", 1)
  
 -              compile_c_code(compiler, compile_dir)
 +              compile_c_code(compile_dir)
  
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
  
 -      fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
 +      # Write all source files to the `compile_dir`
 +      fun write_files(compile_dir: String, cfiles: Array[String])
        do
                var platform = compiler.target_platform
                if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
        end
  
 -      fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
 +      # Get the name of the Makefile to use
 +      fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
  
 -      fun default_outname(mainmodule: MModule): String
 +      # Get the default name of the executable to produce
 +      fun default_outname: String
        do
 +              var mainmodule = compiler.mainmodule
 +
                # Search a non fictive module
                var res = mainmodule.name
                while mainmodule.is_fictive do
        do
                var res = self.toolcontext.opt_output.value
                if res != null then return res
 -              res = default_outname(mainmodule)
 +              res = default_outname
                var dir = self.toolcontext.opt_dir.value
                if dir != null then return dir.join_path(res)
                return res
        end
  
 -      fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
 +      # Write the Makefile
 +      fun write_makefile(compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
                var platform = compiler.target_platform
                        # 2. copy the binary at the right place in the `all` goal.
                        outpath = mainmodule.c_name
                end
 -              var makename = makefile_name(mainmodule)
 +              var makename = makefile_name
                var makepath = "{compile_dir}/{makename}"
                var makefile = new FileWriter.open(makepath)
  
@@@ -462,10 -445,9 +463,10 @@@ endi
                makepath.file_copy_to "{compile_dir}/Makefile"
        end
  
 -      fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
 +      # The C code is generated, compile it to an executable
 +      fun compile_c_code(compile_dir: String)
        do
 -              var makename = makefile_name(compiler.mainmodule)
 +              var makename = makefile_name
  
                var makeflags = self.toolcontext.opt_make_flags.value
                if makeflags == null then makeflags = ""
@@@ -608,8 -590,6 +609,8 @@@ abstract class AbstractCompile
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
 +              self.header.add_decl("#include <sys/types.h>\n")
 +              self.header.add_decl("#include <unistd.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
                self.header.add_decl("#ifdef ANDROID")
                self.header.add_decl("  #include <android/log.h>")
@@@ -1042,14 -1022,6 +1043,6 @@@ extern void nitni_global_ref_decr( stru
        end
  
        fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
-       # Division facility
-       # Avoid division by zero by returning the string "n/a"
-       fun div(a,b:Int):String
-       do
-               if b == 0 then return "n/a"
-               return ((a*10000/b).to_f / 100.0).to_precision(2)
-       end
  end
  
  # A file unit (may be more than one file if
@@@ -1106,6 -1078,9 +1099,6 @@@ abstract class AbstractCompilerVisito
                self.writer = new CodeWriter(compiler.files.last)
        end
  
 -      # Force to get the primitive class named `name` or abort
 -      fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
 -
        # Force to get the primitive property named `name` in the instance `recv` or abort
        fun get_property(name: String, recv: MType): MMethod
        do
  
        fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
  
 +      # Return an element of a native array.
 +      # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
 +      fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
 +
 +      # Store an element in a native array.
 +      # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
 +      fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
 +
        # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
        # This method is used to manage varargs in signatures and returns the real array
        # of runtime variables to use in the call.
                var recv
                var ctype = mtype.ctype
                assert mtype.mclass.name != "NativeArray"
 -              if ctype == "val*" then
 +              if not mtype.is_c_primitive then
                        recv = init_instance(mtype)
                else if ctype == "char*" then
                        recv = new_expr("NULL/*special!*/", mtype)
                end
        end
  
 +      # The currently processed module
 +      #
 +      # alias for `compiler.mainmodule`
 +      fun mmodule: MModule do return compiler.mainmodule
 +
        # Generate an integer value
        fun int_instance(value: Int): RuntimeVariable
        do
 -              var res = self.new_var(self.get_class("Int").mclass_type)
 -              self.add("{res} = {value};")
 +              var t = mmodule.int_type
 +              var res = new RuntimeVariable("{value.to_s}l", t, t)
 +              return res
 +      end
 +
 +      # Generate a char value
 +      fun char_instance(value: Char): RuntimeVariable
 +      do
 +              var t = mmodule.char_type
 +              var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
 +              return res
 +      end
 +
 +      # Generate a float value
 +      #
 +      # FIXME pass a Float, not a string
 +      fun float_instance(value: String): RuntimeVariable
 +      do
 +              var t = mmodule.float_type
 +              var res = new RuntimeVariable("{value}", t, t)
                return res
        end
  
        # Generate an integer value
        fun bool_instance(value: Bool): RuntimeVariable
        do
 -              var res = self.new_var(self.get_class("Bool").mclass_type)
 -              if value then
 -                      self.add("{res} = 1;")
 -              else
 -                      self.add("{res} = 0;")
 -              end
 +              var s = if value then "1" else "0"
 +              var res = new RuntimeVariable(s, bool_type, bool_type)
 +              return res
 +      end
 +
 +      # Generate the `null` value
 +      fun null_instance: RuntimeVariable
 +      do
 +              var t = compiler.mainmodule.model.null_type
 +              var res = new RuntimeVariable("((val*)NULL)", t, t)
                return res
        end
  
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
 -              var mtype = self.get_class("String").mclass_type
 +              var mtype = mmodule.string_type
                var name = self.get_name("varonce")
                self.add_decl("static {mtype.ctype} {name};")
                var res = self.new_var(mtype)
                self.add("if (likely({name}!=NULL)) \{")
                self.add("{res} = {name};")
                self.add("\} else \{")
 -              var native_mtype = self.get_class("NativeString").mclass_type
 +              var native_mtype = mmodule.native_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
                var length = self.int_instance(string.length)
@@@ -1814,16 -1754,12 +1807,16 @@@ redef class MTyp
  
        # Short name of the `ctype` to use in unions
        fun ctypename: String do return "val"
 +
 +      # Is the associated C type a primitive one?
 +      #
 +      # ENSURE `result == (ctype != "val*")`
 +      fun is_c_primitive: Bool do return false
  end
  
  redef class MClassType
  
 -      redef fun ctype: String
 -      do
 +      redef var ctype is lazy do
                if mclass.name == "Int" then
                        return "long"
                else if mclass.name == "Bool" then
                end
        end
  
 +      redef var is_c_primitive is lazy do return ctype != "val*"
 +
        redef fun ctype_extern: String
        do
                if mclass.kind == extern_kind then
@@@ -2332,7 -2266,7 +2325,7 @@@ redef class AAttrPropde
                        if is_lazy then
                                var set
                                var ret = self.mpropdef.static_mtype
 -                              var useiset = ret.ctype == "val*" and not ret isa MNullableType
 +                              var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                var guard = self.mlazypropdef.mproperty
                                if useiset then
                                        set = v.isset_attribute(self.mpropdef.mproperty, recv)
  
                                v.assign(res, value)
                                if not useiset then
 -                                      var true_v = v.new_expr("1", v.bool_type)
 +                                      var true_v = v.bool_instance(true)
                                        v.write_attribute(guard, arguments.first, true_v)
                                end
                                v.add("\}")
                        v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
                        if is_lazy then
                                var ret = self.mpropdef.static_mtype
 -                              var useiset = ret.ctype == "val*" and not ret isa MNullableType
 +                              var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                if not useiset then
 -                                      v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
 +                                      v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
                                end
                        end
                else
@@@ -2760,15 -2694,15 +2753,15 @@@ redef class AOrElseExp
  end
  
  redef class AIntExpr
 -      redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
 +      redef fun expr(v) do return v.int_instance(self.value.as(not null))
  end
  
  redef class AFloatExpr
 -      redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
 +      redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
  end
  
  redef class ACharExpr
 -      redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
 +      redef fun expr(v) do return v.char_instance(self.value.as(not null))
  end
  
  redef class AArrayExpr
  redef class ASuperstringExpr
        redef fun expr(v)
        do
 -              var array = new Array[RuntimeVariable]
 +              var type_string = mtype.as(not null)
 +
 +              # Collect elements of the superstring
 +              var array = new Array[AExpr]
                for ne in self.n_exprs do
 +                      # Drop literal empty string.
 +                      # They appears in things like "{a}" that is ["", a, ""]
                        if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
 -                      var i = v.expr(ne, null)
 -                      array.add(i)
 +                      array.add(ne)
 +              end
 +
 +              # Store the allocated native array in a static variable
 +              # For reusing later
 +              var varonce = v.get_name("varonce")
 +              v.add("if (unlikely({varonce}==NULL)) \{")
 +
 +              # The native array that will contains the elements to_s-ized.
 +              # For fast concatenation.
 +              var a = v.native_array_instance(type_string, v.int_instance(array.length))
 +
 +              v.add_decl("static {a.mtype.ctype} {varonce};")
 +
 +              # Pre-fill the array with the literal string parts.
 +              # So they do not need to be filled again when reused
 +              for i in [0..array.length[ do
 +                      var ne = array[i]
 +                      if not ne isa AStringFormExpr then continue
 +                      var e = v.expr(ne, null)
 +                      v.native_array_set(a, i, e)
                end
 -              var a = v.array_instance(array, v.object_type)
 -              var res = v.send(v.get_property("to_s", a.mtype), [a])
 +
 +              v.add("\} else \{")
 +              # Take the native-array from the store.
 +              # The point is to prevent that some recursive execution use (and corrupt) the same native array
 +              # WARNING: not thread safe! (FIXME?)
 +              v.add("{a} = {varonce};")
 +              v.add("{varonce} = NULL;")
 +              v.add("\}")
 +
 +              # Stringify the elements and put them in the native array
 +              var to_s_method = v.get_property("to_s", v.object_type)
 +              for i in [0..array.length[ do
 +                      var ne = array[i]
 +                      if ne isa AStringFormExpr then continue
 +                      var e = v.expr(ne, null)
 +                      # Skip the `to_s` if the element is already a String
 +                      if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
 +                              e = v.send(to_s_method, [e]).as(not null)
 +                      end
 +                      v.native_array_set(a, i, e)
 +              end
 +
 +              # Fast join the native string to get the result
 +              var res = v.send(v.get_property("native_to_s", a.mtype), [a])
 +
 +              # We finish to work with the native array,
 +              # so store it so that it can be reused
 +              v.add("{varonce} = {a};")
                return res
        end
  end
@@@ -2883,15 -2767,15 +2876,15 @@@ redef class AOrangeExp
  end
  
  redef class ATrueExpr
 -      redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
 +      redef fun expr(v) do return v.bool_instance(true)
  end
  
  redef class AFalseExpr
 -      redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
 +      redef fun expr(v) do return v.bool_instance(false)
  end
  
  redef class ANullExpr
 -      redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
 +      redef fun expr(v) do return v.null_instance
  end
  
  redef class AIsaExpr
@@@ -2919,7 -2803,7 +2912,7 @@@ redef class AAsNotnullExp
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
  
 -              if i.mtype.ctype != "val*" then return i
 +              if i.mtype.is_c_primitive then return i
  
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")
@@@ -59,6 -59,8 +59,8 @@@ redef class ToolContex
        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 +74,7 @@@
                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 +149,6 @@@ class SeparateCompile
        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")
                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
  
        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
  
        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.compute_resolution_tables(live_types)
  
-               return poset
+               return res
        end
  
        private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
                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.as_notnullable.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
                # 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
                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)
                #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]
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
  
                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} */")
  
                        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 */")
                        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
                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("\}")
        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
@@@ -1153,9 -1185,9 +1185,9 @@@ class SeparateCompilerVisito
        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)
                                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)
        # 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
  
        # 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)"
        # 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)"
        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
  
        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
                        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} */")
                        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("\}")
                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
        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)
        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
        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});")
  
                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
                        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
                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*"
 +              return k == interface_kind or t.is_c_primitive
        end
  
        fun maybe_null(value: RuntimeVariable): Bool
  
        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}] */")
  
        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
        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
                end
        end
  
 -      redef fun calloc_array(ret_type, arguments)
 +      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 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"
 +              self.add("{recv}[{i}]={val};")
        end
  
        fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
@@@ -2173,7 -2193,7 +2205,7 @@@ class SeparateRuntimeFunctio
                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
                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)
                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
                        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
@@@ -2324,14 -2344,3 +2356,14 @@@ redef class AMethPropde
                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
@@@ -199,14 -199,12 +199,12 @@@ class SeparateErasureCompile
                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 class_table = self.class_tables[mclass]
                var v = self.new_visitor
  
                var rta = runtime_type_analysis
                var is_dead = false # mclass.kind == abstract_kind or mclass.kind == interface_kind
 -              if not is_dead and rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" then
 +              if not is_dead and rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" then
                        is_dead = true
                end
  
                        end
                        v.add_decl("&type_table_{c_name},")
                        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 */")
                v.add_decl("\}")
                v.add_decl("\};")
  
 -              if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
 +              if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
                        #Build instance struct
                        self.header.add_decl("struct instance_{c_name} \{")
                        self.header.add_decl("const struct class *class;")
  
                        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.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("\}")
@@@ -529,7 -535,7 +535,7 @@@ class SeparateErasureCompilerVisito
                end
  
                var class_ptr
 -              if value.mtype.ctype == "val*" then
 +              if not value.mtype.is_c_primitive then
                        class_ptr = "{value}->class->"
                else
                        var mclass = value.mtype.as(MClassType).mclass
                else if mtype isa MVirtualType then
                        var recv = self.frame.arguments.first
                        var recv_ptr
 -                      if recv.mtype.ctype == "val*" then
 +                      if not recv.mtype.is_c_primitive then
                                recv_ptr = "{recv}->class->"
                        else
                                var mclass = recv.mtype.as(MClassType).mclass
        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\" : {value}->class->name;"
                else
                        self.require_declaration("class_{value.mtype.c_name}")
  
        redef fun native_array_instance(elttype, length)
        do
 -              var nclass = self.get_class("NativeArray")
 +              var nclass = mmodule.native_array_class
                var mtype = nclass.get_mtype([elttype])
                var res = self.new_var(mtype)
                res.is_exact = true
diff --combined src/model/model.nit
@@@ -195,40 -195,31 +195,40 @@@ redef class MModul
        private var flatten_mclass_hierarchy_cache: nullable POSet[MClass] = null
  
        # The primitive type `Object`, the root of the class hierarchy
 -      fun object_type: MClassType
 -      do
 -              var res = self.object_type_cache
 -              if res != null then return res
 -              res = self.get_primitive_class("Object").mclass_type
 -              self.object_type_cache = res
 -              return res
 -      end
 -
 -      private var object_type_cache: nullable MClassType
 +      var object_type: MClassType = self.get_primitive_class("Object").mclass_type is lazy
  
        # The type `Pointer`, super class to all extern classes
        var pointer_type: MClassType = self.get_primitive_class("Pointer").mclass_type is lazy
  
        # The primitive type `Bool`
 -      fun bool_type: MClassType
 -      do
 -              var res = self.bool_type_cache
 -              if res != null then return res
 -              res = self.get_primitive_class("Bool").mclass_type
 -              self.bool_type_cache = res
 -              return res
 -      end
 +      var bool_type: MClassType = self.get_primitive_class("Bool").mclass_type is lazy
 +
 +      # The primitive type `Int`
 +      var int_type: MClassType = self.get_primitive_class("Int").mclass_type is lazy
 +
 +      # The primitive type `Char`
 +      var char_type: MClassType = self.get_primitive_class("Char").mclass_type is lazy
 +
 +      # The primitive type `Float`
 +      var float_type: MClassType = self.get_primitive_class("Float").mclass_type is lazy
 +
 +      # The primitive type `String`
 +      var string_type: MClassType = self.get_primitive_class("String").mclass_type is lazy
 +
 +      # The primitive type `NativeString`
 +      var native_string_type: MClassType = self.get_primitive_class("NativeString").mclass_type is lazy
 +
 +      # A primitive type of `Array`
 +      fun array_type(elt_type: MType): MClassType do return array_class.get_mtype([elt_type])
 +
 +      # The primitive class `Array`
 +      var array_class: MClass = self.get_primitive_class("Array") is lazy
 +
 +      # A primitive type of `NativeArray`
 +      fun native_array_type(elt_type: MType): MClassType do return native_array_class.get_mtype([elt_type])
  
 -      private var bool_type_cache: nullable MClassType
 +      # The primitive class `NativeArray`
 +      var native_array_class: MClass = self.get_primitive_class("NativeArray") is lazy
  
        # The primitive type `Sys`, the main type of the program, if any
        fun sys_type: nullable MClassType
@@@ -477,6 -468,9 +477,9 @@@ class MClas
        end
  
        private var get_mtype_cache = new HashMap[Array[MType], MGenericType]
+       # Is there a `new` factory to allow the pseudo instantiation?
+       var has_new_factory = false is writable
  end