nitc: explicitly cast long to int
[nit.git] / src / compiler / separate_compiler.nit
index ac9897c..e486b64 100644 (file)
@@ -26,20 +26,20 @@ redef class ToolContext
        # --no-inline-intern
        var opt_no_inline_intern = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
        # --no-union-attribute
-       var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
+       var opt_no_union_attribute = new OptionBool("Put primitive attributes in a box instead of an union", "--no-union-attribute")
        # --no-shortcut-equate
        var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
        # --no-tag-primitives
        var opt_no_tag_primitives = new OptionBool("Use only boxes for primitive types", "--no-tag-primitives")
 
        # --colors-are-symbols
-       var opt_colors_are_symbols = new OptionBool("Store colors as symbols (link-boost)", "--colors-are-symbols")
+       var opt_colors_are_symbols = new OptionBool("Store colors as symbols instead of static data (link-boost)", "--colors-are-symbols")
        # --trampoline-call
        var opt_trampoline_call = new OptionBool("Use an indirection when calling", "--trampoline-call")
        # --guard-call
        var opt_guard_call = new OptionBool("Guard VFT calls with a direct call", "--guard-call")
        # --substitute-monomorph
-       var opt_substitute_monomorph = new OptionBool("Replace monomorph trampoline with direct call (link-boost)", "--substitute-monomorph")
+       var opt_substitute_monomorph = new OptionBool("Replace monomorphic trampolines with direct calls (link-boost)", "--substitute-monomorph")
        # --link-boost
        var opt_link_boost = new OptionBool("Enable all link-boost optimizations", "--link-boost")
 
@@ -48,9 +48,9 @@ redef class ToolContext
        # --inline-some-methods
        var opt_inline_some_methods = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
        # --direct-call-monomorph
-       var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
+       var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorphic sites (semi-global)", "--direct-call-monomorph")
        # --direct-call-monomorph0
-       var opt_direct_call_monomorph0 = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph0")
+       var opt_direct_call_monomorph0 = new OptionBool("Allow the separate compiler to direct call monomorphic sites (semi-global)", "--direct-call-monomorph0")
        # --skip-dead-methods
        var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
        # --semi-global
@@ -60,7 +60,7 @@ redef class ToolContext
        # --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")
+       var opt_type_poset = new OptionBool("Build a poset of types to create more condensed tables", "--type-poset")
 
        redef init
        do
@@ -252,10 +252,11 @@ class SeparateCompiler
        do
                # Collect all bas box class
                # FIXME: this is not completely fine with a separate compilation scheme
-               for classname in ["Int", "Bool", "Char", "Float", "NativeString", "Pointer"] do
+               for classname in ["Int", "Bool", "Byte", "Char", "Float", "CString",
+                                "Pointer", "Int8", "Int16", "UInt16", "Int32", "UInt32"] do
                        var classes = self.mainmodule.model.get_mclasses_by_name(classname)
                        if classes == null then continue
-                       assert classes.length == 1 else print classes.join(", ")
+                       assert classes.length == 1 else print_error classes.join(", ")
                        self.box_kinds[classes.first] = self.box_kinds.length + 1
                end
        end
@@ -268,8 +269,8 @@ class SeparateCompiler
                #if mclass.mclass_type.ctype == "val*" or mclass.mclass_type.is_subtype(self.mainmodule, mclass.mclass_type pointer_type) then
                if mclass.mclass_type.ctype_extern == "val*" then
                        return 0
-               else if mclass.kind == extern_kind and mclass.name != "NativeString" then
-                       return self.box_kinds[self.mainmodule.get_primitive_class("Pointer")]
+               else if mclass.kind == extern_kind and mclass.name != "CString" then
+                       return self.box_kinds[self.mainmodule.pointer_type.mclass]
                else
                        return self.box_kinds[mclass]
                end
@@ -386,7 +387,7 @@ class SeparateCompiler
                method_tables = new HashMap[MClass, Array[nullable MPropDef]]
                attr_tables = new HashMap[MClass, Array[nullable MProperty]]
                for mclass in mclasses do
