X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index 9c03910..4321eb0 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -29,8 +29,17 @@ 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") + # --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 +47,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 +64,8 @@ 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(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 +82,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) @@ -156,6 +175,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 @@ -561,11 +581,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 @@ -1073,25 +1156,31 @@ 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 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 +1193,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 +1259,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 +1293,42 @@ class SeparateCompilerVisitor ss.append(", {a}") end - self.require_declaration(const_color) - var call = "(({runtime_function.c_ret} (*){runtime_function.c_sig})({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 + 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})({arguments.first}->class->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})({arguments.first}->class->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})({arguments.first}->class->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/" end if res0 != null then @@ -1281,7 +1399,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) @@ -1740,7 +1858,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]};") @@ -1881,6 +2002,12 @@ class SeparateRuntimeFunction return sig.to_s end + # 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 @@ -1917,6 +2044,7 @@ class SeparateRuntimeFunction 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} \{") @@ -1942,6 +2070,50 @@ 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 MEntity