sepcomp: implement tagging of primitive types
[nit.git] / src / compiler / separate_compiler.nit
index 437840e..f62d733 100644 (file)
@@ -29,8 +29,19 @@ redef class ToolContext
        var opt_no_union_attribute = new OptionBool("Put primitive attibutes 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 (faster)", "--colors-are-symbols")
+       var opt_colors_are_symbols = new OptionBool("Store colors as symbols (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")
+       # --link-boost
+       var opt_link_boost = new OptionBool("Enable all link-boost optimizations", "--link-boost")
 
        # --inline-coloring-numbers
        var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
@@ -38,6 +49,8 @@ redef class ToolContext
        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")
+       # --direct-call-monomorph0
+       var opt_direct_call_monomorph0 = new OptionBool("Allow the separate compiler to direct call monomorph 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
@@ -53,7 +66,9 @@ redef class ToolContext
                self.option_context.add_option(self.opt_separate)
                self.option_context.add_option(self.opt_no_inline_intern)
                self.option_context.add_option(self.opt_no_union_attribute)
-               self.option_context.add_option(self.opt_no_shortcut_equate, opt_colors_are_symbols)
+               self.option_context.add_option(self.opt_no_shortcut_equate)
+               self.option_context.add_option(self.opt_no_tag_primitives)
+               self.option_context.add_option(opt_colors_are_symbols, opt_trampoline_call, opt_guard_call, opt_direct_call_monomorph0, opt_substitute_monomorph, opt_link_boost)
                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)
@@ -70,6 +85,13 @@ redef class ToolContext
                        tc.opt_direct_call_monomorph.value = true
                        tc.opt_skip_dead_methods.value = true
                end
+               if tc.opt_link_boost.value then
+                       tc.opt_colors_are_symbols.value = true
+                       tc.opt_substitute_monomorph.value = true
+               end
+               if tc.opt_substitute_monomorph.value then
+                       tc.opt_trampoline_call.value = true
+               end
        end
 
        var separate_compiler_phase = new SeparateCompilerPhase(self, null)
@@ -144,6 +166,7 @@ class SeparateCompiler
                modelbuilder.toolcontext.info("Property coloring", 2)
                compiler.new_file("{c_name}.classes")
                compiler.do_property_coloring
+               compiler.compile_class_infos
                for m in mainmodule.in_importation.greaters do
                        for mclass in m.intro_mclasses do
                                #if mclass.kind == abstract_kind or mclass.kind == interface_kind then continue
@@ -156,6 +179,7 @@ class SeparateCompiler
                compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
                compiler.compile_finalizer_function
+               compiler.link_mmethods
 
                # compile methods
                for m in mainmodule.in_importation.greaters do
@@ -197,6 +221,11 @@ class SeparateCompiler
                self.header.add_decl("struct instance \{ const struct type *type; const struct class *class; nitattribute_t attrs[]; \}; /* general C type representing a Nit instance. */")
                self.header.add_decl("struct types \{ int dummy; const struct type *types[]; \}; /* a list types (used for vts, fts and unresolved lists). */")
                self.header.add_decl("typedef struct instance val; /* general C type representing a Nit instance. */")
+
+               if not modelbuilder.toolcontext.opt_no_tag_primitives.value then
+                       self.header.add_decl("extern const struct class *class_info[];")
+                       self.header.add_decl("extern const struct type *type_info[];")
+               end
        end
 
        fun compile_header_attribute_structs
@@ -561,11 +590,74 @@ class SeparateCompiler
                                r.compile_to_c(self)
                                var r2 = pd.virtual_runtime_function
                                if r2 != r then r2.compile_to_c(self)
+
+                               # Generate trampolines
+                               if modelbuilder.toolcontext.opt_trampoline_call.value then
+                                       r2.compile_trampolines(self)
+                               end
                        end
                end
                self.mainmodule = old_module
        end
 
