X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index b14da6a..8cb75e7 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -29,12 +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", "--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") @@ -42,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 @@ -57,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, opt_trampoline_call, opt_substitute_monomorph) + 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) @@ -74,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) @@ -148,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 @@ -160,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 @@ -201,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 @@ -243,7 +268,7 @@ class SeparateCompiler if mclass.mclass_type.ctype_extern == "val*" then return 0 else if mclass.kind == extern_kind and mclass.name != "NativeString" then - return self.box_kinds[self.mainmodule.get_primitive_class("Pointer")] + return self.box_kinds[self.mainmodule.pointer_type.mclass] else return self.box_kinds[mclass] end @@ -431,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 @@ -446,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) - 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 + + 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) + 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 @@ -485,9 +527,8 @@ 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) @@ -569,30 +610,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 @@ -712,7 +793,7 @@ class SeparateCompiler var v = new_visitor var rta = runtime_type_analysis - var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" and mclass.name != "Pointer" + var is_dead = rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" and mclass.name != "Pointer" v.add_decl("/* runtime class {c_name} */") @@ -741,9 +822,11 @@ 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 + #Build instance struct self.header.add_decl("struct instance_{c_name} \{") self.header.add_decl("const struct type *type;") @@ -861,6 +944,59 @@ 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}" + 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 @@ -1017,11 +1153,33 @@ 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) + 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 + else if not mtype.is_c_primitive then + if value.mtype.is_tagged then + if value.mtype.name == "Int" then + return self.new_expr("(val*)({value}<<2|1)", mtype) + 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 @@ -1029,7 +1187,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}") @@ -1043,7 +1201,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 @@ -1069,7 +1227,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}") @@ -1084,11 +1242,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 not value.mtype.is_c_primitive + 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 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)" + 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 not value.mtype.is_c_primitive then + if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then + var tag = extract_tag(value) + return "({tag}?type_info[{tag}]:{value}->type)" + end return "{value}->type" else compiler.undead_types.add(value.mtype) @@ -1100,28 +1289,40 @@ 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 + 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 @@ -1232,20 +1433,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 @@ -1307,7 +1529,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 @@ -1358,7 +1580,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 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} */") @@ -1410,7 +1632,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("\}") @@ -1439,7 +1661,7 @@ 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_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 @@ -1565,7 +1787,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 @@ -1591,15 +1813,15 @@ class SeparateCompilerVisitor do var res = self.new_var(bool_type) # Swap values to be symetric - if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then + if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then var tmp = value1 value1 = value2 value2 = tmp end - if value1.mtype.ctype != "val*" then + if value1.mtype.is_c_primitive then if value2.mtype == value1.mtype then self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */") - else if value2.mtype.ctype != "val*" then + else if value2.mtype.is_c_primitive then self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/") else var mtype1 = value1.mtype.as(MClassType) @@ -1607,7 +1829,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 @@ -1616,8 +1838,8 @@ class SeparateCompilerVisitor do 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;" + 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 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";" @@ -1631,16 +1853,18 @@ class SeparateCompilerVisitor redef fun equal_test(value1, value2) do var res = self.new_var(bool_type) - if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then + if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then var tmp = value1 value1 = value2 value2 = tmp end - if value1.mtype.ctype != "val*" then + if value1.mtype.is_c_primitive then if value2.mtype == value1.mtype then self.add("{res} = {value1} == {value2};") - else if value2.mtype.ctype != "val*" then + else if value2.mtype.is_c_primitive then self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/") + else if value1.mtype.is_tagged then + self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});") else var mtype1 = value1.mtype.as(MClassType) self.require_declaration("class_{mtype1.c_name}") @@ -1670,20 +1894,34 @@ 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 + 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 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 + 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 @@ -1702,13 +1940,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};") @@ -1723,7 +1973,7 @@ class SeparateCompilerVisitor var t = value.mcasttype.as_notnullable if not t isa MClassType then return false var k = t.mclass.kind - return k == interface_kind or t.ctype != "val*" + return k == interface_kind or t.is_c_primitive end fun maybe_null(value: RuntimeVariable): Bool @@ -1734,8 +1984,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}] */") @@ -1752,7 +2002,7 @@ class SeparateCompilerVisitor redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable do - var mtype = self.get_class("NativeArray").get_mtype([elttype]) + var mtype = mmodule.native_array_type(elttype) self.require_declaration("NEW_{mtype.mclass.c_name}") assert mtype isa MGenericType var compiler = self.compiler @@ -1772,10 +2022,13 @@ class SeparateCompilerVisitor redef fun native_array_def(pname, ret_type, arguments) do var elttype = arguments.first.mtype - var nclass = self.get_class("NativeArray") + var nclass = mmodule.native_array_class var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values" if pname == "[]" then - 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]};") @@ -1790,14 +2043,6 @@ class SeparateCompilerVisitor end end - redef fun calloc_array(ret_type, arguments) - do - var mclass = self.get_class("ArrayCapable") - var ft = mclass.mparameters.first - var res = self.native_array_instance(ft, arguments[1]) - self.ret(res) - end - fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do assert mtype.need_anchor var compiler = self.compiler @@ -1908,7 +2153,7 @@ class SeparateRuntimeFunction for i in [0..called_signature.arity[ do var mtype = called_signature.mparameters[i].mtype if i == called_signature.vararg_rank then - mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype]) + mtype = mmethoddef.mclassdef.mmodule.array_type(mtype) end sig.append(", {mtype.ctype} p{i}") end @@ -1947,7 +2192,7 @@ class SeparateRuntimeFunction for i in [0..msignature.arity[ do var mtype = msignature.mparameters[i].mtype if i == msignature.vararg_rank then - mtype = v.get_class("Array").get_mtype([mtype]) + mtype = v.mmodule.array_type(mtype) end comment.append(", {mtype}") var argvar = new RuntimeVariable("p{i}", mtype, mtype) @@ -1985,13 +2230,16 @@ 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 var selfvar = arguments.first var ret = called_signature.return_mtype - if mmethoddef.is_intro and recv.ctype == "val*" then + if mmethoddef.is_intro and not recv.is_c_primitive then var m = mmethoddef.mproperty var n2 = "CALL_" + m.const_color compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};") @@ -2008,7 +2256,7 @@ 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};") @@ -2027,6 +2275,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