-                       #if mclass.kind == abstract_kind or mclass.kind == interface_kind then continue
+                       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 mtype = mclass.intro.bound_mtype
@@ -458,14 +459,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
@@ -510,7 +511,7 @@ class SeparateCompiler
                # 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
+                       var c = e.undecorate.as(MClassType).mclass
                        if not bucklets.has_key(c) then
                                bucklets[c] = new HashSet[MType]
                        end
@@ -625,6 +626,7 @@ class SeparateCompiler
                for cd in mmodule.mclassdefs do
                        for pd in cd.mpropdefs do
                                if not pd isa MMethodDef then continue
+                               if pd.mproperty.is_broken or pd.is_broken or pd.msignature == null then continue # Skip broken method
                                var rta = runtime_type_analysis
                                if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
                                #print "compile {pd} @ {cd} @ {mmodule}"
@@ -742,7 +744,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*/")
@@ -768,14 +770,15 @@ class SeparateCompiler
                        end
                        v.add_decl("\},")
                else
-                       v.add_decl("0, \{\}, /*DEAD TYPE*/")
+                       # Use -1 to indicate dead type, the info is used by --hardening
+                       v.add_decl("-1, \{\}, /*DEAD TYPE*/")
                end
                v.add_decl("\};")
        end
 
        fun compile_type_resolution_table(mtype: MType) do
 
-               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};")
@@ -811,18 +814,23 @@ class SeparateCompiler
        # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
        fun compile_class_to_c(mclass: MClass)
        do
+               if mclass.is_broken then return
+
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
 
                var 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)
+               # While the class may be dead, some part of separately compiled code may use symbols associated to the class, so
+               # in order to compile and link correctly the C code, these symbols should be declared and defined.
+               var need_corpse = is_dead and mtype.is_c_primitive or mclass.kind == extern_kind or mclass.kind == enum_kind
 
-               v.add_decl("/* runtime class {c_name} */")
+               v.add_decl("/* runtime class {c_name}: {mclass.full_name} (dead={is_dead}; need_corpse={need_corpse})*/")
 
                # Build class vft
-               if not is_dead then
+               if not is_dead or need_corpse then
                        self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
                        v.add_decl("const struct class class_{c_name} = \{")
                        v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
@@ -837,6 +845,9 @@ class SeparateCompiler
                                        if rta != null and not rta.live_methoddefs.has(mpropdef) then
                                                v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
                                                continue