+       # Process all introduced methods and compile some linking information (if needed)
+       fun link_mmethods
+       do
+               if not modelbuilder.toolcontext.opt_substitute_monomorph.value and not modelbuilder.toolcontext.opt_guard_call.value then return
+
+               for mmodule in mainmodule.in_importation.greaters do
+                       for cd in mmodule.mclassdefs do
+                               for m in cd.intro_mproperties do
+                                       if not m isa MMethod then continue
+                                       link_mmethod(m)
+                               end
+                       end
+               end
+       end
+
+       # Compile some linking information (if needed)
+       fun link_mmethod(m: MMethod)
+       do
+               var n2 = "CALL_" + m.const_color
+
+               # Replace monomorphic call by a direct call to the virtual implementation
+               var md = is_monomorphic(m)
+               if md != null then
+                       linker_script.add("{n2} = {md.virtual_runtime_function.c_name};")
+               end
+
+               # If opt_substitute_monomorph then a trampoline is used, else a weak symbol is used
+               if modelbuilder.toolcontext.opt_guard_call.value then
+                       var r = m.intro.virtual_runtime_function
+                       provide_declaration(n2, "{r.c_ret} {n2}{r.c_sig} __attribute__((weak));")
+               end
+       end
+
+       # The single mmethodef called in case of monomorphism.
+       # Returns nul if dead or polymorphic.
+       fun is_monomorphic(m: MMethod): nullable MMethodDef
+       do
+               var rta = runtime_type_analysis
+               if rta == null then
+                       # Without RTA, monomorphic means alone (uniq name)
+                       if m.mpropdefs.length == 1 then
+                               return m.mpropdefs.first
+                       else
+                               return null
+                       end
+               else
+                       # With RTA, monomorphic means only live methoddef
+                       var res: nullable MMethodDef = null
+                       for md in m.mpropdefs do
+                               if rta.live_methoddefs.has(md) then
+                                       if res != null then return null
+                                       res = md
+                               end
+                       end
+                       return res
+               end
+       end
+
        # Globaly compile the type structure of a live type
        fun compile_type_to_c(mtype: MType)
        do
@@ -717,6 +809,8 @@ class SeparateCompiler
                if mtype.ctype != "val*" 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
+
                        #Build instance struct
                        self.header.add_decl("struct instance_{c_name} \{")
                        self.header.add_decl("const struct type *type;")
@@ -834,6 +928,58 @@ class SeparateCompiler
                v.add("\}")
        end
 
+       # Compile structures used to map tagged primitive values to their classes and types.
+       # This method also determines which class will be tagged.
+       fun compile_class_infos
+       do
+               if modelbuilder.toolcontext.opt_no_tag_primitives.value then return
+
+               # Note: if you change the tagging scheme, do not forget to update
+               # `autobox` and `extract_tag`
+               var class_info = new Array[nullable MClass].filled_with(null, 4)
+               for t in box_kinds.keys do
+                       # Note: a same class can be associated to multiple slots if one want to
+                       # use some Huffman coding.
+                       if t.name == "Int" then
+                               class_info[1] = t
+                       else if t.name == "Char" then
+                               class_info[2] = t
+                       else if t.name == "Bool" then
+                               class_info[3] = t
+                       else
+                               continue
+                       end
+                       t.mclass_type.is_tagged = true
+               end
+
+               # Compile the table for classes. The tag is used as an index
+               var v = self.new_visitor
+               v.add_decl "const struct class *class_info[4] = \{"
+               for t in class_info do
+                       if t == null then
+                               v.add_decl("NULL,")
+                       else
+                               var s = "class_{t.c_name}"
+                               v.require_declaration(s)
+                               v.add_decl("&{s},")
+                       end
+               end
+               v.add_decl("\};")
+
+               # Compile the table for types. The tag is used as an index
+               v.add_decl "const struct type *type_info[4] = \{"
+               for t in class_info do
+                       if t == null then
+                               v.add_decl("NULL,")
+                       else
+                               var s = "type_{t.c_name}"
+                               v.require_declaration(s)
+                               v.add_decl("&{s},")
+                       end
+               end
+               v.add_decl("\};")
+       end
+
        # Add a dynamic test to ensure that the type referenced by `t` is a live type
        fun hardening_live_type(v: VISITOR, t: String)
        do
@@ -993,8 +1139,30 @@ class SeparateCompilerVisitor
                else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
                        return value
                else if value.mtype.ctype == "val*" 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)
+                               else if mtype.name == "Bool" then
+                                       return self.new_expr("(short int)((long)({value})>>2)", mtype)
+                               else
+                                       abort
+                               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
+                       if value.mtype.is_tagged then
+                               if value.mtype.name == "Int" then
+                                       return 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)
+                               else if value.mtype.name == "Bool" then
+                                       return self.new_expr("(val*)((long)({value})<<2|3)", mtype)
+                               else
+                                       abort
+                               end
+                       end
                        var valtype = value.mtype.as(MClassType)
                        if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
                                valtype = compiler.mainmodule.pointer_type
@@ -1057,11 +1225,42 @@ class SeparateCompilerVisitor
                end
        end
 
