import abstract_compiler
import layout_builders
import rapid_type_analysis
-import collect_super_sends
-import compiler_ffi
# Add separate compiler specific options
redef class ToolContext
# --no-shortcut-equate
var opt_no_shortcut_equate: OptionBool = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
# --inline-coloring-numbers
- var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids", "--inline-coloring-numbers")
+ var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
+ # --inline-some-methods
+ var opt_inline_some_methods: OptionBool = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
+ # --direct-call-monomorph
+ var opt_direct_call_monomorph: OptionBool = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
+ # --skip-dead-methods
+ var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
+ # --semi-global
+ var opt_semi_global = new OptionBool("Enable all semi-global optimizations", "--semi-global")
+ # --no-colo-dead-methods
+ var opt_no_colo_dead_methods = new OptionBool("Do not colorize dead methods", "--no-colo-dead-methods")
# --use-naive-coloring
var opt_bm_typing: OptionBool = new OptionBool("Colorize items incrementaly, used to simulate binary matrix typing", "--bm-typing")
# --use-mod-perfect-hashing
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_inline_coloring_numbers)
+ 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_no_colo_dead_methods)
self.option_context.add_option(self.opt_bm_typing)
self.option_context.add_option(self.opt_phmod_typing)
self.option_context.add_option(self.opt_phand_typing)
self.option_context.add_option(self.opt_tables_metrics)
end
+
+ redef fun process_options(args)
+ do
+ super
+
+ var tc = self
+ if tc.opt_semi_global.value then
+ tc.opt_inline_coloring_numbers.value = true
+ tc.opt_inline_some_methods.value = true
+ tc.opt_direct_call_monomorph.value = true
+ tc.opt_skip_dead_methods.value = true
+ end
+ end
end
redef class ModelBuilder
for t in mtypes do
compiler.compile_type_to_c(t)
end
+ # compile remaining types structures (useless but needed for the symbol resolution at link-time)
+ for t in compiler.undead_types do
+ if mtypes.has(t) then continue
+ compiler.compile_type_to_c(t)
+ end
compiler.display_stats
self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
write_and_make(compiler)
end
+
+ # Count number of invocations by VFT
+ private var nb_invok_by_tables = 0
+ # Count number of invocations by direct call
+ private var nb_invok_by_direct = 0
+ # Count number of invocations by inlining
+ private var nb_invok_by_inline = 0
end
# Singleton that store the knowledge about the separate compilation process
var runtime_type_analysis: nullable RapidTypeAnalysis
private var undead_types: Set[MType] = new HashSet[MType]
- private var partial_types: Set[MType] = new HashSet[MType]
private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
private var type_layout: nullable Layout[MType]
fun do_property_coloring do
var mclasses = new HashSet[MClass].from(modelbuilder.model.mclasses)
+ var rta = runtime_type_analysis
+
# Layouts
var method_layout_builder: PropertyLayoutBuilder[PropertyLayoutElement]
var attribute_layout_builder: PropertyLayoutBuilder[MAttribute]
attribute_layout_builder = new MPropertyColorer[MAttribute](self.mainmodule, class_layout_builder)
#end
+ # The dead methods, still need to provide a dead color symbol
+ var dead_methods = new Array[MMethod]
+
# lookup properties to build layout with
var mmethods = new HashMap[MClass, Set[PropertyLayoutElement]]
var mattributes = new HashMap[MClass, Set[MAttribute]]
mattributes[mclass] = new HashSet[MAttribute]
for mprop in self.mainmodule.properties(mclass) do
if mprop isa MMethod then
+ if modelbuilder.toolcontext.opt_no_colo_dead_methods.value and rta != null and not rta.live_methods.has(mprop) then
+ dead_methods.add(mprop)
+ continue
+ end
mmethods[mclass].add(mprop)
else if mprop isa MAttribute then
mattributes[mclass].add(mprop)
end
end
+ # Collect all super calls (dead or not)
+ var all_super_calls = new HashSet[MMethodDef]
+ for mmodule in self.mainmodule.in_importation.greaters do
+ for mclassdef in mmodule.mclassdefs do
+ for mpropdef in mclassdef.mpropdefs do
+ if not mpropdef isa MMethodDef then continue
+ if mpropdef.has_supercall then
+ all_super_calls.add(mpropdef)
+ end
+ end
+ end
+ end
+
# lookup super calls and add it to the list of mmethods to build layout with
var super_calls
- if runtime_type_analysis != null then
- super_calls = runtime_type_analysis.live_super_sends
+ if rta != null then
+ super_calls = rta.live_super_sends
else
- super_calls = modelbuilder.collect_super_sends
+ super_calls = all_super_calls
end
+
for mmethoddef in super_calls do
var mclass = mmethoddef.mclassdef.mclass
mmethods[mclass].add(mmethoddef)
self.method_tables = build_method_tables(mclasses, super_calls)
self.compile_color_consts(method_layout.pos)
- # attribute null color to dead supercalls
- for mmodule in self.mainmodule.in_importation.greaters do
- for mclassdef in mmodule.mclassdefs do
- for mpropdef in mclassdef.mpropdefs do
- if mpropdef.has_supercall then
- compile_color_const(new_visitor, mpropdef, -1)
- end
- end
- end
+ # attribute null color to dead methods and supercalls
+ for mproperty in dead_methods do
+ compile_color_const(new_visitor, mproperty, -1)
+ end
+ for mpropdef in all_super_calls do
+ if super_calls.has(mpropdef) then continue
+ compile_color_const(new_visitor, mpropdef, -1)
end
# attributes coloration
if parent == mclass then continue
for mproperty in self.mainmodule.properties(parent) do
if not mproperty isa MMethod then continue
+ if not layout.pos.has_key(mproperty) then continue
var color = layout.pos[mproperty]
if table.length <= color then
for i in [table.length .. color[ do
# then override with local properties
for mproperty in self.mainmodule.properties(mclass) do
if not mproperty isa MMethod then continue
+ if not layout.pos.has_key(mproperty) then continue
var color = layout.pos[mproperty]
if table.length <= color then
for i in [table.length .. color[ do
var mtypes = new HashSet[MType]
mtypes.add_all(self.runtime_type_analysis.live_types)
mtypes.add_all(self.runtime_type_analysis.live_cast_types)
- mtypes.add_all(self.undead_types)
for c in self.box_kinds.keys do
mtypes.add(c.mclass_type)
end
- for mtype in mtypes do
- retrieve_partial_types(mtype)
- end
- mtypes.add_all(self.partial_types)
-
# Typing Layout
var layout_builder: TypingLayoutBuilder[MType]
if modelbuilder.toolcontext.opt_bm_typing.value then
return tables
end
- fun retrieve_partial_types(mtype: MType) do
- # add formal types arguments to mtypes
- if mtype isa MGenericType then
- for ft in mtype.arguments do
- if ft.need_anchor then
- print("Why do we need anchor here ?")
- abort
- end
- self.partial_types.add(ft)
- retrieve_partial_types(ft)
- end
- end
- var mclass_type: MClassType
- if mtype isa MNullableType then
- mclass_type = mtype.mtype.as(MClassType)
- else
- mclass_type = mtype.as(MClassType)
- end
-
- # add virtual types to mtypes
- for vt in self.mainmodule.properties(mclass_type.mclass) do
- if vt isa MVirtualTypeProp then
- var anchored = vt.mvirtualtype.lookup_bound(self.mainmodule, mclass_type).anchor_to(self.mainmodule, mclass_type)
- self.partial_types.add(anchored)
- end
- end
- end
-
# Separately compile all the method definitions of the module
fun compile_module_to_c(mmodule: MModule)
do
for cd in mmodule.mclassdefs do
for pd in cd.mpropdefs do
if not pd isa MMethodDef then continue
+ var rta = runtime_type_analysis
+ if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
#print "compile {pd} @ {cd} @ {mmodule}"
var r = pd.separate_runtime_function
r.compile_to_c(self)
# Globaly compile the type structure of a live type
fun compile_type_to_c(mtype: MType)
do
+ assert not mtype.need_anchor
+ var layout = self.type_layout
+ var is_live = mtype isa MClassType and runtime_type_analysis.live_types.has(mtype)
+ var is_cast_live = runtime_type_analysis.live_cast_types.has(mtype)
var c_name = mtype.c_name
var v = new SeparateCompilerVisitor(self)
v.add_decl("/* runtime type {mtype} */")
# const struct type_X
v.add_decl("const struct type type_{c_name} = \{")
- v.add_decl("{self.type_layout.ids[mtype]},")
+
+ # type id (for cast target)
+ if is_cast_live then
+ v.add_decl("{layout.ids[mtype]},")
+ else
+ v.add_decl("-1, /*CAST DEAD*/")
+ end
+
+ # type name
v.add_decl("\"{mtype}\", /* class_name_string */")
- var layout = self.type_layout
- if layout isa PHLayout[MType, MType] then
- v.add_decl("{layout.masks[mtype]},")
+
+ # type color (for cast target)
+ if is_cast_live then
+ if layout isa PHLayout[MType, MType] then
+ v.add_decl("{layout.masks[mtype]},")
+ else
+ v.add_decl("{layout.pos[mtype]},")
+ end
else
- v.add_decl("{layout.pos[mtype]},")
+ v.add_decl("-1, /*CAST DEAD*/")
end
+
+ # is_nullable bit
if mtype isa MNullableType then
v.add_decl("1,")
else
v.add_decl("0,")
end
- if compile_type_resolution_table(mtype) then
- v.require_declaration("resolution_table_{c_name}")
- v.add_decl("&resolution_table_{c_name},")
+
+ # resolution table (for receiver)
+ if is_live then
+ var mclass_type = mtype
+ if mclass_type isa MNullableType then mclass_type = mclass_type.mtype
+ assert mclass_type isa MClassType
+ if resolution_tables[mclass_type].is_empty then
+ v.add_decl("NULL, /*NO RESOLUTIONS*/")
+ else
+ compile_type_resolution_table(mtype)
+ v.require_declaration("resolution_table_{c_name}")
+ v.add_decl("&resolution_table_{c_name},")
+ end
else
- v.add_decl("NULL,")
+ v.add_decl("NULL, /*DEAD*/")
end
- v.add_decl("{self.type_tables[mtype].length},")
- v.add_decl("\{")
- for stype in self.type_tables[mtype] do
- if stype == null then
- v.add_decl("-1, /* empty */")
- else
- v.add_decl("{self.type_layout.ids[stype]}, /* {stype} */")
+
+ # cast table (for receiver)
+ if is_live then
+ v.add_decl("{self.type_tables[mtype].length},")
+ v.add_decl("\{")
+ for stype in self.type_tables[mtype] do
+ if stype == null then
+ v.add_decl("-1, /* empty */")
+ else
+ v.add_decl("{layout.ids[stype]}, /* {stype} */")
+ end
end
+ v.add_decl("\},")
+ else
+ v.add_decl("0, \{\}, /*DEAD TYPE*/")
end
- v.add_decl("\},")
v.add_decl("\};")
end
- fun compile_type_resolution_table(mtype: MType): Bool do
+ fun compile_type_resolution_table(mtype: MType) do
var mclass_type: MClassType
if mtype isa MNullableType then
else
mclass_type = mtype.as(MClassType)
end
- if not self.resolution_tables.has_key(mclass_type) then return false
var layout = self.resolution_layout
end
v.add_decl("\}")
v.add_decl("\};")
- return true
end
# Globally compile the table of the class mclass
do
var mtype = mclass.intro.bound_mtype
var c_name = mclass.c_name
+ var c_instance_name = mclass.c_instance_name
var vft = self.method_tables[mclass]
var attrs = self.attr_tables[mclass]
var v = new_visitor
- var is_dead = runtime_type_analysis != null and not runtime_type_analysis.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray"
+ var rta = runtime_type_analysis
+ var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray"
v.add_decl("/* runtime class {c_name} */")
v.add_decl("NULL, /* empty */")
else
assert mpropdef isa MMethodDef
+ if rta != null and not rta.live_methoddefs.has(mpropdef) then
+ v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
+ continue
+ end
var rf = mpropdef.virtual_runtime_function
v.require_declaration(rf.c_name)
v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
end
if mtype.ctype != "val*" then
- #Build instance struct
- self.header.add_decl("struct instance_{c_name} \{")
- self.header.add_decl("const struct type *type;")
- self.header.add_decl("const struct class *class;")
- self.header.add_decl("{mtype.ctype} value;")
- self.header.add_decl("\};")
+ if mtype.mclass.name == "Pointer" or mtype.mclass.kind != extern_kind then
+ #Build instance struct
+ 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;")
+ self.header.add_decl("{mtype.ctype} value;")
+ self.header.add_decl("\};")
+ end
- if not self.runtime_type_analysis.live_types.has(mtype) then return
+ if not rta.live_types.has(mtype) then return
#Build BOX
- self.header.add_decl("val* BOX_{c_name}({mtype.ctype});")
+ self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype});")
v.add_decl("/* allocate {mtype} */")
v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
- v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
+ v.add("struct instance_{c_instance_name}*res = nit_alloc(sizeof(struct instance_{c_instance_name}));")
v.require_declaration("type_{c_name}")
v.add("res->type = &type_{c_name};")
v.require_declaration("class_{c_name}")
return
else if mclass.name == "NativeArray" then
#Build instance struct
- self.header.add_decl("struct instance_{c_name} \{")
+ 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_name}) + length*sizeof({mtype_elt.ctype}));")
+ 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
v.add("if({t} == NULL) \{")
v.add_abort("type null")
v.add("\}")
- v.add("if({t}->resolution_table == NULL) \{")
+ v.add("if({t}->table_size == 0) \{")
v.add("fprintf(stderr, \"Insantiation of a dead type: %s\\n\", {t}->name);")
v.add_abort("type dead")
v.add("\}")
if self.modelbuilder.toolcontext.opt_tables_metrics.value then
display_sizes
end
+ if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+ display_isset_checks
+ end
+ var tc = self.modelbuilder.toolcontext
+ tc.info("# implementation of method invocation",2)
+ var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
+ tc.info("total number of invocations: {nb_invok_total}",2)
+ tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
+ tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
+ tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
end
fun display_sizes
print "\t{total}\t{holes}"
end
+ protected var isset_checks_count = 0
+ protected var attr_read_count = 0
+
+ fun display_isset_checks do
+ print "# total number of compiled attribute reads"
+ print "\t{attr_read_count}"
+ print "# total number of compiled isset-checks"
+ print "\t{isset_checks_count}"
+ end
+
redef fun compile_nitni_structs
do
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
- self.mainmodule = nmodule.mmodule.as(not null)
+ var old_module = self.mainmodule
+ self.mainmodule = mmodule
super
+ self.mainmodule = old_module
end
end
else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
return value
else if value.mtype.ctype == "val*" then
- return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
+ return self.new_expr("((struct instance_{mtype.c_instance_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
else if mtype.ctype == "val*" then
var valtype = value.mtype.as(MClassType)
var res = self.new_var(mtype)
self.add("printf(\"Dead code executed!\\n\"); show_backtrace(1);")
return res
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!
if value.mtype.ctype == "val*" then
return "{value}->type"
else
+ compiler.undead_types.add(value.mtype)
self.require_declaration("type_{value.mtype.c_name}")
return "(&type_{value.mtype.c_name})"
end
end
+ redef fun compile_callsite(callsite, args)
+ do
+ var rta = compiler.runtime_type_analysis
+ var recv = args.first.mtype
+ 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)
+ 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
+ end
redef fun send(mmethod, arguments)
do
self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
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
- 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("{res} = 1; /* {arg.inspect} cannot be null */")
end
else
- self.add_abort("Reciever is 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
redef fun call(mmethoddef, recvtype, arguments)
do
+ assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
+
var res: nullable RuntimeVariable
var ret = mmethoddef.msignature.return_mtype
if mmethoddef.mproperty.is_new then
res = self.new_var(ret)
end
- if self.compiler.modelbuilder.mpropdef2npropdef.has_key(mmethoddef) and
- self.compiler.modelbuilder.mpropdef2npropdef[mmethoddef] isa AInternMethPropdef and
- not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value then
+ 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
var old_frame = self.frame
self.frame = frame
- self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) */")
+ self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
mmethoddef.compile_inside_to_c(self, arguments)
self.add("{frame.returnlabel.as(not null)}:(void)0;")
self.add("\}")
self.frame = old_frame
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.require_declaration(mmethoddef.c_name)
if res == null then
- self.add("{mmethoddef.c_name}({arguments.join(", ")});")
+ self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
return null
else
self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
var intromclassdef = a.intro.mclassdef
ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
+ if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+ self.compiler.attr_read_count += 1
+ self.add("count_attr_reads++;")
+ end
+
self.require_declaration(a.const_color)
if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
# Get the attribute or a box (ie. always a val*)
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
- self.add("if ({res} == NULL) \{")
+ 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("\}")
+
+ if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+ self.compiler.isset_checks_count += 1
+ self.add("count_isset_checks++;")
+ end
end
# Return the attribute or its unboxed version
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
- self.add("if ({res} == NULL) \{")
+ 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("\}")
+ if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+ self.compiler.isset_checks_count += 1
+ self.add("count_isset_checks++;")
+ end
end
return res
# The attribute is primitive, thus we store it in a box
# The trick is to create the box the first time then resuse the box
self.add("if ({attr} != NULL) \{")
- self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
+ self.add("((struct instance_{mtype.c_instance_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
self.add("\} else \{")
value = self.autobox(value, self.object_type.as_nullable)
self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
end
end
+ # Check that mtype is a live open type
+ fun hardening_live_open_type(mtype: MType)
+ do
+ if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
+ self.require_declaration(mtype.const_color)
+ var col = mtype.const_color
+ self.add("if({col} == -1) \{")
+ self.add("fprintf(stderr, \"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
+ self.add_abort("open type dead")
+ self.add("\}")
+ end
+
+ # Check that mtype it a pointer to a live cast type
+ fun hardening_cast_type(t: String)
+ do
+ if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
+ add("if({t} == NULL) \{")
+ add_abort("cast type null")
+ add("\}")
+ add("if({t}->id == -1 || {t}->color == -1) \{")
+ add("fprintf(stderr, \"Try to cast on a dead cast type: %s\\n\", {t}->name);")
+ add_abort("cast type dead")
+ add("\}")
+ end
+
redef fun init_instance(mtype)
do
self.require_declaration("NEW_{mtype.mclass.c_name}")
var compiler = self.compiler
if mtype isa MGenericType and mtype.need_anchor then
+ hardening_live_open_type(mtype)
link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
var recv = self.frame.arguments.first
var recv_type_info = self.type_info(recv)
self.add_decl("const struct type* {type_struct};")
# Either with resolution_table with a direct resolution
- link_unresolved_type(self.frame.mpropdef.mclassdef, ntype)
- self.require_declaration(ntype.const_color)
+ hardening_live_open_type(mtype)
+ link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
+ self.require_declaration(mtype.const_color)
if compiler.modelbuilder.toolcontext.opt_phmod_typing.value or compiler.modelbuilder.toolcontext.opt_phand_typing.value then
- self.add("{type_struct} = {recv_type_info}->resolution_table->types[HASH({recv_type_info}->resolution_table->mask, {ntype.const_color})];")
+ self.add("{type_struct} = {recv_type_info}->resolution_table->types[HASH({recv_type_info}->resolution_table->mask, {mtype.const_color})];")
else
- self.add("{type_struct} = {recv_type_info}->resolution_table->types[{ntype.const_color}];")
+ self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
end
if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
self.compiler.count_type_test_unresolved[tag] += 1
self.add("count_type_test_unresolved_{tag}++;")
end
+ hardening_cast_type(type_struct)
self.add("{cltype} = {type_struct}->color;")
self.add("{idtype} = {type_struct}->id;")
if maybe_null and accept_null == "0" then
else if ntype isa MClassType then
compiler.undead_types.add(mtype)
self.require_declaration("type_{mtype.c_name}")
+ hardening_cast_type("(&type_{mtype.c_name})")
self.add("{cltype} = type_{mtype.c_name}.color;")
self.add("{idtype} = type_{mtype.c_name}.id;")
if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
end
end
if primitive != null then
- test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
+ test.add("((struct instance_{primitive.c_instance_name}*){value1})->value == ((struct instance_{primitive.c_instance_name}*){value2})->value")
else if can_be_primitive(value1) and can_be_primitive(value2) then
test.add("{value1}->class == {value2}->class")
var s = new Array[String]
for t, v in self.compiler.box_kinds do
- s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
+ s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_instance_name}*){value1})->value == ((struct instance_{t.c_instance_name}*){value2})->value)"
end
test.add("({s.join(" || ")})")
else
assert mtype isa MGenericType
var compiler = self.compiler
if mtype.need_anchor then
+ hardening_live_open_type(mtype)
link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
var recv = self.frame.arguments.first
var recv_type_info = self.type_info(recv)
do
var elttype = arguments.first.mtype
var nclass = self.get_class("NativeArray")
- var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
+ var recv = "((struct instance_{nclass.c_instance_name}*){arguments[0]})->values"
if pname == "[]" then
self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
return
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_name}*){arguments[1]})->values"
+ var recv1 = "((struct instance_{nclass.c_instance_name}*){arguments[1]})->values"
self.add("memcpy({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
return
end
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
redef class MType
fun const_color: String do return "COLOR_{c_name}"
+
+ # C name of the instance type to use
+ fun c_instance_name: String do return c_name
+end
+
+redef class MClassType
+ redef fun c_instance_name do return mclass.c_instance_name
+end
+
+redef class MClass
+ # Extern classes use the C instance of kernel::Pointer
+ fun c_instance_name: String
+ do
+ if kind == extern_kind then
+ return "kernel__Pointer"
+ else return c_name
+ end
end
redef class MProperty