import abstract_compiler
import layout_builders
import rapid_type_analysis
-import compiler_ffi
# Add separate compiler specific options
redef class ToolContext
self.header.add_decl("struct instance_{c_instance_name} \{")
self.header.add_decl("const struct type *type;")
self.header.add_decl("const struct class *class;")
- # NativeArrays are just a instance header followed by an array of values
+ # NativeArrays are just a instance header followed by a length and an array of values
+ self.header.add_decl("int length;")
self.header.add_decl("val* values[0];")
self.header.add_decl("\};")
self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
v.add_decl("/* allocate {mtype} */")
v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
- var res = v.new_named_var(mtype, "self")
- res.is_exact = true
+ var res = v.get_name("self")
+ v.add_decl("struct instance_{c_instance_name} *{res};")
var mtype_elt = mtype.arguments.first
v.add("{res} = nit_alloc(sizeof(struct instance_{c_instance_name}) + length*sizeof({mtype_elt.ctype}));")
v.add("{res}->type = type;")
hardening_live_type(v, "type")
v.require_declaration("class_{c_name}")
v.add("{res}->class = &class_{c_name};")
- v.add("return {res};")
+ v.add("{res}->length = length;")
+ v.add("return (val*){res};")
v.add("\}")
return
end
self.header.add_decl("struct nitni_instance \{struct instance *value;\};")
end
- redef fun finalize_ffi_for_module(nmodule)
+ redef fun finalize_ffi_for_module(mmodule)
do
var old_module = self.mainmodule
- self.mainmodule = nmodule.mmodule.as(not null)
+ self.mainmodule = mmodule
super
self.mainmodule = old_module
end
self.require_declaration("BOX_{valtype.c_name}")
self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
return res
- else if value.mtype.cname_blind == "void*" and mtype.cname_blind == "void*" then
+ else if value.mtype.ctype == "void*" and mtype.ctype == "void*" then
return value
else
# Bad things will appen!
do
var rta = compiler.runtime_type_analysis
var recv = args.first.mtype
- if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and recv isa MClassType then
+ if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null then
var tgs = rta.live_targets(callsite)
if tgs.length == 1 then
# DIRECT CALL
var mmethod = callsite.mproperty
self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
- return call(tgs.first, recv, args)
+ var res0 = before_send(mmethod, args)
+ var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
+ if res0 != null then
+ assert res != null
+ self.assign(res0, res)
+ res = res0
+ end
+ add("\}") # close the before_send
+ return res
end
end
return super
return table_send(mmethod, arguments, mmethod.const_color)
end
- private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+ # Handel common special cases before doing the effective method invocation
+ # This methods handle the `==` and `!=` methods and the case of the null receiver.
+ # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
+ # Client must not forget to close the } after them.
+ #
+ # The value returned is the result of the common special cases.
+ # If not null, client must compine it with the result of their own effective method invocation.
+ #
+ # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
+ # is generated to cancel the effective method invocation that will follow
+ # TODO: find a better approach
+ private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
do
- compiler.modelbuilder.nb_invok_by_tables += 1
-
- assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
-
- 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 mmethod.is_new then
- ret = arguments.first.mtype
- res = self.new_var(ret)
- else if ret == null then
- res = null
- else
- res = self.new_var(ret)
- end
-
- var s = new Buffer
- var ss = new Buffer
-
+ var res: nullable RuntimeVariable = null
var recv = arguments.first
- s.append("val*")
- ss.append("{recv}")
- for i in [0..msignature.arity[ do
- var a = arguments[i+1]
- var t = msignature.mparameters[i].mtype
- 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 consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or mmethod.name == "==" or mmethod.name == "!="
var maybenull = recv.mcasttype isa MNullableType and consider_null
if maybenull then
self.add("if ({recv} == NULL) \{")
if mmethod.name == "==" then
- assert res != null
+ res = self.new_var(bool_type)
var arg = arguments[1]
if arg.mcasttype isa MNullableType then
self.add("{res} = ({arg} == NULL);")
self.add("{res} = 0; /* {arg.inspect} cannot be null */")
end
else if mmethod.name == "!=" then
- assert res != null
+ res = self.new_var(bool_type)
var arg = arguments[1]
if arg.mcasttype isa MNullableType then
self.add("{res} = ({arg} != NULL);")
self.add_abort("Receiver is null")
end
self.add("\} else \{")
+ else
+ self.add("\{")
end
if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
- assert res != null
+ 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)
var arg = arguments[1]
if arg.mcasttype isa MNullType then
else
self.add("{res} = 1; /* arg is null and recv is not */")
end
- if maybenull then
- self.add("\}")
- end
- return res
+ self.add("\}") # closes the null case
+ self.add("if (0) \{") # what follow is useless, CC will drop it
end
end
+ return res
+ end
+
+ private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+ do
+ compiler.modelbuilder.nb_invok_by_tables += 1
+ if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
+
+ assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
+ var recv = arguments.first
+
+ var res0 = before_send(mmethod, arguments)
+
+ 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 mmethod.is_new then
+ ret = arguments.first.mtype
+ res = self.new_var(ret)
+ else if ret == null then
+ res = null
+ else
+ 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]
+ var t = msignature.mparameters[i].mtype
+ 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.add("{call};")
end
- if maybenull then
- self.add("\}")
+ if res0 != null then
+ assert res != null
+ assign(res0,res)
+ res = res0
end
+ self.add("\}") # closes the null case
+
return res
end
if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
(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)
frame.returnlabel = self.get_name("RET_LABEL")
frame.returnvar = res
return res
end
compiler.modelbuilder.nb_invok_by_direct += 1
+ if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
# Autobox arguments
self.adapt_signature(mmethoddef, arguments)
self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
# Check for Uninitialized attribute
- if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+ if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
self.add("if (unlikely({res} == NULL)) \{")
self.add_abort("Uninitialized attribute {a.name}")
self.add("\}")
self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
# Check for Uninitialized attribute
- if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+ if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
self.add("if (unlikely({res} == NULL)) \{")
self.add_abort("Uninitialized attribute {a.name}")
self.add("\}")
else if pname == "[]=" then
self.add("{recv}[{arguments[1]}]={arguments[2]};")
return
+ else if pname == "length" then
+ self.ret(self.new_expr("((struct instance_{nclass.c_instance_name}*){arguments[0]})->length", ret_type.as(not null)))
+ return
else if pname == "copy_to" then
var recv1 = "((struct instance_{nclass.c_instance_name}*){arguments[1]})->values"
self.add("memcpy({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
- var sig = new Buffer
- var comment = new Buffer
+ var sig = new FlatBuffer
+ var comment = new FlatBuffer
var ret = msignature.return_mtype
if ret != null then
sig.append("{ret.ctype} ")
var frame = new Frame(v, mmethoddef, recv, arguments)
v.frame = frame
- var sig = new Buffer
- var comment = new Buffer
+ 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