+                                       else if mpropdef.is_broken or mpropdef.msignature == null or mpropdef.mproperty.is_broken then
+                                               v.add_decl("NULL, /* DEAD (BROKEN) {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
+                                               continue
                                        end
                                        var rf = mpropdef.virtual_runtime_function
                                        v.require_declaration(rf.c_name)
@@ -847,7 +858,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
@@ -859,13 +870,15 @@ class SeparateCompiler
                        self.header.add_decl("{mtype.ctype_extern} value;")
                        self.header.add_decl("\};")
 
-                       if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
+                       # Pointer is needed by extern types, live or not
+                       if is_dead and mtype.mclass.name != "Pointer" then return
 
                        #Build BOX
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
-                       v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
+                       v.add("struct instance_{c_name}*res = {alloc};")
                        v.compiler.undead_types.add(mtype)
                        v.require_declaration("type_{c_name}")
                        v.add("res->type = &type_{c_name};")
@@ -875,6 +888,7 @@ class SeparateCompiler
                        v.add("return (val*)res;")
                        v.add("\}")
 
+                       # A Pointer class also need its constructor
                        if mtype.mclass.name != "Pointer" then return
 
                        v = new_visitor
@@ -886,7 +900,8 @@ class SeparateCompiler
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
-                               v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
+                               alloc = v.nit_alloc("sizeof(struct instance_{mtype.c_name})", mclass.full_name)
+                               v.add("{res} = {alloc};")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
@@ -913,7 +928,8 @@ class SeparateCompiler
                        var res = v.get_name("self")
                        v.add_decl("struct instance_{c_name} *{res};")
                        var mtype_elt = mtype.arguments.first
-                       v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
+                       var alloc = v.nit_alloc("sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype})", mclass.full_name)
+                       v.add("{res} = {alloc};")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
@@ -922,21 +938,22 @@ class SeparateCompiler
                        v.add("return (val*){res};")
                        v.add("\}")
                        return
-               else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
-                       # Is an extern class (other than Pointer and NativeString)
-                       # Pointer is caught in a previous `if`, and NativeString is internal
+               else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
+                       # Is an extern class (other than Pointer and CString)
+                       # Pointer is caught in a previous `if`, and CString is internal
 
                        var pointer_type = mainmodule.pointer_type
 
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
-                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("/* allocate extern {mtype} */")
                        v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
                        if is_dead then
                                v.add_abort("{mclass} is DEAD")
                        else
                                var res = v.new_named_var(mtype, "self")
                                res.is_exact = true
-                               v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
+                               var alloc = v.nit_alloc("sizeof(struct instance_{pointer_type.c_name})", mclass.full_name)
+                               v.add("{res} = {alloc};")
                                v.add("{res}->type = type;")
                                hardening_live_type(v, "type")
                                v.require_declaration("class_{c_name}")
@@ -959,9 +976,11 @@ class SeparateCompiler
                        res.is_exact = true
                        var attrs = self.attr_tables.get_or_null(mclass)
                        if attrs == null then
-                               v.add("{res} = nit_alloc(sizeof(struct instance));")
+                               var alloc = v.nit_alloc("sizeof(struct instance)", mclass.full_name)
+                               v.add("{res} = {alloc};")
                        else
-                               v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
+                               var alloc = v.nit_alloc("sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t)", mclass.full_name)
+                               v.add("{res} = {alloc};")
                        end
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
@@ -990,10 +1009,13 @@ class SeparateCompiler
                        # use some Huffman coding.
                        if t.name == "Int" then
                                class_info[1] = t
+                               t.mclass_type.tag_value = 1
                        else if t.name == "Char" then
                                class_info[2] = t
+                               t.mclass_type.tag_value = 2
                        else if t.name == "Bool" then
                                class_info[3] = t
+                               t.mclass_type.tag_value = 3
                        else
                                continue
                        end
@@ -1036,8 +1058,8 @@ class SeparateCompiler
                v.add("if({t} == NULL) \{")
                v.add_abort("type null")
                v.add("\}")
-               v.add("if({t}->table_size == 0) \{")
-               v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
+               v.add("if({t}->table_size < 0) \{")
+               v.add("PRINT_ERROR(\"Instantiation of a dead type: %s\\n\", {t}->name);")
                v.add_abort("type dead")
                v.add("\}")
        end
@@ -1158,8 +1180,9 @@ class SeparateCompilerVisitor
                        args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
                end
                for i in [0..msignature.arity[ do
-                       var t = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
+                       var mp = msignature.mparameters[i]
+                       var t = mp.mtype
+                       if mp.is_vararg then
                                t = args[i+1].mtype
                        end
                        args[i+1] = self.autobox(args[i+1], t)
@@ -1173,8 +1196,9 @@ class SeparateCompilerVisitor
                        args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
                end
                for i in [0..msignature.arity[ do
-                       var t = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
+                       var mp = msignature.mparameters[i]
+                       var t = mp.mtype
+                       if mp.is_vararg then
                                t = args[i+1].mtype
                        end
                        if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
@@ -1185,14 +1209,14 @@ 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)
                                else if mtype.name == "Char" then
-                                       return self.new_expr("(char)((long)({value})>>2)", mtype)
+                                       return self.new_expr("(uint32_t)((long)({value})>>2)", mtype)
                                else if mtype.name == "Bool" then
                                        return self.new_expr("(short int)((long)({value})>>2)", mtype)
                                else
@@ -1200,28 +1224,30 @@ 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
+                       assert value.mtype == value.mcasttype
                        if value.mtype.is_tagged then
+                               var res
                                if value.mtype.name == "Int" then
-                                       return self.new_expr("(val*)({value}<<2|1)", mtype)
+                                       res = self.new_expr("(val*)({value}<<2|1)", mtype)
                                else if value.mtype.name == "Char" then
-                                       return self.new_expr("(val*)((long)({value})<<2|2)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|2)", mtype)
                                else if value.mtype.name == "Bool" then
-                                       return self.new_expr("(val*)((long)({value})<<2|3)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|3)", mtype)
                                else
                                        abort
                                end
+                               # Do not loose type info
+                               res.mcasttype = value.mcasttype
+                               return res
                        end
                        var valtype = value.mtype.as(MClassType)
-                       if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
+                       if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
                                valtype = compiler.mainmodule.pointer_type
                        end
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
-                               self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
-                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       # Do not loose type info
+                       res.mcasttype = value.mcasttype
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
@@ -1241,7 +1267,7 @@ class SeparateCompilerVisitor
        redef fun unbox_extern(value, mtype)
        do
                if mtype isa MClassType and mtype.mclass.kind == extern_kind and
-                  mtype.mclass.name != "NativeString" then
+                  mtype.mclass.name != "CString" then
                        var pointer_type = compiler.mainmodule.pointer_type
                        var res = self.new_var_extern(mtype)
                        self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
@@ -1254,14 +1280,10 @@ class SeparateCompilerVisitor
        redef fun box_extern(value, mtype)
        do
                if mtype isa MClassType and mtype.mclass.kind == extern_kind and
-                  mtype.mclass.name != "NativeString" then
+                  mtype.mclass.name != "CString" then
                        var valtype = compiler.mainmodule.pointer_type
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
-                               self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
-                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       compiler.undead_types.add(mtype)
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
                        self.require_declaration("type_{mtype.c_name}")
@@ -1280,7 +1302,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
 
@@ -1288,7 +1310,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)"
@@ -1305,7 +1327,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)"
@@ -1354,7 +1376,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
@@ -1383,8 +1405,7 @@ class SeparateCompilerVisitor
                var res: nullable RuntimeVariable = null
                var recv = arguments.first
                var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
-               var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null
-               if maybenull then
+               if maybenull(recv) and consider_null then
                        self.add("if ({recv} == NULL) \{")
                        if mmethod.name == "==" or mmethod.name == "is_same_instance" then
                                res = self.new_var(bool_type)
@@ -1436,13 +1457,14 @@ class SeparateCompilerVisitor
                if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
 
                assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
-               var recv = arguments.first
 
                var res0 = before_send(mmethod, arguments)
 
                var runtime_function = mmethod.intro.virtual_runtime_function
                var msignature = runtime_function.called_signature
 
+               adapt_signature(mmethod.intro, arguments)
+
                var res: nullable RuntimeVariable
                var ret = msignature.return_mtype
                if ret == null then
@@ -1451,18 +1473,7 @@ class SeparateCompilerVisitor
                        res = self.new_var(ret)
                end
 
-               var ss = new FlatBuffer
-
-               ss.append("{recv}")
-               for i in [0..msignature.arity[ do
-                       var a = arguments[i+1]
-                       var t = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               t = arguments[i+1].mcasttype
-                       end
-                       a = self.autobox(a, t)
-                       ss.append(", {a}")
-               end
+               var ss = arguments.join(", ")
 
                var const_color = mentity.const_color
                var ress
@@ -1561,7 +1572,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
@@ -1612,7 +1623,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} */")
@@ -1664,7 +1675,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("\}")
@@ -1693,7 +1704,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
@@ -1845,20 +1860,20 @@ 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)
                                self.require_declaration("class_{mtype1.c_name}")
-                               self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
+                               self.add("{res} = ({value2} != NULL) && ({class_info(value2)} == &class_{mtype1.c_name}); /* is_same_type_test */")
                        end
                else
                        self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
@@ -1870,10 +1885,10 @@ 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
+                       value.mtype.as(MClassType).name != "CString" then
                        self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
                else
                        self.require_declaration("type_{value.mtype.c_name}")
@@ -1885,26 +1900,64 @@ 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 value2.mtype == value1.mtype then
+               if value1.mtype.is_c_primitive then
+                       var t1 = value1.mtype
+                       assert t1 == value1.mcasttype
+
+                       # Fast case: same C type.
+                       if value2.mtype == t1 then
+                               # Same exact C primitive representation.
                                self.add("{res} = {value1} == {value2};")
-                       else if value2.mtype.ctype != "val*" then
-                               self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
-                       else if value1.mtype.is_tagged then
-                               self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});")
+                               return res
+                       end
+
+                       # Complex case: value2 has a different representation
+                       # Thus, it should be checked if `value2` is type-compatible with `value1`
+                       # This compatibility is done statically if possible and dynamically else
+
+                       # Conjunction (ands) of dynamic tests according to the static knowledge
+                       var tests = new Array[String]
+
+                       var t2 = value2.mcasttype
+                       if t2 isa MNullableType then
+                               # The destination type cannot be null
+                               tests.add("({value2} != NULL)")
+                               t2 = t2.mtype
+                       else if t2 isa MNullType then
+                               # `value2` is known to be null, thus incompatible with a primitive
+                               self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
+                               return res
+                       end
+
+                       if t2 == t1 then
+                               # Same type but different representation.
+                       else if t2.is_c_primitive then
+                               # Type of `value2` is a different primitive type, thus incompatible
+                               self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
+                               return res
+                       else if t1.is_tagged then
+                               # To be equal, `value2` should also be correctly tagged
+                               tests.add("({extract_tag(value2)} == {t1.tag_value})")
                        else
