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 (faster)", "--colors-are-symbols")
+
# --inline-coloring-numbers
var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
# --inline-some-methods
self.option_context.add_option(self.opt_separate)
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(self.opt_no_shortcut_equate, opt_colors_are_symbols)
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)
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
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)
end
end
self.mainmodule = old_module
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
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
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
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]
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 call = "(({runtime_function.c_ret} (*){runtime_function.c_sig})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
if res != null then
self.add("{res} = {call};")
(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
# 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)
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}"
-
- redef fun to_s do return self.mmethoddef.to_s
redef fun compile_to_c(compiler)
do
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
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
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)}:;")
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
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