self.toolcontext.info("*** GENERATING C ***", 1)
var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis)
+ compiler.do_compilation
+ compiler.display_stats
+
+ var time1 = get_time
+ self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
+ write_and_make(compiler)
+ end
+end
+
+# Compiler that use global compilation and perform hard optimisations like:
+# * customization
+# * switch dispatch
+# * inlining
+class GlobalCompiler
+ super AbstractCompiler
+
+ redef type VISITOR: GlobalCompilerVisitor
+
+ # The result of the RTA (used to know live types and methods)
+ var runtime_type_analysis: RapidTypeAnalysis
+
+ init
+ do
+ var file = new_file("{mainmodule.c_name}.nitgg")
+ self.header = new CodeWriter(file)
+ self.live_primitive_types = new Array[MClassType]
+ for t in runtime_type_analysis.live_types do
+ 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
# Init instance code (allocate and init-arguments)
for t in runtime_type_analysis.live_types do
- if t.ctype == "val*" then
+ if not t.is_c_primitive then
compiler.generate_init_instance(t)
if t.mclass.kind == extern_kind then
compiler.generate_box_instance(t)
# 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)
+ modelbuilder.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.display_stats
-
- var time1 = get_time
- self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
- write_and_make(compiler)
- end
-end
-
-# Compiler that use global compilation and perform hard optimisations like:
-# * customization
-# * switch dispatch
-# * inlining
-class GlobalCompiler
- super AbstractCompiler
-
- redef type VISITOR: GlobalCompilerVisitor
+ modelbuilder.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2)
- # The result of the RTA (used to know live types and methods)
- var runtime_type_analysis: RapidTypeAnalysis
-
- init
- do
- var file = new_file("{mainmodule.c_name}.nitgg")
- self.header = new CodeWriter(file)
- self.live_primitive_types = new Array[MClassType]
- for t in runtime_type_analysis.live_types do
- if t.ctype != "val*" or t.mclass.name == "Pointer" then
- self.live_primitive_types.add(t)
- end
- end
end
# Compile class names (for the class_name and output_class_name methods)
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
v.add_decl("{mtype.arguments.first.ctype} values[1];")
end
+ 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
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} */")
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)
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} */")
# 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 != "NativeString" then
+ 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
redef fun box_extern(value, mtype)
do
if not mtype isa MClassType or mtype.mclass.kind != extern_kind or
- mtype.mclass.name == "NativeString" then return value
+ 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\"); 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}); /* boxing {value.mtype} */")
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
- end
+ 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])
+ var ret_type = mmodule.native_array_type(elttype)
ret_type = anchor(ret_type).as(MClassType)
- return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type)
+ 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, callsite)
+ do
+ var mmethoddef = callsite.mpropdef
+ 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
+ compiler.todo(method)
+ compiler.todo(thunk)
+ var ret_type = self.anchor(routine_mclass_type).as(MClassType)
+ var res = self.new_expr("NEW_{ret_type.c_name}({my_recv}, &{thunk.c_name})", ret_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
+ ret_mtype = resolve_for(ret_mtype, routine)
+ 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)
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
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);")
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
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
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
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} */")
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)
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)
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)
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*/")
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("\}")
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
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;"
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))}];"
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)
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
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
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
+ redef fun recv_mtype
+ do
+ return recv
+ 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
+ redef var return_mtype
- 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
- 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")
-
- 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)};")
+ 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
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)
end
end
end
+
+# Thunk implementation for global compiler.
+# For more detail see `abstract_compiler::ThunkFunction` documentation.
+class CustomizedThunkFunction
+ super ThunkFunction
+ super CustomizedRuntimeFunction
+
+ 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