Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Callref compilers #2793

Merged
merged 7 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
423 changes: 227 additions & 196 deletions src/compiler/abstract_compiler.nit

Large diffs are not rendered by default.

177 changes: 175 additions & 2 deletions src/compiler/global_compiler.nit
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,23 @@ class GlobalCompiler
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
Expand Down Expand Up @@ -232,13 +249,29 @@ class GlobalCompiler
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)
Louis-Vincent marked this conversation as resolved.
Show resolved Hide resolved
var sig
if is_native_array then
sig = "int length"
else
sig = "void"
end
if is_routine_ref then
Louis-Vincent marked this conversation as resolved.
Show resolved Hide resolved
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} */")
Expand All @@ -251,6 +284,10 @@ class GlobalCompiler
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)
Expand Down Expand Up @@ -426,6 +463,53 @@ class GlobalCompilerVisitor
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)
Expand Down Expand Up @@ -1023,7 +1107,32 @@ private class CustomizedRuntimeFunction
if ret != null then
ret = v.resolve_for(ret, arguments.first)
end
if self.mmethoddef.can_inline(v) then

# 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.
# ~~~~
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can live with the warning for now, --global it not a popular option. #2781 is problematic because its a new warning and cannot be disabled without breaking previous version of gcc. Maybe we will just disable all C warnings.

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
Expand All @@ -1050,3 +1159,67 @@ private class CustomizedRuntimeFunction
end
end
end

# Thunk implementation for global compiler.
# For more detail see `abstract_compiler::ThunkFunction` documentation.
class CustomizedThunkFunction
Louis-Vincent marked this conversation as resolved.
Show resolved Hide resolved
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
Loading