X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index ba65675..5b5975f 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -29,11 +29,15 @@ 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 (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 @@ -45,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 @@ -61,7 +67,8 @@ redef class ToolContext 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) - self.option_context.add_option(opt_colors_are_symbols, opt_trampoline_call, opt_substitute_monomorph, opt_link_boost) + 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) @@ -159,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 @@ -171,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 @@ -212,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 @@ -442,14 +456,9 @@ class SeparateCompiler # Collect types to colorize var live_types = runtime_type_analysis.live_types var live_cast_types = runtime_type_analysis.live_cast_types - var mtypes = new HashSet[MType] - mtypes.add_all(live_types) - for c in self.box_kinds.keys do - mtypes.add(c.mclass_type) - end # Compute colors - var poset = poset_from_mtypes(mtypes, live_cast_types) + var poset = poset_from_mtypes(live_types, live_cast_types) var colorer = new POSetColorer[MType] colorer.colorize(poset) type_ids = colorer.ids @@ -457,20 +466,42 @@ class SeparateCompiler type_tables = build_type_tables(poset) # VT and FT are stored with other unresolved types in the big resolution_tables - self.compile_resolution_tables(mtypes) + self.compute_resolution_tables(live_types) return poset end private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do var poset = new POSet[MType] + + # Instead of doing the full matrix mtypes X cast_types, + # a grouping is done by the base classes of the type so + # that we compare only types whose base classes are in inheritance. + + var mtypes_by_class = new MultiHashMap[MClass, MType] for e in mtypes do + var c = e.as_notnullable.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 + casttypes_by_class[c].add(e) poset.add_node(e) - for o in cast_types do - if e == o then continue - poset.add_node(o) - if e.is_subtype(mainmodule, null, o) then - poset.add_edge(e, o) + end + + for c1, ts1 in mtypes_by_class do + for c2 in c1.in_hierarchy(mainmodule).greaters do + var ts2 = casttypes_by_class[c2] + for e in ts1 do + for o in ts2 do + if e == o then continue + if e.is_subtype(mainmodule, null, o) then + poset.add_edge(e, o) + end + end end end end @@ -496,29 +527,33 @@ class SeparateCompiler return tables end - protected fun compile_resolution_tables(mtypes: Set[MType]) do - # resolution_tables is used to perform a type resolution at runtime in O(1) - + # resolution_tables is used to perform a type resolution at runtime in O(1) + private fun compute_resolution_tables(mtypes: Set[MType]) do # During the visit of the body of classes, live_unresolved_types are collected # and associated to # Collect all live_unresolved_types (visited in the body of classes) # Determinate fo each livetype what are its possible requested anchored types - var mtype2unresolved = new HashMap[MClassType, Set[MType]] + var mtype2unresolved = new HashMap[MClass, Set[MType]] for mtype in self.runtime_type_analysis.live_types do - var set = new HashSet[MType] + var mclass = mtype.mclass + var set = mtype2unresolved.get_or_null(mclass) + if set == null then + set = new HashSet[MType] + mtype2unresolved[mclass] = set + end for cd in mtype.collect_mclassdefs(self.mainmodule) do if self.live_unresolved_types.has_key(cd) then set.add_all(self.live_unresolved_types[cd]) end end - mtype2unresolved[mtype] = set end # Compute the table layout with the prefered method - var colorer = new BucketsColorer[MType, MType] + var colorer = new BucketsColorer[MClass, MType] + opentype_colors = colorer.colorize(mtype2unresolved) - resolution_tables = self.build_resolution_tables(mtype2unresolved) + resolution_tables = self.build_resolution_tables(self.runtime_type_analysis.live_types, mtype2unresolved) # Compile a C constant for each collected unresolved type. # Either to a color, or to -1 if the unresolved type is dead (no live receiver can require it) @@ -543,9 +578,10 @@ class SeparateCompiler #print "" end - fun build_resolution_tables(elements: Map[MClassType, Set[MType]]): Map[MClassType, Array[nullable MType]] do + fun build_resolution_tables(elements: Set[MClassType], map: Map[MClass, Set[MType]]): Map[MClassType, Array[nullable MType]] do var tables = new HashMap[MClassType, Array[nullable MType]] - for mclasstype, mtypes in elements do + for mclasstype in elements do + var mtypes = map[mclasstype.mclass] var table = new Array[nullable MType] for mtype in mtypes do var color = opentype_colors[mtype] @@ -580,30 +616,70 @@ class SeparateCompiler # Generate trampolines if modelbuilder.toolcontext.opt_trampoline_call.value then r2.compile_trampolines(self) - - # Replace monomorphic call to a trampoline by a direct call to the virtual implementation - if modelbuilder.toolcontext.opt_substitute_monomorph.value then do - var m = pd.mproperty - if rta == null then - # Without RTA, monomorphic means alone (uniq name) - if m.mpropdefs.length != 1 then break label - else - # With RTA, monomorphic means only live methoddef - if not rta.live_methoddefs.has(pd) then break label - for md in m.mpropdefs do - if md != pd and rta.live_methoddefs.has(md) then break label - end - end - # Here the trick, GNU ld can substitute symbols with specific values. - var n2 = "CALL_" + m.const_color - linker_script.add("{n2} = {r2.c_name};") - end label 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 @@ -718,8 +794,6 @@ class SeparateCompiler var mtype = mclass.intro.bound_mtype var c_name = mclass.c_name - var vft = self.method_tables[mclass] - var attrs = self.attr_tables[mclass] var v = new_visitor var rta = runtime_type_analysis @@ -733,7 +807,8 @@ class SeparateCompiler v.add_decl("const struct class class_{c_name} = \{") v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */") v.add_decl("\{") - for i in [0 .. vft.length[ do + var vft = self.method_tables.get_or_null(mclass) + if vft != null then for i in [0 .. vft.length[ do var mpropdef = vft[i] if mpropdef == null then v.add_decl("NULL, /* empty */") @@ -755,6 +830,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;") @@ -860,18 +937,78 @@ class SeparateCompiler else var res = v.new_named_var(mtype, "self") res.is_exact = true - v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));") + var attrs = self.attr_tables.get_or_null(mclass) + if attrs == null then + v.add("{res} = nit_alloc(sizeof(struct instance));") + else + v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));") + end v.add("{res}->type = type;") hardening_live_type(v, "type") v.require_declaration("class_{c_name}") v.add("{res}->class = &class_{c_name};") - self.generate_init_attr(v, res, mtype) - v.set_finalizer res + if attrs != null then + self.generate_init_attr(v, res, mtype) + v.set_finalizer res + end v.add("return {res};") end v.add("\}") 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}" + undead_types.add(t.mclass_type) + 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 @@ -1031,8 +1168,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 @@ -1040,7 +1199,7 @@ class SeparateCompilerVisitor var res = self.new_var(mtype) if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */") - self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);") + self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);") return res end self.require_declaration("BOX_{valtype.c_name}") @@ -1054,7 +1213,7 @@ class SeparateCompilerVisitor # Bad things will appen! var res = self.new_var(mtype) self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */") - self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);") + self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); fatal_exit(1);") return res end end @@ -1080,7 +1239,7 @@ class SeparateCompilerVisitor var res = self.new_var(mtype) if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */") - self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);") + self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);") return res end self.require_declaration("BOX_{valtype.c_name}") @@ -1095,11 +1254,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) @@ -1111,25 +1301,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 @@ -1243,20 +1445,41 @@ class SeparateCompilerVisitor end var const_color = mentity.const_color - var call - if not compiler.modelbuilder.toolcontext.opt_trampoline_call.value then - self.require_declaration(const_color) - call = "(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/" + var ress + if res != null then + ress = "{res} = " else + 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) - call = "{callsym}({ss}) /* {mmethod} on {arguments.first.inspect}*/" - end - - if res != null then - self.add("{res} = {call};") + 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.add("{call};") + 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 @@ -1576,7 +1799,7 @@ class SeparateCompilerVisitor self.add("count_type_test_resolved_{tag}++;") end else - self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);") + self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); fatal_exit(1);") end # check color is in table @@ -1618,7 +1841,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 @@ -1628,7 +1851,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}\";" @@ -1652,6 +1875,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}") @@ -1688,6 +1913,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 @@ -1695,6 +1927,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 @@ -1713,13 +1952,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};") @@ -1786,7 +2037,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]};") @@ -1996,6 +2250,9 @@ class SeparateRuntimeFunction 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 @@ -2038,6 +2295,12 @@ class SeparateRuntimeFunction 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 var const_color: String is lazy do return "COLOR_{c_name}" end