-       # Return a C expression returning the runtime type structure of the value
-       # The point of the method is to works also with primitives types.
+       # Returns a C expression containing the tag of the value as a long.
+       #
+       # If the C expression is evaluated to 0, it means there is no tag.
+       # Thus the expression can be used as a condition.
+       fun extract_tag(value: RuntimeVariable): String
+       do
+               assert value.mtype.ctype == "val*"
+               return "((long){value}&3)" # Get the two low bits
+       end
+
+       # Returns a C expression of the runtime class structure of the value.
+       # 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 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)"
+                       end
+                       return "{value}->class"
+               else
+                       compiler.undead_types.add(value.mtype)
+                       self.require_declaration("class_{value.mtype.c_name}")
+                       return "(&class_{value.mtype.c_name})"
+               end
+       end
+
+       # Returns a C expression of the runtime type structure of the value.
+       # 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 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
                        return "{value}->type"
                else
                        compiler.undead_types.add(value.mtype)
@@ -1073,25 +1272,37 @@ class SeparateCompilerVisitor
        redef fun compile_callsite(callsite, args)
        do
                var rta = compiler.runtime_type_analysis
-               var mmethod = callsite.mproperty
                # TODO: Inlining of new-style constructors with initializers
                if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
                        var tgs = rta.live_targets(callsite)
                        if tgs.length == 1 then
-                               # DIRECT CALL
-                               var res0 = before_send(mmethod, args)
-                               var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
-                               if res0 != null then
-                                       assert res != null
-                                       self.assign(res0, res)
-                                       res = res0
-                               end
-                               add("\}") # close the before_send
-                               return res
+                               return direct_call(tgs.first, args)
                        end
                end
+               # Shortcut intern methods as they are not usually redefinable
+               if callsite.mpropdef.is_intern and callsite.mproperty.name != "object_id" then
+                       # `object_id` is the only redefined intern method, so it can not be directly called.
+                       # TODO find a less ugly approach?
+                       return direct_call(callsite.mpropdef, args)
+               end
                return super
        end
+
+       # Fully and directly call a mpropdef
+       #
+       # This method is used by `compile_callsite`
+       private fun direct_call(mpropdef: MMethodDef, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       do
+               var res0 = before_send(mpropdef.mproperty, args)
+               var res = call(mpropdef, mpropdef.mclassdef.bound_mtype, args)
+               if res0 != null then
+                       assert res != null
+                       self.assign(res0, res)
+                       res = res0
+               end
+               add("\}") # close the before_send
+               return res
+       end
        redef fun send(mmethod, arguments)
        do
                if arguments.first.mcasttype.ctype != "val*" then
@@ -1104,7 +1315,7 @@ class SeparateCompilerVisitor
                        return res
                end
 
-               return table_send(mmethod, arguments, mmethod.const_color)
+               return table_send(mmethod, arguments, mmethod)
        end
 
        # Handle common special cases before doing the effective method invocation
@@ -1170,7 +1381,7 @@ class SeparateCompilerVisitor
                return res
        end
 
-       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
        do
                compiler.modelbuilder.nb_invok_by_tables += 1
                if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
@@ -1204,13 +1415,42 @@ class SeparateCompilerVisitor
                        ss.append(", {a}")
                end
 
-               self.require_declaration(const_color)
-               var call = "(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
-
+               var const_color = mentity.const_color
+               var ress
                if res != null then
-                       self.add("{res} = {call};")
+                       ress = "{res} = "
                else
-                       self.add("{call};")
+                       ress = ""
+               end
+               if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_direct_call_monomorph0.value then
+                       # opt_direct_call_monomorph0 is used to compare the efficiency of the alternative lookup implementation, ceteris paribus.
+                       # The difference with the non-zero option is that the monomorphism is looked-at on the mmethod level and not at the callsite level.
+                       # TODO: remove this mess and use per callsite service to detect monomorphism in a single place.
+                       var md = compiler.is_monomorphic(mentity)
+                       if md != null then
+                               var callsym = md.virtual_runtime_function.c_name
+                               self.require_declaration(callsym)
+                               self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       else
+                               self.require_declaration(const_color)
+                               self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       end
+               else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_guard_call.value then
+                       var callsym = "CALL_" + const_color
+                       self.require_declaration(callsym)
+                       self.add "if (!{callsym}) \{"
+                       self.require_declaration(const_color)
+                       self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       self.add "\} else \{"
+                       self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+                       self.add "\}"
+               else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
+                       var callsym = "CALL_" + const_color
+                       self.require_declaration(callsym)
+                       self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
+               else
+                       self.require_declaration(const_color)
+                       self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
                end
 
                if res0 != null then
@@ -1281,7 +1521,7 @@ class SeparateCompilerVisitor
                        self.compiler.mainmodule = main
                        return res
                end
-               return table_send(m.mproperty, arguments, m.const_color)
+               return table_send(m.mproperty, arguments, m)
        end
 
        redef fun vararg_instance(mpropdef, recv, varargs, elttype)
@@ -1572,7 +1812,7 @@ class SeparateCompilerVisitor
                                self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
                        end
                else
-                       self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {value1}->class == {value2}->class); /* is_same_type_test */")
+                       self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
                end
                return res
        end