-                               var mtype1 = value1.mtype.as(MClassType)
-                               self.require_declaration("class_{mtype1.c_name}")
-                               self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
-                               self.add("if ({res}) \{")
-                               self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
-                               self.add("\}")
+                               # To be equal, `value2` should also be boxed with the same class
+                               self.require_declaration("class_{t1.c_name}")
+                               tests.add "({class_info(value2)} == &class_{t1.c_name})"
+                       end
+
+                       # Compare the unboxed `value2` with `value1`
+                       if tests.not_empty then
+                               self.add "if ({tests.join(" && ")}) \{"
+                       end
+                       self.add "{res} = {self.autobox(value2, t1)} == {value1};"
+                       if tests.not_empty then
+                               self.add "\} else {res} = 0;"
                        end
+
                        return res
                end
                var maybe_null = true
@@ -1926,11 +1979,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
@@ -1944,7 +1997,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
@@ -2002,10 +2055,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
@@ -2016,8 +2069,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}] */")
@@ -2032,55 +2085,70 @@ class SeparateCompilerVisitor
                return res
        end
 
-       redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
+       redef fun native_array_instance(elttype, length)
        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
+               length = autobox(length, compiler.mainmodule.int_type)
                if mtype.need_anchor then
                        hardening_live_open_type(mtype)
                        link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
                        var recv = self.frame.arguments.first
                        var recv_type_info = self.type_info(recv)
                        self.require_declaration(mtype.const_color)
