X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/global_compiler.nit b/src/compiler/global_compiler.nit index 9a82243..8f3b437 100644 --- a/src/compiler/global_compiler.nit +++ b/src/compiler/global_compiler.nit @@ -59,35 +59,7 @@ redef class ModelBuilder self.toolcontext.info("*** GENERATING C ***", 1) var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis) - compiler.compile_header - - for t in runtime_type_analysis.live_types do - compiler.declare_runtimeclass(t) - end - - compiler.compile_class_names - - # Init instance code (allocate and init-arguments) - for t in runtime_type_analysis.live_types do - if t.ctype == "val*" then - compiler.generate_init_instance(t) - else - compiler.generate_box_instance(t) - end - end - - # The main function of the C - compiler.compile_nitni_global_ref_functions - compiler.compile_main_function - - # Compile until all runtime_functions are visited - while not compiler.todos.is_empty do - var m = compiler.todos.shift - self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3) - m.compile_to_c(compiler) - end - self.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2) - + compiler.do_compilation compiler.display_stats var time1 = get_time @@ -108,20 +80,59 @@ class GlobalCompiler # The result of the RTA (used to know live types and methods) var runtime_type_analysis: RapidTypeAnalysis - init(mainmodule: MModule, modelbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis) + init do - super(mainmodule, modelbuilder) - var file = new_file("{mainmodule.name}.nitgg") + var file = new_file("{mainmodule.c_name}.nitgg") self.header = new CodeWriter(file) - self.runtime_type_analysis = runtime_type_analysis self.live_primitive_types = new Array[MClassType] for t in runtime_type_analysis.live_types do - if t.ctype != "val*" then + if t.is_c_primitive or t.mclass.name == "Pointer" then self.live_primitive_types.add(t) end end end + redef fun do_compilation + do + var compiler = self + + compiler.compile_header + + if mainmodule.model.get_mclasses_by_name("Pointer") != null then + runtime_type_analysis.live_types.add(mainmodule.pointer_type) + end + for t in runtime_type_analysis.live_types do + compiler.declare_runtimeclass(t) + end + + compiler.compile_class_names + + # Init instance code (allocate and init-arguments) + for t in runtime_type_analysis.live_types do + if not t.is_c_primitive then + compiler.generate_init_instance(t) + if t.mclass.kind == extern_kind then + compiler.generate_box_instance(t) + end + else + compiler.generate_box_instance(t) + end + end + + # The main function of the C + compiler.compile_nitni_global_ref_functions + compiler.compile_main_function + + # Compile until all runtime_functions are visited + while not compiler.todos.is_empty do + var m = compiler.todos.shift + modelbuilder.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3) + m.compile_to_c(compiler) + end + modelbuilder.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2) + + end + # Compile class names (for the class_name and output_class_name methods) protected fun compile_class_names do var v = new_visitor @@ -140,7 +151,7 @@ class GlobalCompiler if self.classids.has_key(mtype) then return self.classids[mtype] end - print "No classid for {mtype}" + print_error "No classid for {mtype}" abort end @@ -157,7 +168,7 @@ class GlobalCompiler # Subset of runtime_type_analysis.live_types that contains only primitive types # Used to implement the equal test - var live_primitive_types: Array[MClassType] + var live_primitive_types: Array[MClassType] is noinit # Add a new todo task fun todo(m: AbstractRuntimeFunction) @@ -193,11 +204,28 @@ class GlobalCompiler v.add_decl("{mtype.arguments.first.ctype} values[1];") end - if mtype.ctype != "val*" then + if all_routine_types_name.has(mtype.mclass.name) then + v.add_decl("val* recv;") + var c_args = ["val* self"] + var c_ret = "void" + var k = mtype.arguments.length + if mtype.mclass.name.has("Fun") then + c_ret = mtype.arguments.last.ctype + k -= 1 + end + for i in [0..k[ do + var t = mtype.arguments[i] + c_args.push("{t.ctype} p{i}") + end + var c_sig = c_args.join(", ") + v.add_decl("{c_ret} (*method)({c_sig});") + end + + if mtype.ctype_extern != "val*" then # Is the Nit type is native then the struct is a box with two fields: # * the `classid` to be polymorph # * the `value` that contains the native value. - v.add_decl("{mtype.ctype} value;") + v.add_decl("{mtype.ctype_extern} value;") end # Collect all attributes and associate them a field in the structure. @@ -217,17 +245,33 @@ class GlobalCompiler fun generate_init_instance(mtype: MClassType) do assert self.runtime_type_analysis.live_types.has(mtype) - assert mtype.ctype == "val*" + assert not mtype.is_c_primitive var v = self.new_visitor var is_native_array = mtype.mclass.name == "NativeArray" - + var is_routine_ref = all_routine_types_name.has(mtype.mclass.name) var sig if is_native_array then sig = "int length" else sig = "void" end + if is_routine_ref then + var c_args = ["val* self"] + var c_ret = "void" + var k = mtype.arguments.length + if mtype.mclass.name.has("Fun") then + c_ret = mtype.arguments.last.ctype + k -= 1 + end + for i in [0..k[ do + var t = mtype.arguments[i] + c_args.push("{t.ctype} p{i}") + end + # The underlying method signature + var method_sig = "{c_ret} (*method)({c_args.join(", ")})" + sig = "val* recv, {method_sig}" + end self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig});") v.add_decl("/* allocate {mtype} */") @@ -235,12 +279,15 @@ class GlobalCompiler var res = v.new_var(mtype) res.is_exact = true if is_native_array then - var mtype_elt = mtype.arguments.first - v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));") + v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}) + length*sizeof(val*));") v.add("((struct {mtype.c_name}*){res})->length = length;") else v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}));") end + if is_routine_ref then + v.add("((struct {mtype.c_name}*){res})->recv = recv;") + v.add("((struct {mtype.c_name}*){res})->method = method;") + end v.add("{res}->classid = {self.classid(mtype)};") self.generate_init_attr(v, res, mtype) @@ -252,7 +299,6 @@ class GlobalCompiler fun generate_box_instance(mtype: MClassType) do assert self.runtime_type_analysis.live_types.has(mtype) - assert mtype.ctype != "val*" var v = self.new_visitor self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});") @@ -293,16 +339,16 @@ class GlobalCompilerVisitor 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 return self.new_expr("((struct {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 var valtype = value.mtype.as(MClassType) var res = self.new_var(mtype) if 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.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */") @@ -313,11 +359,39 @@ class GlobalCompilerVisitor # 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 + + redef fun unbox_extern(value, mtype) + do + if mtype isa MClassType and mtype.mclass.kind == extern_kind and + mtype.mclass.name != "CString" then + var res = self.new_var_extern(mtype) + self.add "{res} = ((struct {mtype.c_name}*){value})->value; /* unboxing {value.mtype} */" return res + else + return value end end + redef fun box_extern(value, mtype) + do + if not mtype isa MClassType or mtype.mclass.kind != extern_kind or + mtype.mclass.name == "CString" then return value + + var valtype = value.mtype.as(MClassType) + var res = self.new_var(mtype) + if 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\"); fatal_exit(1);") + return res + end + self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */") + return res + end + # The runtime types that are acceptable for a given receiver. fun collect_types(recv: RuntimeVariable): Array[MClassType] do @@ -348,41 +422,106 @@ class GlobalCompilerVisitor var recv = "((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->values" if pname == "[]" then self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null))) - return + return true else if pname == "[]=" then self.add("{recv}[{arguments[1]}]={arguments[2]};") - return + return true else if pname == "length" then self.ret(self.new_expr("((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->length", ret_type.as(not null))) - return + return true else if pname == "copy_to" then var recv1 = "((struct {arguments[1].mcasttype.c_name}*){arguments[1]})->values" self.add("memmove({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));") - return + return true + else if pname == "memmove" then + # fun memmove(start: Int, length: Int, dest: NativeArray[E], dest_start: Int) is intern do + var recv1 = "((struct {arguments[3].mcasttype.c_name}*){arguments[3]})->values" + self.add("memmove({recv1}+{arguments[4]}, {recv}+{arguments[1]}, {arguments[2]}*sizeof({elttype.ctype}));") + return true end + return false end - redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable + redef fun native_array_instance(elttype, length) do - var ret_type = self.get_class("NativeArray").get_mtype([elttype]) - return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type) + var ret_type = mmodule.native_array_type(elttype) + ret_type = anchor(ret_type).as(MClassType) + length = autobox(length, compiler.mainmodule.int_type) + return self.new_expr("NEW_{ret_type.c_name}((int){length})", ret_type) end - redef fun calloc_array(ret_type, arguments) + redef fun native_array_get(nat, i) do - self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type)) + var recv = "((struct {nat.mcasttype.c_name}*){nat})->values" + var ret_type = nat.mcasttype.as(MClassType).arguments.first + return self.new_expr("{recv}[{i}]", ret_type) end + redef fun native_array_set(nat, i, val) + do + var recv = "((struct {nat.mcasttype.c_name}*){nat})->values" + self.add("{recv}[{i}]={val};") + end + + redef fun routine_ref_instance(routine_mclass_type, recv, mmethoddef) + do + var method = new CustomizedRuntimeFunction(mmethoddef, recv.mcasttype.as(MClassType)) + var my_recv = recv + if recv.mtype.is_c_primitive then + var object_type = mmodule.object_type + my_recv = autobox(recv, object_type) + end + var thunk = new CustomizedThunkFunction(mmethoddef, my_recv.mtype.as(MClassType)) + thunk.polymorph_call_flag = not my_recv.is_exact #true + #thunk.force_polymorphism = not my_recv.is_exact + compiler.todo(method) + compiler.todo(thunk) + + var res = self.new_expr("NEW_{routine_mclass_type.c_name}({my_recv}, &{thunk.c_name})", routine_mclass_type) + return res + end + + redef fun routine_ref_call(mmethoddef, arguments) + do + var routine = arguments.first + var routine_type = routine.mtype.as(MClassType) + var routine_class = routine_type.mclass + var underlying_recv = "((struct {routine.mcasttype.c_name}*){routine})->recv" + var underlying_method = "((struct {routine.mcasttype.c_name}*){routine})->method" + adapt_signature(mmethoddef, arguments) + arguments.shift + var ss = "{underlying_recv}" + if arguments.length > 0 then + ss = "{ss}, {arguments.join(", ")}" + end + arguments.unshift routine + + var ret_mtype = mmethoddef.msignature.return_mtype + + if ret_mtype != null then + # TODO check for separate compiler + ret_mtype = resolve_for(ret_mtype, routine) + # var temp = ret_mtype + # If mmethoddef has a return type, use the type defined + # in the routine instance instead. + #ret_mtype = routine_type.arguments.last + end + var callsite = "{underlying_method}({ss})" + if ret_mtype != null then + var subres = new_expr("{callsite}", ret_mtype) + ret(subres) + else + add("{callsite};") + end + end + redef fun send(m, args) do var types = self.collect_types(args.first) var res: nullable RuntimeVariable var ret = m.intro.msignature.return_mtype - if m.is_new then - ret = args.first.mtype - res = self.new_var(ret) - else if ret == null then + if ret == null then res = null else ret = self.resolve_for(ret, args.first) @@ -390,9 +529,13 @@ class GlobalCompilerVisitor end self.add("/* send {m} on {args.first.inspect} */") - if args.first.mtype.ctype != "val*" then + if args.first.mtype.is_c_primitive then var mclasstype = args.first.mtype.as(MClassType) if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then + self.add("/* skip, dead class {mclasstype} */") + return res + end + if not mclasstype.has_mproperty(self.compiler.mainmodule, m) then self.add("/* skip, no method {m} */") return res end @@ -405,7 +548,7 @@ class GlobalCompilerVisitor if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException self.add("if ({args.first} == NULL) \{ /* Special null case */") - if m.name == "==" then + if m.name == "==" or m.name == "is_same_instance" then assert res != null if args[1].mcasttype isa MNullableType then self.add("{res} = ({args[1]} == NULL);") @@ -441,7 +584,7 @@ class GlobalCompilerVisitor var defaultpropdef: nullable MMethodDef = null for t in types do var propdef = m.lookup_first_definition(self.compiler.mainmodule, t) - if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then + if propdef.mclassdef.mclass.name == "Object" and not t.is_c_primitive then defaultpropdef = propdef continue end @@ -469,7 +612,7 @@ class GlobalCompilerVisitor fun check_valid_reciever(recvtype: MClassType) do if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return - print "{recvtype} is not a live type" + print_error "{recvtype} is not a live type" abort end @@ -479,51 +622,24 @@ class GlobalCompilerVisitor private fun get_recvtype(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): MClassType do check_valid_reciever(recvtype) - #debug("call {m} on {recvtype} on {args.first}:{args.first.mtype}") - if m.mproperty.is_toplevel then - # Do not customize top-level methods - recvtype = m.mclassdef.bound_mtype - end return recvtype end - # Subpart of old call function - # Gets the receiver boxed and casted if necessary - private fun get_recv(recvtype: MClassType, args: Array[RuntimeVariable]): RuntimeVariable + redef fun call(m, recvtype, args) do - return self.autoadapt(self.autobox(args.first, recvtype), recvtype) - end + var recv_type = get_recvtype(m, recvtype, args) + var recv = self.autoadapt(self.autobox(args.first, recvtype), recvtype) + if m.is_extern then recv = unbox_extern(recv, recv_type) + + args = args.to_a + args.first = recv - # Finalizes a call to a method ´m´ on type ´recvtype´ with arguments ´args´ - private fun finalize_call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable - do assert args.length == m.msignature.arity + 1 else debug("Invalid arity for {m}. {args.length} arguments given.") var rm = new CustomizedRuntimeFunction(m, recvtype) return rm.call(self, args) end - redef fun call(m, recvtype, args) - do - var recv_type = get_recvtype(m, recvtype, args) - var recv = get_recv(recv_type, args) - var new_args = args.to_a - self.varargize(m, m.msignature.as(not null), new_args) - new_args.first = recv - return finalize_call(m, recv_type, new_args) - end - - # Does a call without encapsulating varargs into an array - # Avoids multiple encapsulation when calling a super in a variadic function - fun call_without_varargize(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable - do - var recv_type = get_recvtype(m, recvtype, args) - var recv = get_recv(recv_type, args) - var new_args = args.to_a - new_args.first = recv - return finalize_call(m, recv_type, new_args) - end - redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable do var types = self.collect_types(args.first) @@ -538,14 +654,14 @@ class GlobalCompilerVisitor end self.add("/* super {m} on {args.first.inspect} */") - if args.first.mtype.ctype != "val*" then + if args.first.mtype.is_c_primitive then var mclasstype = args.first.mtype.as(MClassType) if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then self.add("/* skip, no method {m} */") return res end var propdef = m.lookup_next_definition(self.compiler.mainmodule, mclasstype) - var res2 = self.call_without_varargize(propdef, mclasstype, args) + var res2 = self.call(propdef, mclasstype, args) if res != null then self.assign(res, res2.as(not null)) return res end @@ -567,7 +683,7 @@ class GlobalCompilerVisitor else self.add("case {self.compiler.classid(t)}: /* test {t} */") end - var res2 = self.call_without_varargize(propdef, t, args) + var res2 = self.call(propdef, t, args) if res != null then self.assign(res, res2.as(not null)) self.add "break;" end @@ -583,8 +699,9 @@ class GlobalCompilerVisitor do var recv = args.first for i in [0..m.msignature.arity[ do - var t = m.msignature.mparameters[i].mtype - if i == m.msignature.vararg_rank then + var mp = m.msignature.mparameters[i] + var t = mp.mtype + if mp.is_vararg then t = args[i+1].mtype end t = self.resolve_for(t, recv) @@ -592,6 +709,20 @@ class GlobalCompilerVisitor end end + redef fun unbox_signature_extern(m, args) + do + var recv = args.first + for i in [0..m.msignature.arity[ do + var mp = m.msignature.mparameters[i] + var t = mp.mtype + if mp.is_vararg then + t = args[i+1].mtype + end + t = self.resolve_for(t, recv) + if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t) + end + end + # FIXME: this is currently buggy since recv is not exact redef fun vararg_instance(mpropdef, recv, varargs, elttype) do @@ -601,9 +732,9 @@ class GlobalCompilerVisitor fun bugtype(recv: RuntimeVariable) do - if recv.mtype.ctype != "val*" then return + if recv.mtype.is_c_primitive then return self.add("PRINT_ERROR(\"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");") - self.add("show_backtrace(1);") + self.add("fatal_exit(1);") end redef fun isset_attribute(a, recv) @@ -632,7 +763,7 @@ class GlobalCompilerVisitor ta = self.resolve_for(ta, recv2) var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta) if not ta isa MNullableType then - if ta.ctype == "val*" then + if not ta.is_c_primitive then self.add("{res} = ({attr} != NULL);") else self.add("{res} = 1; /*NOTYET isset on primitive attributes*/") @@ -678,7 +809,7 @@ class GlobalCompilerVisitor ta = self.resolve_for(ta, recv2) var res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta) if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then - if ta.ctype == "val*" then + if not ta.is_c_primitive then self.add("if ({res2} == NULL) \{") self.add_abort("Uninitialized attribute {a.name}") self.add("\}") @@ -754,7 +885,7 @@ class GlobalCompilerVisitor var res = self.new_var(bool_type) self.add("/* isa {mtype} on {value.inspect} */") - if value.mtype.ctype != "val*" then + if value.mtype.is_c_primitive then if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then self.add("{res} = 1;") else @@ -789,14 +920,14 @@ class GlobalCompilerVisitor redef fun is_same_type_test(value1, value2) do var res = self.new_var(bool_type) - if value2.mtype.ctype == "val*" then - if value1.mtype.ctype == "val*" then + if not value2.mtype.is_c_primitive then + if not value1.mtype.is_c_primitive then self.add "{res} = {value1}->classid == {value2}->classid;" else self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;" end else - if value1.mtype.ctype == "val*" then + if not value1.mtype.is_c_primitive then self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};" else if value1.mcasttype == value2.mcasttype then self.add "{res} = 1;" @@ -811,7 +942,7 @@ class GlobalCompilerVisitor do var res = self.get_name("var_class_name") self.add_decl("const char* {res};") - if value.mtype.ctype == "val*" then + if not value.mtype.is_c_primitive then self.add "{res} = class_names[{value}->classid];" else self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];" @@ -822,15 +953,15 @@ class GlobalCompilerVisitor 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 var mtype1 = value1.mtype.as(MClassType) @@ -846,6 +977,15 @@ class GlobalCompilerVisitor if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)" end + + if self.compiler.mainmodule.model.get_mclasses_by_name("Pointer") != null then + var pointer_type = self.compiler.mainmodule.pointer_type + if value1.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) or + value2.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) then + s.add "(((struct {pointer_type.c_name}*){value1})->value == ((struct {pointer_type.c_name}*){value2})->value)" + end + end + if s.is_empty then self.add("{res} = {value1} == {value2};") else @@ -858,10 +998,10 @@ class GlobalCompilerVisitor redef fun array_instance(array, elttype) do elttype = self.anchor(elttype) - var arraytype = self.get_class("Array").get_mtype([elttype]) + var arraytype = mmodule.array_type(elttype) var res = self.init_instance(arraytype) self.add("\{ /* {res} = array_instance Array[{elttype}] */") - var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype])) + var nat = self.new_var(mmodule.native_array_type(elttype)) nat.is_exact = true self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});") for i in [0..array.length[ do @@ -875,7 +1015,7 @@ class GlobalCompilerVisitor end end -# A runtime function customized on a specific monomrph receiver type +# A runtime function customized on a specific monomorph receiver type private class CustomizedRuntimeFunction super AbstractRuntimeFunction @@ -886,12 +1026,6 @@ private class CustomizedRuntimeFunction # (usually is a live type but no strong guarantee) var recv: MClassType - init(mmethoddef: MMethodDef, recv: MClassType) - do - super(mmethoddef) - self.recv = recv - end - redef fun build_c_name do var res = self.c_name_cache @@ -926,88 +1060,85 @@ private class CustomizedRuntimeFunction end end - # compile the code customized for the reciever - redef fun compile_to_c(compiler) - do - var recv = self.recv - var mmethoddef = self.mmethoddef - if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then - print("problem: why do we compile {self} for {recv}?") - abort - end - - var v = compiler.new_visitor - var selfvar = new RuntimeVariable("self", recv, recv) - if compiler.runtime_type_analysis.live_types.has(recv) then - selfvar.is_exact = true - end - var arguments = new Array[RuntimeVariable] - var frame = new Frame(v, mmethoddef, recv, arguments) - v.frame = frame - - var sig = new FlatBuffer - var comment = new FlatBuffer - var ret = mmethoddef.msignature.return_mtype - if ret != null then - ret = v.resolve_for(ret, selfvar) - sig.append("{ret.ctype} ") - else if mmethoddef.mproperty.is_new then - ret = recv - sig.append("{ret.ctype} ") - else - sig.append("void ") - end - sig.append(self.c_name) - sig.append("({recv.ctype} {selfvar}") - comment.append("(self: {recv}") - arguments.add(selfvar) - for i in [0..mmethoddef.msignature.arity[ do - var mtype = mmethoddef.msignature.mparameters[i].mtype - if i == mmethoddef.msignature.vararg_rank then - mtype = v.get_class("Array").get_mtype([mtype]) - end - mtype = v.resolve_for(mtype, selfvar) - 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.header.add_decl("{sig};") - - v.add_decl("/* method {self} for {comment} */") - v.add_decl("{sig} \{") - #v.add("printf(\"method {self} for {comment}\\n\");") - if ret != null then - frame.returnvar = v.new_var(ret) - end - frame.returnlabel = v.get_name("RET_LABEL") + redef fun recv_mtype + do + return recv + end - mmethoddef.compile_inside_to_c(v, arguments) + redef var return_mtype - v.add("{frame.returnlabel.as(not null)}:;") - if ret != null then - v.add("return {frame.returnvar.as(not null)};") + redef fun resolve_receiver(v) + do + var selfvar = new RuntimeVariable("self", recv, recv) + if v.compiler.runtime_type_analysis.live_types.has(recv) then + selfvar.is_exact = true 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})" - end + return selfvar + end + + redef fun resolve_return_mtype(v) + do + var selfvar = v.frame.selfvar + if has_return then + var ret = msignature.return_mtype.as(not null) + return_mtype = v.resolve_for(ret, selfvar) + end + end + redef fun resolve_ith_parameter(v, i) + do + var selfvar = v.frame.selfvar + var mp = msignature.mparameters[i] + var mtype = mp.mtype + if mp.is_vararg then + mtype = v.mmodule.array_type(mtype) + end + mtype = v.resolve_for(mtype, selfvar) + return new RuntimeVariable("p{i}", mtype, mtype) + end + + redef fun declare_signature(v, sig) + do + v.compiler.header.add_decl("{sig};") + end + + redef fun end_compile_to_c(v) + do + if not self.c_name.has_substring("VIRTUAL", 0) then v.compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" + end redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do var ret = self.mmethoddef.msignature.return_mtype - if self.mmethoddef.mproperty.is_new then - ret = recv - end if ret != null then ret = v.resolve_for(ret, arguments.first) end - if self.mmethoddef.can_inline(v) then - var frame = new Frame(v, self.mmethoddef, self.recv, arguments) + + # TODO: remove this guard when gcc warning issue (#2781) is resolved + # WARNING: the next two lines of code is used to prevent inlining. + # Inlining of a callref seems to work all the time. However, + # it will produce some deadcode in certain scenarios (when using nullable type). + # + # ~~~~nitish + # class A[E] + # fun toto(x: E) + # do + # ...do something with x... + # end + # end + # end + # var a = new A[nullable Int] + # var f = &a.toto + # f.call(null) <-- Will produce a proper C callsite, but it will + # -- produce unreachable (dead code) for type checking + # -- and covariance. Thus, creating warnings when + # -- compiling in global. However, if you ignore + # -- those warnings, the binary works perfectly fine. + # ~~~~ + var intromclassdef = self.mmethoddef.mproperty.intro_mclassdef + var is_callref = v.compiler.all_routine_types_name.has(intromclassdef.name) + + if self.mmethoddef.can_inline(v) and not is_callref then + var frame = new StaticFrame(v, self.mmethoddef, self.recv, arguments) frame.returnlabel = v.get_name("RET_LABEL") if ret != null then frame.returnvar = v.new_var(ret) @@ -1033,3 +1164,67 @@ private class CustomizedRuntimeFunction end end end + +class CustomizedThunkFunction + super ThunkFunction + super CustomizedRuntimeFunction + + #var force_polymorphism = false + + redef fun c_name + do + return "THUNK_" + super + end + + redef fun hash + do + return super + c_name.hash + end + + redef fun resolve_receiver(v) + do + var res = super(v) + if res.is_exact then res.is_exact = not polymorph_call_flag + return res + end + + redef fun target_recv + do + # If the targeted method was introduced by a primitive type, + # then target_recv must be set to it. Otherwise, there will + # be a missing cast. Here's an example: + # + # ~~~~nitish + # class Int + # fun mult_by(x:Int):Int do return x * self + # end + # + # var f = &10.mult_by + # ~~~~ + # Here the thunk `f` must box the receiver `10` into an object. + # This is due to the memory representation of a call ref which + # has a pointer to an opaque type `val*`: + # + # ```C + # struct Mult_by_callref_struct { + # classid; + # // The receiver `10` would be here + # val* recv; + # // the targeted receiver is a `long` + # long (*pointer_to_mult_by)(long, long); + # } + # ``` + # + # Thus, every primitive type must be boxed into an `Object` when + # instantiating a callref. + # + # However, if the underlying method was introduced by a primitive + # type then a cast must be invoked to convert our boxed receiver + # to its original primitive type. + var intro_recv = mmethoddef.mproperty.intro_mclassdef.bound_mtype + if intro_recv.is_c_primitive then + return intro_recv + end + return recv_mtype + end +end