X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index 6797978..667c196 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -29,12 +29,26 @@ 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 (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") # --inline-some-methods var opt_inline_some_methods = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods") # --direct-call-monomorph var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph") + # --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 @@ -51,6 +65,7 @@ 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_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) @@ -67,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) @@ -90,45 +112,7 @@ redef class ModelBuilder self.toolcontext.info("*** GENERATING C ***", 1) var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis) - compiler.compile_header - - # compile class structures - self.toolcontext.info("Property coloring", 2) - compiler.new_file("{mainmodule.name}.classes") - compiler.do_property_coloring - 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 - compiler.compile_class_to_c(mclass) - end - end - - # The main function of the C - compiler.new_file("{mainmodule.name}.main") - compiler.compile_nitni_global_ref_functions - compiler.compile_main_function - compiler.compile_finalizer_function - - # compile methods - for m in mainmodule.in_importation.greaters do - self.toolcontext.info("Generate C for module {m}", 2) - compiler.new_file("{m.name}.sep") - compiler.compile_module_to_c(m) - end - - # compile live & cast type structures - self.toolcontext.info("Type coloring", 2) - compiler.new_file("{mainmodule.name}.types") - var mtypes = compiler.do_type_coloring - for t in mtypes do - compiler.compile_type_to_c(t) - end - # compile remaining types structures (useless but needed for the symbol resolution at link-time) - for t in compiler.undead_types do - if mtypes.has(t) then continue - compiler.compile_type_to_c(t) - end - + compiler.do_compilation compiler.display_stats var time1 = get_time @@ -156,20 +140,73 @@ class SeparateCompiler private var undead_types: Set[MType] = new HashSet[MType] private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]] - private var type_ids: Map[MType, Int] - private var type_colors: Map[MType, Int] - private var opentype_colors: Map[MType, Int] - protected var method_colors: Map[PropertyLayoutElement, Int] - protected var attr_colors: Map[MAttribute, Int] + private var type_ids: Map[MType, Int] is noinit + private var type_colors: Map[MType, Int] is noinit + private var opentype_colors: Map[MType, Int] is noinit + protected var method_colors: Map[PropertyLayoutElement, Int] is noinit + protected var attr_colors: Map[MAttribute, Int] is noinit - init(mainmodule: MModule, mmbuilder: ModelBuilder, runtime_type_analysis: nullable RapidTypeAnalysis) do - super(mainmodule, mmbuilder) + init do var file = new_file("nit.common") self.header = new CodeWriter(file) - self.runtime_type_analysis = runtime_type_analysis self.compile_box_kinds end + redef fun do_compilation + do + var compiler = self + compiler.compile_header + + var c_name = mainmodule.c_name + + # compile class structures + modelbuilder.toolcontext.info("Property coloring", 2) + compiler.new_file("{c_name}.classes") + compiler.do_property_coloring + 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 + compiler.compile_class_to_c(mclass) + end + end + + # The main function of the C + compiler.new_file("{c_name}.main") + 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 + modelbuilder.toolcontext.info("Generate C for module {m.full_name}", 2) + compiler.new_file("{m.c_name}.sep") + compiler.compile_module_to_c(m) + end + + # compile live & cast type structures + modelbuilder.toolcontext.info("Type coloring", 2) + compiler.new_file("{c_name}.types") + compiler.compile_types + end + + # Color and compile type structures and cast information + fun compile_types + do + var compiler = self + + var mtypes = compiler.do_type_coloring + for t in mtypes do + compiler.compile_type_to_c(t) + end + # compile remaining types structures (useless but needed for the symbol resolution at link-time) + for t in compiler.undead_types do + if mtypes.has(t) then continue + compiler.compile_type_to_c(t) + end + + end + redef fun compile_header_structs do self.header.add_decl("typedef void(*nitmethod_t)(void); /* general C type representing a Nit method. */") self.compile_header_attribute_structs @@ -238,27 +275,21 @@ class SeparateCompiler fun compile_color_const(v: SeparateCompilerVisitor, m: Object, color: Int) do if color_consts_done.has(m) then return - if m isa MProperty then - if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then - self.provide_declaration(m.const_color, "#define {m.const_color} {color}") - else - self.provide_declaration(m.const_color, "extern const int {m.const_color};") - v.add("const int {m.const_color} = {color};") - end - else if m isa MPropDef then + if m isa MEntity then if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then self.provide_declaration(m.const_color, "#define {m.const_color} {color}") - else + else if not modelbuilder.toolcontext.opt_colors_are_symbols.value or not v.compiler.target_platform.supports_linker_script then self.provide_declaration(m.const_color, "extern const int {m.const_color};") v.add("const int {m.const_color} = {color};") - end - else if m isa MType then - if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then - self.provide_declaration(m.const_color, "#define {m.const_color} {color}") else - self.provide_declaration(m.const_color, "extern const int {m.const_color};") - v.add("const int {m.const_color} = {color};") + # The color 'C' is the ``address'' of a false static variable 'XC' + self.provide_declaration(m.const_color, "#define {m.const_color} ((long)&X{m.const_color})\nextern const void X{m.const_color};") + if color == -1 then color = 0 # Symbols cannot be negative, so just use 0 for dead things + # Teach the linker that the address of 'XC' is `color`. + linker_script.add("X{m.const_color} = {color};") end + else + abort end color_consts_done.add(m) end @@ -418,13 +449,12 @@ class SeparateCompiler var live_cast_types = runtime_type_analysis.live_cast_types var mtypes = new HashSet[MType] mtypes.add_all(live_types) - mtypes.add_all(live_cast_types) for c in self.box_kinds.keys do mtypes.add(c.mclass_type) end # Compute colors - var poset = poset_from_mtypes(mtypes) + var poset = poset_from_mtypes(mtypes, live_cast_types) var colorer = new POSetColorer[MType] colorer.colorize(poset) type_ids = colorer.ids @@ -437,12 +467,13 @@ class SeparateCompiler return poset end - private fun poset_from_mtypes(mtypes: Set[MType]): POSet[MType] do + private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do var poset = new POSet[MType] for e in mtypes do poset.add_node(e) - for o in mtypes do + 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 @@ -549,12 +580,75 @@ class SeparateCompiler var r = pd.separate_runtime_function r.compile_to_c(self) var r2 = pd.virtual_runtime_function - r2.compile_to_c(self) + 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 @@ -1063,8 +1157,8 @@ class SeparateCompilerVisitor do var rta = compiler.runtime_type_analysis var mmethod = callsite.mproperty - # TODO: Inlining of new-style constructors - if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and not mmethod.is_root_init then + # 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 @@ -1093,7 +1187,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 @@ -1112,10 +1206,10 @@ class SeparateCompilerVisitor var res: nullable RuntimeVariable = null var recv = arguments.first var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!=" - var maybenull = recv.mcasttype isa MNullableType and consider_null + var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null if maybenull then self.add("if ({recv} == NULL) \{") - if mmethod.name == "==" then + if mmethod.name == "==" or mmethod.name == "is_same_instance" then res = self.new_var(bool_type) var arg = arguments[1] if arg.mcasttype isa MNullableType then @@ -1142,15 +1236,15 @@ class SeparateCompilerVisitor else self.add("\{") end - if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then - if res == null then res = self.new_var(bool_type) - # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants) + if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then + # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants) var arg = arguments[1] if arg.mcasttype isa MNullType then - if mmethod.name == "==" then - self.add("{res} = 0; /* arg is null but recv is not */") - else + if res == null then res = self.new_var(bool_type) + if mmethod.name == "!=" then self.add("{res} = 1; /* arg is null and recv is not */") + else # `==` and `is_same_instance` + self.add("{res} = 0; /* arg is null but recv is not */") end self.add("\}") # closes the null case self.add("if (0) \{") # what follow is useless, CC will drop it @@ -1159,7 +1253,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++;") @@ -1169,8 +1263,10 @@ class SeparateCompilerVisitor var res0 = before_send(mmethod, arguments) + var runtime_function = mmethod.intro.virtual_runtime_function + var msignature = runtime_function.called_signature + var res: nullable RuntimeVariable - var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true) var ret = msignature.return_mtype if ret == null then res = null @@ -1178,10 +1274,8 @@ class SeparateCompilerVisitor res = self.new_var(ret) end - var s = new FlatBuffer var ss = new FlatBuffer - s.append("val*") ss.append("{recv}") for i in [0..msignature.arity[ do var a = arguments[i+1] @@ -1189,21 +1283,46 @@ class SeparateCompilerVisitor if i == msignature.vararg_rank then t = arguments[i+1].mcasttype end - s.append(", {t.ctype}") a = self.autobox(a, t) ss.append(", {a}") end - - var r - if ret == null then r = "void" else r = ret.ctype - self.require_declaration(const_color) - var call = "(({r} (*)({s}))({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 @@ -1234,7 +1353,7 @@ class SeparateCompilerVisitor (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then compiler.modelbuilder.nb_invok_by_inline += 1 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;") - var frame = new Frame(self, mmethoddef, recvtype, arguments) + var frame = new StaticFrame(self, mmethoddef, recvtype, arguments) frame.returnlabel = self.get_name("RET_LABEL") frame.returnvar = res var old_frame = self.frame @@ -1274,7 +1393,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) @@ -1284,11 +1403,11 @@ class SeparateCompilerVisitor # of the method (ie recv) if the static type is unresolved # This is more complex than usual because the unresolved type must not be resolved # with the current receiver (ie self). - # Therefore to isolate the resolution from self, a local Frame is created. + # Therefore to isolate the resolution from self, a local StaticFrame is created. # One can see this implementation as an inlined method of the receiver whose only # job is to allocate the array var old_frame = self.frame - var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv]) + var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv]) self.frame = frame #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}" var res = self.array_instance(varargs, elttype) @@ -1733,7 +1852,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]};") @@ -1767,108 +1889,118 @@ class SeparateCompilerVisitor end redef class MMethodDef - fun separate_runtime_function: AbstractRuntimeFunction + # The C function associated to a mmethoddef + fun separate_runtime_function: SeparateRuntimeFunction do var res = self.separate_runtime_function_cache if res == null then - res = new SeparateRuntimeFunction(self) + var recv = mclassdef.bound_mtype + var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true) + res = new SeparateRuntimeFunction(self, recv, msignature, c_name) self.separate_runtime_function_cache = res end return res end private var separate_runtime_function_cache: nullable SeparateRuntimeFunction - fun virtual_runtime_function: AbstractRuntimeFunction + # The C function associated to a mmethoddef, that can be stored into a VFT of a class + # The first parameter (the reciever) is always typed by val* in order to accept an object value + # The C-signature is always compatible with the intro + fun virtual_runtime_function: SeparateRuntimeFunction do var res = self.virtual_runtime_function_cache if res == null then - res = new VirtualRuntimeFunction(self) + # Because the function is virtual, the signature must match the one of the original class + var intromclassdef = mproperty.intro.mclassdef + var recv = intromclassdef.bound_mtype + + res = separate_runtime_function + if res.called_recv == recv then + self.virtual_runtime_function_cache = res + return res + end + + var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true) + + if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then + self.virtual_runtime_function_cache = res + return res + end + + res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}") self.virtual_runtime_function_cache = res + res.is_thunk = true end return res end - private var virtual_runtime_function_cache: nullable VirtualRuntimeFunction + private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction +end + +redef class MSignature + # Does the C-version of `self` the same than the C-version of `other`? + fun c_equiv(other: MSignature): Bool + do + if self == other then return true + if arity != other.arity then return false + for i in [0..arity[ do + if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false + end + if return_mtype != other.return_mtype then + if return_mtype == null or other.return_mtype == null then return false + if return_mtype.ctype != other.return_mtype.ctype then return false + end + return true + end end # The C function associated to a methoddef separately compiled class SeparateRuntimeFunction super AbstractRuntimeFunction - redef fun build_c_name: String do return "{mmethoddef.c_name}" + # The call-side static receiver + var called_recv: MType - redef fun to_s do return self.mmethoddef.to_s + # The call-side static signature + var called_signature: MSignature - redef fun compile_to_c(compiler) - do - var mmethoddef = self.mmethoddef + # The name on the compiled method + redef var build_c_name: String - var recv = self.mmethoddef.mclassdef.bound_mtype - var v = compiler.new_visitor - var selfvar = new RuntimeVariable("self", recv, recv) - var arguments = new Array[RuntimeVariable] - var frame = new Frame(v, mmethoddef, recv, arguments) - v.frame = frame + # Statically call the original body instead + var is_thunk = false - var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true) + redef fun to_s do return self.mmethoddef.to_s - var sig = new FlatBuffer - var comment = new FlatBuffer - var ret = msignature.return_mtype + # The C return type (something or `void`) + var c_ret: String is lazy do + var ret = called_signature.return_mtype if ret != null then - sig.append("{ret.ctype} ") + return ret.ctype else - sig.append("void ") + return "void" end - sig.append(self.c_name) - sig.append("({selfvar.mtype.ctype} {selfvar}") - comment.append("({selfvar}: {selfvar.mtype}") - arguments.add(selfvar) - for i in [0..msignature.arity[ do - var mtype = msignature.mparameters[i].mtype - if i == msignature.vararg_rank then - mtype = v.get_class("Array").get_mtype([mtype]) + end + + # The C signature (only the parmeter part) + var c_sig: String is lazy do + var sig = new FlatBuffer + sig.append("({called_recv.ctype} self") + for i in [0..called_signature.arity[ do + var mtype = called_signature.mparameters[i].mtype + if i == called_signature.vararg_rank then + mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype]) end - comment.append(", {mtype}") sig.append(", {mtype.ctype} p{i}") - var argvar = new RuntimeVariable("p{i}", mtype, mtype) - arguments.add(argvar) end sig.append(")") - comment.append(")") - if ret != null then - comment.append(": {ret}") - end - compiler.provide_declaration(self.c_name, "{sig};") - - v.add_decl("/* method {self} for {comment} */") - v.add_decl("{sig} \{") - if ret != null then - frame.returnvar = v.new_var(ret) - end - frame.returnlabel = v.get_name("RET_LABEL") - - if recv != arguments.first.mtype then - #print "{self} {recv} {arguments.first}" - end - mmethoddef.compile_inside_to_c(v, arguments) - - v.add("{frame.returnlabel.as(not null)}:;") - if ret != null then - v.add("return {frame.returnvar.as(not null)};") - end - v.add("\}") - if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" + return sig.to_s end -end - -# The C function associated to a methoddef on a primitive type, stored into a VFT of a class -# The first parameter (the reciever) is always typed by val* in order to accept an object value -class VirtualRuntimeFunction - super AbstractRuntimeFunction - redef fun build_c_name: String do return "VIRTUAL_{mmethoddef.c_name}" + # The C type for the function pointer. + var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}" - redef fun to_s do return self.mmethoddef.to_s + # The arguments, as generated by `compile_to_c` + private var arguments: Array[RuntimeVariable] is noinit redef fun compile_to_c(compiler) do @@ -1876,25 +2008,20 @@ class VirtualRuntimeFunction var recv = self.mmethoddef.mclassdef.bound_mtype var v = compiler.new_visitor - var selfvar = new RuntimeVariable("self", v.object_type, recv) + var selfvar = new RuntimeVariable("self", called_recv, recv) var arguments = new Array[RuntimeVariable] - var frame = new Frame(v, mmethoddef, recv, arguments) + var frame = new StaticFrame(v, mmethoddef, recv, arguments) v.frame = frame + var msignature = called_signature + var ret = called_signature.return_mtype + var sig = new FlatBuffer var comment = new FlatBuffer - - # Because the function is virtual, the signature must match the one of the original class - var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef - var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true) - var ret = msignature.return_mtype - if ret != null then - sig.append("{ret.ctype} ") - else - sig.append("void ") - end + sig.append(c_ret) + sig.append(" ") sig.append(self.c_name) - sig.append("({selfvar.mtype.ctype} {selfvar}") + sig.append(c_sig) comment.append("({selfvar}: {selfvar.mtype}") arguments.add(selfvar) for i in [0..msignature.arity[ do @@ -1903,16 +2030,15 @@ class VirtualRuntimeFunction mtype = v.get_class("Array").get_mtype([mtype]) end comment.append(", {mtype}") - sig.append(", {mtype.ctype} p{i}") var argvar = new RuntimeVariable("p{i}", mtype, mtype) arguments.add(argvar) end - sig.append(")") comment.append(")") if ret != null then comment.append(": {ret}") end compiler.provide_declaration(self.c_name, "{sig};") + self.arguments = arguments.to_a v.add_decl("/* method {self} for {comment} */") v.add_decl("{sig} \{") @@ -1921,10 +2047,14 @@ class VirtualRuntimeFunction end frame.returnlabel = v.get_name("RET_LABEL") - var subret = v.call(mmethoddef, recv, arguments) - if ret != null then - assert subret != null - v.assign(frame.returnvar.as(not null), subret) + if is_thunk then + var subret = v.call(mmethoddef, recv, arguments) + if ret != null then + assert subret != null + v.assign(frame.returnvar.as(not null), subret) + end + else + mmethoddef.compile_inside_to_c(v, arguments) end v.add("{frame.returnlabel.as(not null)}:;") @@ -1932,27 +2062,66 @@ class VirtualRuntimeFunction v.add("return {frame.returnvar.as(not null)};") end v.add("\}") - if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}--{mmethoddef.location.line_start})" + compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" end - # TODO ? - redef fun call(v, arguments) do abort + # 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 - fun const_color: String do return "COLOR_{c_name}" +redef class MEntity + var const_color: String is lazy do return "COLOR_{c_name}" end interface PropertyLayoutElement end redef class MProperty super PropertyLayoutElement - fun const_color: String do return "COLOR_{c_name}" end redef class MPropDef super PropertyLayoutElement - fun const_color: String do return "COLOR_{c_name}" end redef class AMethPropdef