@@ -1582,7 +1822,7 @@ class SeparateCompilerVisitor
                var res = self.get_name("var_class_name")
                self.add_decl("const char* {res};")
                if value.mtype.ctype == "val*" then
-                       self.add "{res} = {value} == NULL ? \"null\" : {value}->type->name;"
+                       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
                        self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
@@ -1606,6 +1846,8 @@ class SeparateCompilerVisitor
                                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});")
                        else
                                var mtype1 = value1.mtype.as(MClassType)
                                self.require_declaration("class_{mtype1.c_name}")
@@ -1642,6 +1884,13 @@ class SeparateCompilerVisitor
                        else if t2.ctype != "val*" then
                                incompatible = true
                        else if can_be_primitive(value2) then
+                               if t1.is_tagged then
+                                       self.add("{res} = {value1} == {value2};")
+                                       return res
+                               end
+                               if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
+                                       test.add("(!{extract_tag(value2)})")
+                               end
                                test.add("{value1}->class == {value2}->class")
                        else
                                incompatible = true
@@ -1649,6 +1898,13 @@ class SeparateCompilerVisitor
                else if t2.ctype != "val*" then
                        primitive = t2
                        if can_be_primitive(value1) then
+                               if t2.is_tagged then
+                                       self.add("{res} = {value1} == {value2};")
+                                       return res
+                               end
+                               if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
+                                       test.add("(!{extract_tag(value1)})")
+                               end
                                test.add("{value1}->class == {value2}->class")
                        else
                                incompatible = true
@@ -1667,13 +1923,25 @@ class SeparateCompilerVisitor
                        end
                end
                if primitive != null then
+                       if primitive.is_tagged then
+                               self.add("{res} = {value1} == {value2};")
+                               return res
+                       end
                        test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
                else if can_be_primitive(value1) and can_be_primitive(value2) then
+                       if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
+                               test.add("(!{extract_tag(value1)}) && (!{extract_tag(value2)})")
+                       end
                        test.add("{value1}->class == {value2}->class")
                        var s = new Array[String]
                        for t, v in self.compiler.box_kinds do
+                               if t.mclass_type.is_tagged then continue
                                s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
                        end
+                       if s.is_empty then
+                               self.add("{res} = {value1} == {value2};")
+                               return res
+                       end
                        test.add("({s.join(" || ")})")
                else
                        self.add("{res} = {value1} == {value2};")
@@ -1740,7 +2008,10 @@ class SeparateCompilerVisitor
                var nclass = self.get_class("NativeArray")
                var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
                if pname == "[]" then
-                       self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
+                       # 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
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
@@ -1949,6 +2220,56 @@ class SeparateRuntimeFunction
                v.add("\}")
                compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
+
+       # Compile the trampolines used to implement late-binding.
+       #
+       # See `opt_trampoline_call`.
+       fun compile_trampolines(compiler: SeparateCompiler)
+       do
+               var recv = self.mmethoddef.mclassdef.bound_mtype
+               var selfvar = arguments.first
+               var ret = called_signature.return_mtype
+
+               if mmethoddef.is_intro and recv.ctype == "val*" 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(", ")});"
+                       if ret != null then
+                               v2.add "return {call}"
+                       else
+                               v2.add call
+                       end
+
+                       v2.add "\}"
+
+               end
+               if mmethoddef.has_supercall and recv.ctype == "val*" 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(", ")});"
+                       if ret != null then
+                               v2.add "return {call}"
+                       else
+                               v2.add call
+                       end
+
+                       v2.add "\}"
+               end
+       end
+end
+
+redef class MType
+       # Are values of `self` tagged?
+       # If false, it means that the type is not primitive, or is boxed.
+       var is_tagged = false
 end
 
 redef class MEntity