-                       return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
+                       return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
                end
                compiler.undead_types.add(mtype)
                self.require_declaration("type_{mtype.c_name}")
-               return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
+               return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, &type_{mtype.c_name})", mtype)
        end
 
        redef fun native_array_def(pname, ret_type, arguments)
        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
                        var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
                        res.mcasttype = ret_type.as(not null)
                        self.ret(res)
-                       return
+                       return true
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
-                       return
+                       return true
                else if pname == "length" then
                        self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
-                       return
+                       return true
                else if pname == "copy_to" then
                        var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
                        self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
-                       return
+                       return true
+               else if pname == "memmove" then
+                       # fun memmove(start: Int, length: Int, dest: NativeArray[E], dest_start: Int) is intern do
+                       var recv1 = "((struct instance_{nclass.c_name}*){arguments[3]})->values"
+                       self.add("memmove({recv1}+{arguments[4]}, {recv}+{arguments[1]}, {arguments[2]}*sizeof({elttype.ctype}));")
+                       return true
                end
+               return false
        end
 
-       redef fun 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
@@ -2191,9 +2259,10 @@ class SeparateRuntimeFunction
                var sig = new FlatBuffer
                sig.append("({called_recv.ctype} self")
                for i in [0..called_signature.arity[ do
-                       var mtype = called_signature.mparameters[i].mtype
-                       if i == called_signature.vararg_rank then
-                               mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
+                       var mp = called_signature.mparameters[i]
+                       var mtype = mp.mtype
+                       if mp.is_vararg then
+                               mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
                        end
                        sig.append(", {mtype.ctype} p{i}")
                end
@@ -2204,13 +2273,15 @@ class SeparateRuntimeFunction
        # The C type for the function pointer.
        var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
 
-       # The arguments, as generated by `compile_to_c`
-       private var arguments: Array[RuntimeVariable] is noinit
-
        redef fun compile_to_c(compiler)
        do
                var mmethoddef = self.mmethoddef
 
+               var sig = "{c_ret} {c_name}{c_sig}"
+               compiler.provide_declaration(self.c_name, "{sig};")
+
+               var rta = compiler.as(SeparateCompiler).runtime_type_analysis
+
                var recv = self.mmethoddef.mclassdef.bound_mtype
                var v = compiler.new_visitor
                var selfvar = new RuntimeVariable("self", called_recv, recv)
@@ -2221,18 +2292,14 @@ class SeparateRuntimeFunction
                var msignature = called_signature
                var ret = called_signature.return_mtype
 
-               var sig = new FlatBuffer
                var comment = new FlatBuffer
-               sig.append(c_ret)
-               sig.append(" ")
-               sig.append(self.c_name)
-               sig.append(c_sig)
                comment.append("({selfvar}: {selfvar.mtype}")
                arguments.add(selfvar)
                for i in [0..msignature.arity[ do
-                       var mtype = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
+                       var mp = msignature.mparameters[i]
+                       var mtype = mp.mtype
+                       if mp.is_vararg then
+                               mtype = v.mmodule.array_type(mtype)
                        end
                        comment.append(", {mtype}")
                        var argvar = new RuntimeVariable("p{i}", mtype, mtype)
@@ -2242,8 +2309,6 @@ class SeparateRuntimeFunction
                if ret != null then
                        comment.append(": {ret}")
                end
-               compiler.provide_declaration(self.c_name, "{sig};")
-               self.arguments = arguments.to_a
 
                v.add_decl("/* method {self} for {comment} */")
                v.add_decl("{sig} \{")
@@ -2258,6 +2323,8 @@ class SeparateRuntimeFunction
                                assert subret != null
                                v.assign(frame.returnvar.as(not null), subret)
                        end
+               else if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
+                       v.add_abort("FATAL: Dead method executed.")
                else
                        mmethoddef.compile_inside_to_c(v, arguments)
                end
@@ -2276,17 +2343,19 @@ class SeparateRuntimeFunction
        fun compile_trampolines(compiler: SeparateCompiler)
        do
                var recv = self.mmethoddef.mclassdef.bound_mtype
-               var selfvar = arguments.first
+               var selfvar = new RuntimeVariable("self", called_recv, recv)
                var ret = called_signature.return_mtype
+               var arguments = ["self"]
+               for i in [0..called_signature.arity[ do arguments.add "p{i}"
 
-               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
@@ -2296,14 +2365,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
@@ -2319,6 +2388,12 @@ redef class MType
        # Are values of `self` tagged?
        # If false, it means that the type is not primitive, or is boxed.
        var is_tagged = false
+
+       # The tag value of the type
+       #
+       # ENSURE `is_tagged == (tag_value > 0)`
+       # ENSURE `not is_tagged == (tag_value == 0)`
+       var tag_value = 0
 end
 
 redef class MEntity
@@ -2344,3 +2419,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