sep_compiler: add RuntimeFunction::c_fun_ptr
[nit.git] / src / compiler / separate_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Separate compilation of a Nit program
16 module separate_compiler
17
18 import abstract_compiler
19 import coloring
20 import rapid_type_analysis
21
22 # Add separate compiler specific options
23 redef class ToolContext
24 # --separate
25 var opt_separate = new OptionBool("Use separate compilation", "--separate")
26 # --no-inline-intern
27 var opt_no_inline_intern = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
28 # --no-union-attribute
29 var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
30 # --no-shortcut-equate
31 var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
32 # --colors-are-symbols
33 var opt_colors_are_symbols = new OptionBool("Store colors as symbols (faster)", "--colors-are-symbols")
34
35 # --inline-coloring-numbers
36 var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
37 # --inline-some-methods
38 var opt_inline_some_methods = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
39 # --direct-call-monomorph
40 var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
41 # --skip-dead-methods
42 var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
43 # --semi-global
44 var opt_semi_global = new OptionBool("Enable all semi-global optimizations", "--semi-global")
45 # --no-colo-dead-methods
46 var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods")
47 # --tables-metrics
48 var opt_tables_metrics = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
49
50 redef init
51 do
52 super
53 self.option_context.add_option(self.opt_separate)
54 self.option_context.add_option(self.opt_no_inline_intern)
55 self.option_context.add_option(self.opt_no_union_attribute)
56 self.option_context.add_option(self.opt_no_shortcut_equate, opt_colors_are_symbols)
57 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)
58 self.option_context.add_option(self.opt_colo_dead_methods)
59 self.option_context.add_option(self.opt_tables_metrics)
60 end
61
62 redef fun process_options(args)
63 do
64 super
65
66 var tc = self
67 if tc.opt_semi_global.value then
68 tc.opt_inline_coloring_numbers.value = true
69 tc.opt_inline_some_methods.value = true
70 tc.opt_direct_call_monomorph.value = true
71 tc.opt_skip_dead_methods.value = true
72 end
73 end
74
75 var separate_compiler_phase = new SeparateCompilerPhase(self, null)
76 end
77
78 class SeparateCompilerPhase
79 super Phase
80 redef fun process_mainmodule(mainmodule, given_mmodules) do
81 if not toolcontext.opt_separate.value then return
82
83 var modelbuilder = toolcontext.modelbuilder
84 var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
85 modelbuilder.run_separate_compiler(mainmodule, analysis)
86 end
87 end
88
89 redef class ModelBuilder
90 fun run_separate_compiler(mainmodule: MModule, runtime_type_analysis: nullable RapidTypeAnalysis)
91 do
92 var time0 = get_time
93 self.toolcontext.info("*** GENERATING C ***", 1)
94
95 var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis)
96 compiler.do_compilation
97 compiler.display_stats
98
99 var time1 = get_time
100 self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
101 write_and_make(compiler)
102 end
103
104 # Count number of invocations by VFT
105 private var nb_invok_by_tables = 0
106 # Count number of invocations by direct call
107 private var nb_invok_by_direct = 0
108 # Count number of invocations by inlining
109 private var nb_invok_by_inline = 0
110 end
111
112 # Singleton that store the knowledge about the separate compilation process
113 class SeparateCompiler
114 super AbstractCompiler
115
116 redef type VISITOR: SeparateCompilerVisitor
117
118 # The result of the RTA (used to know live types and methods)
119 var runtime_type_analysis: nullable RapidTypeAnalysis
120
121 private var undead_types: Set[MType] = new HashSet[MType]
122 private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
123
124 private var type_ids: Map[MType, Int] is noinit
125 private var type_colors: Map[MType, Int] is noinit
126 private var opentype_colors: Map[MType, Int] is noinit
127 protected var method_colors: Map[PropertyLayoutElement, Int] is noinit
128 protected var attr_colors: Map[MAttribute, Int] is noinit
129
130 init do
131 var file = new_file("nit.common")
132 self.header = new CodeWriter(file)
133 self.compile_box_kinds
134 end
135
136 redef fun do_compilation
137 do
138 var compiler = self
139 compiler.compile_header
140
141 var c_name = mainmodule.c_name
142
143 # compile class structures
144 modelbuilder.toolcontext.info("Property coloring", 2)
145 compiler.new_file("{c_name}.classes")
146 compiler.do_property_coloring
147 for m in mainmodule.in_importation.greaters do
148 for mclass in m.intro_mclasses do
149 #if mclass.kind == abstract_kind or mclass.kind == interface_kind then continue
150 compiler.compile_class_to_c(mclass)
151 end
152 end
153
154 # The main function of the C
155 compiler.new_file("{c_name}.main")
156 compiler.compile_nitni_global_ref_functions
157 compiler.compile_main_function
158 compiler.compile_finalizer_function
159
160 # compile methods
161 for m in mainmodule.in_importation.greaters do
162 modelbuilder.toolcontext.info("Generate C for module {m.full_name}", 2)
163 compiler.new_file("{m.c_name}.sep")
164 compiler.compile_module_to_c(m)
165 end
166
167 # compile live & cast type structures
168 modelbuilder.toolcontext.info("Type coloring", 2)
169 compiler.new_file("{c_name}.types")
170 compiler.compile_types
171 end
172
173 # Color and compile type structures and cast information
174 fun compile_types
175 do
176 var compiler = self
177
178 var mtypes = compiler.do_type_coloring
179 for t in mtypes do
180 compiler.compile_type_to_c(t)
181 end
182 # compile remaining types structures (useless but needed for the symbol resolution at link-time)
183 for t in compiler.undead_types do
184 if mtypes.has(t) then continue
185 compiler.compile_type_to_c(t)
186 end
187
188 end
189
190 redef fun compile_header_structs do
191 self.header.add_decl("typedef void(*nitmethod_t)(void); /* general C type representing a Nit method. */")
192 self.compile_header_attribute_structs
193 self.header.add_decl("struct class \{ int box_kind; nitmethod_t vft[]; \}; /* general C type representing a Nit class. */")
194
195 # With resolution_table_table, all live type resolution are stored in a big table: resolution_table
196 self.header.add_decl("struct type \{ int id; const char *name; int color; short int is_nullable; const struct types *resolution_table; int table_size; int type_table[]; \}; /* general C type representing a Nit type. */")
197 self.header.add_decl("struct instance \{ const struct type *type; const struct class *class; nitattribute_t attrs[]; \}; /* general C type representing a Nit instance. */")
198 self.header.add_decl("struct types \{ int dummy; const struct type *types[]; \}; /* a list types (used for vts, fts and unresolved lists). */")
199 self.header.add_decl("typedef struct instance val; /* general C type representing a Nit instance. */")
200 end
201
202 fun compile_header_attribute_structs
203 do
204 if modelbuilder.toolcontext.opt_no_union_attribute.value then
205 self.header.add_decl("typedef void* nitattribute_t; /* general C type representing a Nit attribute. */")
206 else
207 self.header.add_decl("typedef union \{")
208 self.header.add_decl("void* val;")
209 for c, v in self.box_kinds do
210 var t = c.mclass_type
211
212 # `Pointer` reuse the `val` field
213 if t.mclass.name == "Pointer" then continue
214
215 self.header.add_decl("{t.ctype_extern} {t.ctypename};")
216 end
217 self.header.add_decl("\} nitattribute_t; /* general C type representing a Nit attribute. */")
218 end
219 end
220
221 fun compile_box_kinds
222 do
223 # Collect all bas box class
224 # FIXME: this is not completely fine with a separate compilation scheme
225 for classname in ["Int", "Bool", "Char", "Float", "NativeString", "Pointer"] do
226 var classes = self.mainmodule.model.get_mclasses_by_name(classname)
227 if classes == null then continue
228 assert classes.length == 1 else print classes.join(", ")
229 self.box_kinds[classes.first] = self.box_kinds.length + 1
230 end
231 end
232
233 var box_kinds = new HashMap[MClass, Int]
234
235 fun box_kind_of(mclass: MClass): Int
236 do
237 #var pointer_type = self.mainmodule.pointer_type
238 #if mclass.mclass_type.ctype == "val*" or mclass.mclass_type.is_subtype(self.mainmodule, mclass.mclass_type pointer_type) then
239 if mclass.mclass_type.ctype_extern == "val*" then
240 return 0
241 else if mclass.kind == extern_kind and mclass.name != "NativeString" then
242 return self.box_kinds[self.mainmodule.get_primitive_class("Pointer")]
243 else
244 return self.box_kinds[mclass]
245 end
246
247 end
248
249 fun compile_color_consts(colors: Map[Object, Int]) do
250 var v = new_visitor
251 for m, c in colors do
252 compile_color_const(v, m, c)
253 end
254 end
255
256 fun compile_color_const(v: SeparateCompilerVisitor, m: Object, color: Int) do
257 if color_consts_done.has(m) then return
258 if m isa MEntity then
259 if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
260 self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
261 else if not modelbuilder.toolcontext.opt_colors_are_symbols.value or not v.compiler.target_platform.supports_linker_script then
262 self.provide_declaration(m.const_color, "extern const int {m.const_color};")
263 v.add("const int {m.const_color} = {color};")
264 else
265 # The color 'C' is the ``address'' of a false static variable 'XC'
266 self.provide_declaration(m.const_color, "#define {m.const_color} ((long)&X{m.const_color})\nextern const void X{m.const_color};")
267 if color == -1 then color = 0 # Symbols cannot be negative, so just use 0 for dead things
268 # Teach the linker that the address of 'XC' is `color`.
269 linker_script.add("X{m.const_color} = {color};")
270 end
271 else
272 abort
273 end
274 color_consts_done.add(m)
275 end
276
277 private var color_consts_done = new HashSet[Object]
278
279 # colorize classe properties
280 fun do_property_coloring do
281
282 var rta = runtime_type_analysis
283
284 # Layouts
285 var poset = mainmodule.flatten_mclass_hierarchy
286 var mclasses = new HashSet[MClass].from(poset)
287 var colorer = new POSetColorer[MClass]
288 colorer.colorize(poset)
289
290 # The dead methods, still need to provide a dead color symbol
291 var dead_methods = new Array[MMethod]
292
293 # lookup properties to build layout with
294 var mmethods = new HashMap[MClass, Set[PropertyLayoutElement]]
295 var mattributes = new HashMap[MClass, Set[MAttribute]]
296 for mclass in mclasses do
297 mmethods[mclass] = new HashSet[PropertyLayoutElement]
298 mattributes[mclass] = new HashSet[MAttribute]
299 for mprop in self.mainmodule.properties(mclass) do
300 if mprop isa MMethod then
301 if not modelbuilder.toolcontext.opt_colo_dead_methods.value and rta != null and not rta.live_methods.has(mprop) then
302 dead_methods.add(mprop)
303 continue
304 end
305 mmethods[mclass].add(mprop)
306 else if mprop isa MAttribute then
307 mattributes[mclass].add(mprop)
308 end
309 end
310 end
311
312 # Collect all super calls (dead or not)
313 var all_super_calls = new HashSet[MMethodDef]
314 for mmodule in self.mainmodule.in_importation.greaters do
315 for mclassdef in mmodule.mclassdefs do
316 for mpropdef in mclassdef.mpropdefs do
317 if not mpropdef isa MMethodDef then continue
318 if mpropdef.has_supercall then
319 all_super_calls.add(mpropdef)
320 end
321 end
322 end
323 end
324
325 # lookup super calls and add it to the list of mmethods to build layout with
326 var super_calls
327 if rta != null then
328 super_calls = rta.live_super_sends
329 else
330 super_calls = all_super_calls
331 end
332
333 for mmethoddef in super_calls do
334 var mclass = mmethoddef.mclassdef.mclass
335 mmethods[mclass].add(mmethoddef)
336 for descendant in mclass.in_hierarchy(self.mainmodule).smallers do
337 mmethods[descendant].add(mmethoddef)
338 end
339 end
340
341 # methods coloration
342 var meth_colorer = new POSetBucketsColorer[MClass, PropertyLayoutElement](poset, colorer.conflicts)
343 method_colors = meth_colorer.colorize(mmethods)
344 method_tables = build_method_tables(mclasses, super_calls)
345 compile_color_consts(method_colors)
346
347 # attribute null color to dead methods and supercalls
348 for mproperty in dead_methods do
349 compile_color_const(new_visitor, mproperty, -1)
350 end
351 for mpropdef in all_super_calls do
352 if super_calls.has(mpropdef) then continue
353 compile_color_const(new_visitor, mpropdef, -1)
354 end
355
356 # attributes coloration
357 var attr_colorer = new POSetBucketsColorer[MClass, MAttribute](poset, colorer.conflicts)
358 attr_colors = attr_colorer.colorize(mattributes)
359 attr_tables = build_attr_tables(mclasses)
360 compile_color_consts(attr_colors)
361 end
362
363 fun build_method_tables(mclasses: Set[MClass], super_calls: Set[MMethodDef]): Map[MClass, Array[nullable MPropDef]] do
364 var tables = new HashMap[MClass, Array[nullable MPropDef]]
365 for mclass in mclasses do
366 var table = new Array[nullable MPropDef]
367 tables[mclass] = table
368
369 var mproperties = self.mainmodule.properties(mclass)
370 var mtype = mclass.intro.bound_mtype
371
372 for mproperty in mproperties do
373 if not mproperty isa MMethod then continue
374 if not method_colors.has_key(mproperty) then continue
375 var color = method_colors[mproperty]
376 if table.length <= color then
377 for i in [table.length .. color[ do
378 table[i] = null
379 end
380 end
381 table[color] = mproperty.lookup_first_definition(mainmodule, mtype)
382 end
383
384 for supercall in super_calls do
385 if not mtype.collect_mclassdefs(mainmodule).has(supercall.mclassdef) then continue
386
387 var color = method_colors[supercall]
388 if table.length <= color then
389 for i in [table.length .. color[ do
390 table[i] = null
391 end
392 end
393 var mmethoddef = supercall.lookup_next_definition(mainmodule, mtype)
394 table[color] = mmethoddef
395 end
396
397 end
398 return tables
399 end
400
401 fun build_attr_tables(mclasses: Set[MClass]): Map[MClass, Array[nullable MPropDef]] do
402 var tables = new HashMap[MClass, Array[nullable MPropDef]]
403 for mclass in mclasses do
404 var table = new Array[nullable MPropDef]
405 tables[mclass] = table
406
407 var mproperties = self.mainmodule.properties(mclass)
408 var mtype = mclass.intro.bound_mtype
409
410 for mproperty in mproperties do
411 if not mproperty isa MAttribute then continue
412 if not attr_colors.has_key(mproperty) then continue
413 var color = attr_colors[mproperty]
414 if table.length <= color then
415 for i in [table.length .. color[ do
416 table[i] = null
417 end
418 end
419 table[color] = mproperty.lookup_first_definition(mainmodule, mtype)
420 end
421 end
422 return tables
423 end
424
425 # colorize live types of the program
426 private fun do_type_coloring: POSet[MType] do
427 # Collect types to colorize
428 var live_types = runtime_type_analysis.live_types
429 var live_cast_types = runtime_type_analysis.live_cast_types
430 var mtypes = new HashSet[MType]
431 mtypes.add_all(live_types)
432 for c in self.box_kinds.keys do
433 mtypes.add(c.mclass_type)
434 end
435
436 # Compute colors
437 var poset = poset_from_mtypes(mtypes, live_cast_types)
438 var colorer = new POSetColorer[MType]
439 colorer.colorize(poset)
440 type_ids = colorer.ids
441 type_colors = colorer.colors
442 type_tables = build_type_tables(poset)
443
444 # VT and FT are stored with other unresolved types in the big resolution_tables
445 self.compile_resolution_tables(mtypes)
446
447 return poset
448 end
449
450 private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
451 var poset = new POSet[MType]
452 for e in mtypes do
453 poset.add_node(e)
454 for o in cast_types do
455 if e == o then continue
456 poset.add_node(o)
457 if e.is_subtype(mainmodule, null, o) then
458 poset.add_edge(e, o)
459 end
460 end
461 end
462 return poset
463 end
464
465 # Build type tables
466 fun build_type_tables(mtypes: POSet[MType]): Map[MType, Array[nullable MType]] do
467 var tables = new HashMap[MType, Array[nullable MType]]
468 for mtype in mtypes do
469 var table = new Array[nullable MType]
470 for sup in mtypes[mtype].greaters do
471 var color = type_colors[sup]
472 if table.length <= color then
473 for i in [table.length .. color[ do
474 table[i] = null
475 end
476 end
477 table[color] = sup
478 end
479 tables[mtype] = table
480 end
481 return tables
482 end
483
484 protected fun compile_resolution_tables(mtypes: Set[MType]) do
485 # resolution_tables is used to perform a type resolution at runtime in O(1)
486
487 # During the visit of the body of classes, live_unresolved_types are collected
488 # and associated to
489 # Collect all live_unresolved_types (visited in the body of classes)
490
491 # Determinate fo each livetype what are its possible requested anchored types
492 var mtype2unresolved = new HashMap[MClassType, Set[MType]]
493 for mtype in self.runtime_type_analysis.live_types do
494 var set = new HashSet[MType]
495 for cd in mtype.collect_mclassdefs(self.mainmodule) do
496 if self.live_unresolved_types.has_key(cd) then
497 set.add_all(self.live_unresolved_types[cd])
498 end
499 end
500 mtype2unresolved[mtype] = set
501 end
502
503 # Compute the table layout with the prefered method
504 var colorer = new BucketsColorer[MType, MType]
505 opentype_colors = colorer.colorize(mtype2unresolved)
506 resolution_tables = self.build_resolution_tables(mtype2unresolved)
507
508 # Compile a C constant for each collected unresolved type.
509 # Either to a color, or to -1 if the unresolved type is dead (no live receiver can require it)
510 var all_unresolved = new HashSet[MType]
511 for t in self.live_unresolved_types.values do
512 all_unresolved.add_all(t)
513 end
514 var all_unresolved_types_colors = new HashMap[MType, Int]
515 for t in all_unresolved do
516 if opentype_colors.has_key(t) then
517 all_unresolved_types_colors[t] = opentype_colors[t]
518 else
519 all_unresolved_types_colors[t] = -1
520 end
521 end
522 self.compile_color_consts(all_unresolved_types_colors)
523
524 #print "tables"
525 #for k, v in unresolved_types_tables.as(not null) do
526 # print "{k}: {v.join(", ")}"
527 #end
528 #print ""
529 end
530
531 fun build_resolution_tables(elements: Map[MClassType, Set[MType]]): Map[MClassType, Array[nullable MType]] do
532 var tables = new HashMap[MClassType, Array[nullable MType]]
533 for mclasstype, mtypes in elements do
534 var table = new Array[nullable MType]
535 for mtype in mtypes do
536 var color = opentype_colors[mtype]
537 if table.length <= color then
538 for i in [table.length .. color[ do
539 table[i] = null
540 end
541 end
542 table[color] = mtype
543 end
544 tables[mclasstype] = table
545 end
546 return tables
547 end
548
549 # Separately compile all the method definitions of the module
550 fun compile_module_to_c(mmodule: MModule)
551 do
552 var old_module = self.mainmodule
553 self.mainmodule = mmodule
554 for cd in mmodule.mclassdefs do
555 for pd in cd.mpropdefs do
556 if not pd isa MMethodDef then continue
557 var rta = runtime_type_analysis
558 if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
559 #print "compile {pd} @ {cd} @ {mmodule}"
560 var r = pd.separate_runtime_function
561 r.compile_to_c(self)
562 var r2 = pd.virtual_runtime_function
563 if r2 != r then r2.compile_to_c(self)
564 end
565 end
566 self.mainmodule = old_module
567 end
568
569 # Globaly compile the type structure of a live type
570 fun compile_type_to_c(mtype: MType)
571 do
572 assert not mtype.need_anchor
573 var is_live = mtype isa MClassType and runtime_type_analysis.live_types.has(mtype)
574 var is_cast_live = runtime_type_analysis.live_cast_types.has(mtype)
575 var c_name = mtype.c_name
576 var v = new SeparateCompilerVisitor(self)
577 v.add_decl("/* runtime type {mtype} */")
578
579 # extern const struct type_X
580 self.provide_declaration("type_{c_name}", "extern const struct type type_{c_name};")
581
582 # const struct type_X
583 v.add_decl("const struct type type_{c_name} = \{")
584
585 # type id (for cast target)
586 if is_cast_live then
587 v.add_decl("{type_ids[mtype]},")
588 else
589 v.add_decl("-1, /*CAST DEAD*/")
590 end
591
592 # type name
593 v.add_decl("\"{mtype}\", /* class_name_string */")
594
595 # type color (for cast target)
596 if is_cast_live then
597 v.add_decl("{type_colors[mtype]},")
598 else
599 v.add_decl("-1, /*CAST DEAD*/")
600 end
601
602 # is_nullable bit
603 if mtype isa MNullableType then
604 v.add_decl("1,")
605 else
606 v.add_decl("0,")
607 end
608
609 # resolution table (for receiver)
610 if is_live then
611 var mclass_type = mtype.as_notnullable
612 assert mclass_type isa MClassType
613 if resolution_tables[mclass_type].is_empty then
614 v.add_decl("NULL, /*NO RESOLUTIONS*/")
615 else
616 compile_type_resolution_table(mtype)
617 v.require_declaration("resolution_table_{c_name}")
618 v.add_decl("&resolution_table_{c_name},")
619 end
620 else
621 v.add_decl("NULL, /*DEAD*/")
622 end
623
624 # cast table (for receiver)
625 if is_live then
626 v.add_decl("{self.type_tables[mtype].length},")
627 v.add_decl("\{")
628 for stype in self.type_tables[mtype] do
629 if stype == null then
630 v.add_decl("-1, /* empty */")
631 else
632 v.add_decl("{type_ids[stype]}, /* {stype} */")
633 end
634 end
635 v.add_decl("\},")
636 else
637 v.add_decl("0, \{\}, /*DEAD TYPE*/")
638 end
639 v.add_decl("\};")
640 end
641
642 fun compile_type_resolution_table(mtype: MType) do
643
644 var mclass_type = mtype.as_notnullable.as(MClassType)
645
646 # extern const struct resolution_table_X resolution_table_X
647 self.provide_declaration("resolution_table_{mtype.c_name}", "extern const struct types resolution_table_{mtype.c_name};")
648
649 # const struct fts_table_X fts_table_X
650 var v = new_visitor
651 v.add_decl("const struct types resolution_table_{mtype.c_name} = \{")
652 v.add_decl("0, /* dummy */")
653 v.add_decl("\{")
654 for t in self.resolution_tables[mclass_type] do
655 if t == null then
656 v.add_decl("NULL, /* empty */")
657 else
658 # The table stores the result of the type resolution
659 # Therefore, for a receiver `mclass_type`, and a unresolved type `t`
660 # the value stored is tv.
661 var tv = t.resolve_for(mclass_type, mclass_type, self.mainmodule, true)
662 # FIXME: What typeids means here? How can a tv not be live?
663 if type_ids.has_key(tv) then
664 v.require_declaration("type_{tv.c_name}")
665 v.add_decl("&type_{tv.c_name}, /* {t}: {tv} */")
666 else
667 v.add_decl("NULL, /* empty ({t}: {tv} not a live type) */")
668 end
669 end
670 end
671 v.add_decl("\}")
672 v.add_decl("\};")
673 end
674
675 # Globally compile the table of the class mclass
676 # In a link-time optimisation compiler, tables are globally computed
677 # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
678 fun compile_class_to_c(mclass: MClass)
679 do
680 var mtype = mclass.intro.bound_mtype
681 var c_name = mclass.c_name
682
683 var vft = self.method_tables[mclass]
684 var attrs = self.attr_tables[mclass]
685 var v = new_visitor
686
687 var rta = runtime_type_analysis
688 var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" and mclass.name != "Pointer"
689
690 v.add_decl("/* runtime class {c_name} */")
691
692 # Build class vft
693 if not is_dead then
694 self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
695 v.add_decl("const struct class class_{c_name} = \{")
696 v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
697 v.add_decl("\{")
698 for i in [0 .. vft.length[ do
699 var mpropdef = vft[i]
700 if mpropdef == null then
701 v.add_decl("NULL, /* empty */")
702 else
703 assert mpropdef isa MMethodDef
704 if rta != null and not rta.live_methoddefs.has(mpropdef) then
705 v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
706 continue
707 end
708 var rf = mpropdef.virtual_runtime_function
709 v.require_declaration(rf.c_name)
710 v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
711 end
712 end
713 v.add_decl("\}")
714 v.add_decl("\};")
715 end
716
717 if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
718 # Is a primitive type or the Pointer class, not any other extern class
719
720 #Build instance struct
721 self.header.add_decl("struct instance_{c_name} \{")
722 self.header.add_decl("const struct type *type;")
723 self.header.add_decl("const struct class *class;")
724 self.header.add_decl("{mtype.ctype_extern} value;")
725 self.header.add_decl("\};")
726
727 if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
728
729 #Build BOX
730 self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
731 v.add_decl("/* allocate {mtype} */")
732 v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
733 v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
734 v.compiler.undead_types.add(mtype)
735 v.require_declaration("type_{c_name}")
736 v.add("res->type = &type_{c_name};")
737 v.require_declaration("class_{c_name}")
738 v.add("res->class = &class_{c_name};")
739 v.add("res->value = value;")
740 v.add("return (val*)res;")
741 v.add("\}")
742
743 if mtype.mclass.name != "Pointer" then return
744
745 v = new_visitor
746 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
747 v.add_decl("/* allocate {mtype} */")
748 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
749 if is_dead then
750 v.add_abort("{mclass} is DEAD")
751 else
752 var res = v.new_named_var(mtype, "self")
753 res.is_exact = true
754 v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
755 v.add("{res}->type = type;")
756 hardening_live_type(v, "type")
757 v.require_declaration("class_{c_name}")
758 v.add("{res}->class = &class_{c_name};")
759 v.add("((struct instance_{mtype.c_name}*){res})->value = NULL;")
760 v.add("return {res};")
761 end
762 v.add("\}")
763 return
764 else if mclass.name == "NativeArray" then
765 #Build instance struct
766 self.header.add_decl("struct instance_{c_name} \{")
767 self.header.add_decl("const struct type *type;")
768 self.header.add_decl("const struct class *class;")
769 # NativeArrays are just a instance header followed by a length and an array of values
770 self.header.add_decl("int length;")
771 self.header.add_decl("val* values[0];")
772 self.header.add_decl("\};")
773
774 #Build NEW
775 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
776 v.add_decl("/* allocate {mtype} */")
777 v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
778 var res = v.get_name("self")
779 v.add_decl("struct instance_{c_name} *{res};")
780 var mtype_elt = mtype.arguments.first
781 v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
782 v.add("{res}->type = type;")
783 hardening_live_type(v, "type")
784 v.require_declaration("class_{c_name}")
785 v.add("{res}->class = &class_{c_name};")
786 v.add("{res}->length = length;")
787 v.add("return (val*){res};")
788 v.add("\}")
789 return
790 else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
791 # Is an extern class (other than Pointer and NativeString)
792 # Pointer is caught in a previous `if`, and NativeString is internal
793
794 var pointer_type = mainmodule.pointer_type
795
796 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
797 v.add_decl("/* allocate {mtype} */")
798 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
799 if is_dead then
800 v.add_abort("{mclass} is DEAD")
801 else
802 var res = v.new_named_var(mtype, "self")
803 res.is_exact = true
804 v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
805 v.add("{res}->type = type;")
806 hardening_live_type(v, "type")
807 v.require_declaration("class_{c_name}")
808 v.add("{res}->class = &class_{c_name};")
809 v.add("((struct instance_{pointer_type.c_name}*){res})->value = NULL;")
810 v.add("return {res};")
811 end
812 v.add("\}")
813 return
814 end
815
816 #Build NEW
817 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
818 v.add_decl("/* allocate {mtype} */")
819 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
820 if is_dead then
821 v.add_abort("{mclass} is DEAD")
822 else
823 var res = v.new_named_var(mtype, "self")
824 res.is_exact = true
825 v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
826 v.add("{res}->type = type;")
827 hardening_live_type(v, "type")
828 v.require_declaration("class_{c_name}")
829 v.add("{res}->class = &class_{c_name};")
830 self.generate_init_attr(v, res, mtype)
831 v.set_finalizer res
832 v.add("return {res};")
833 end
834 v.add("\}")
835 end
836
837 # Add a dynamic test to ensure that the type referenced by `t` is a live type
838 fun hardening_live_type(v: VISITOR, t: String)
839 do
840 if not v.compiler.modelbuilder.toolcontext.opt_hardening.value then return
841 v.add("if({t} == NULL) \{")
842 v.add_abort("type null")
843 v.add("\}")
844 v.add("if({t}->table_size == 0) \{")
845 v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
846 v.add_abort("type dead")
847 v.add("\}")
848 end
849
850 redef fun new_visitor do return new SeparateCompilerVisitor(self)
851
852 # Stats
853
854 private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
855 private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
856 protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
857 protected var attr_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
858
859 redef fun display_stats
860 do
861 super
862 if self.modelbuilder.toolcontext.opt_tables_metrics.value then
863 display_sizes
864 end
865 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
866 display_isset_checks
867 end
868 var tc = self.modelbuilder.toolcontext
869 tc.info("# implementation of method invocation",2)
870 var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
871 tc.info("total number of invocations: {nb_invok_total}",2)
872 tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
873 tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
874 tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
875 end
876
877 fun display_sizes
878 do
879 print "# size of subtyping tables"
880 print "\ttotal \tholes"
881 var total = 0
882 var holes = 0
883 for t, table in type_tables do
884 total += table.length
885 for e in table do if e == null then holes += 1
886 end
887 print "\t{total}\t{holes}"
888
889 print "# size of resolution tables"
890 print "\ttotal \tholes"
891 total = 0
892 holes = 0
893 for t, table in resolution_tables do
894 total += table.length
895 for e in table do if e == null then holes += 1
896 end
897 print "\t{total}\t{holes}"
898
899 print "# size of methods tables"
900 print "\ttotal \tholes"
901 total = 0
902 holes = 0
903 for t, table in method_tables do
904 total += table.length
905 for e in table do if e == null then holes += 1
906 end
907 print "\t{total}\t{holes}"
908
909 print "# size of attributes tables"
910 print "\ttotal \tholes"
911 total = 0
912 holes = 0
913 for t, table in attr_tables do
914 total += table.length
915 for e in table do if e == null then holes += 1
916 end
917 print "\t{total}\t{holes}"
918 end
919
920 protected var isset_checks_count = 0
921 protected var attr_read_count = 0
922
923 fun display_isset_checks do
924 print "# total number of compiled attribute reads"
925 print "\t{attr_read_count}"
926 print "# total number of compiled isset-checks"
927 print "\t{isset_checks_count}"
928 end
929
930 redef fun compile_nitni_structs
931 do
932 self.header.add_decl """
933 struct nitni_instance \{
934 struct nitni_instance *next,
935 *prev; /* adjacent global references in global list */
936 int count; /* number of time this global reference has been marked */
937 struct instance *value;
938 \};
939 """
940 super
941 end
942
943 redef fun finalize_ffi_for_module(mmodule)
944 do
945 var old_module = self.mainmodule
946 self.mainmodule = mmodule
947 super
948 self.mainmodule = old_module
949 end
950 end
951
952 # A visitor on the AST of property definition that generate the C code of a separate compilation process.
953 class SeparateCompilerVisitor
954 super AbstractCompilerVisitor
955
956 redef type COMPILER: SeparateCompiler
957
958 redef fun adapt_signature(m, args)
959 do
960 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
961 var recv = args.first
962 if recv.mtype.ctype != m.mclassdef.mclass.mclass_type.ctype then
963 args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
964 end
965 for i in [0..msignature.arity[ do
966 var t = msignature.mparameters[i].mtype
967 if i == msignature.vararg_rank then
968 t = args[i+1].mtype
969 end
970 args[i+1] = self.autobox(args[i+1], t)
971 end
972 end
973
974 redef fun unbox_signature_extern(m, args)
975 do
976 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
977 if not m.mproperty.is_init and m.is_extern then
978 args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
979 end
980 for i in [0..msignature.arity[ do
981 var t = msignature.mparameters[i].mtype
982 if i == msignature.vararg_rank then
983 t = args[i+1].mtype
984 end
985 if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
986 end
987 end
988
989 redef fun autobox(value, mtype)
990 do
991 if value.mtype == mtype then
992 return value
993 else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
994 return value
995 else if value.mtype.ctype == "val*" then
996 return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
997 else if mtype.ctype == "val*" then
998 var valtype = value.mtype.as(MClassType)
999 if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
1000 valtype = compiler.mainmodule.pointer_type
1001 end
1002 var res = self.new_var(mtype)
1003 if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
1004 self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
1005 self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
1006 return res
1007 end
1008 self.require_declaration("BOX_{valtype.c_name}")
1009 self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
1010 return res
1011 else if (value.mtype.ctype == "void*" and mtype.ctype == "void*") or
1012 (value.mtype.ctype == "char*" and mtype.ctype == "void*") or
1013 (value.mtype.ctype == "void*" and mtype.ctype == "char*") then
1014 return value
1015 else
1016 # Bad things will appen!
1017 var res = self.new_var(mtype)
1018 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1019 self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);")
1020 return res
1021 end
1022 end
1023
1024 redef fun unbox_extern(value, mtype)
1025 do
1026 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1027 mtype.mclass.name != "NativeString" then
1028 var pointer_type = compiler.mainmodule.pointer_type
1029 var res = self.new_var_extern(mtype)
1030 self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
1031 return res
1032 else
1033 return value
1034 end
1035 end
1036
1037 redef fun box_extern(value, mtype)
1038 do
1039 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1040 mtype.mclass.name != "NativeString" then
1041 var valtype = compiler.mainmodule.pointer_type
1042 var res = self.new_var(mtype)
1043 if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
1044 self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
1045 self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
1046 return res
1047 end
1048 self.require_declaration("BOX_{valtype.c_name}")
1049 self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
1050 self.require_declaration("type_{mtype.c_name}")
1051 self.add("{res}->type = &type_{mtype.c_name};")
1052 self.require_declaration("class_{mtype.c_name}")
1053 self.add("{res}->class = &class_{mtype.c_name};")
1054 return res
1055 else
1056 return value
1057 end
1058 end
1059
1060 # Return a C expression returning the runtime type structure of the value
1061 # The point of the method is to works also with primitives types.
1062 fun type_info(value: RuntimeVariable): String
1063 do
1064 if value.mtype.ctype == "val*" then
1065 return "{value}->type"
1066 else
1067 compiler.undead_types.add(value.mtype)
1068 self.require_declaration("type_{value.mtype.c_name}")
1069 return "(&type_{value.mtype.c_name})"
1070 end
1071 end
1072
1073 redef fun compile_callsite(callsite, args)
1074 do
1075 var rta = compiler.runtime_type_analysis
1076 var mmethod = callsite.mproperty
1077 # TODO: Inlining of new-style constructors with initializers
1078 if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
1079 var tgs = rta.live_targets(callsite)
1080 if tgs.length == 1 then
1081 # DIRECT CALL
1082 var res0 = before_send(mmethod, args)
1083 var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
1084 if res0 != null then
1085 assert res != null
1086 self.assign(res0, res)
1087 res = res0
1088 end
1089 add("\}") # close the before_send
1090 return res
1091 end
1092 end
1093 return super
1094 end
1095 redef fun send(mmethod, arguments)
1096 do
1097 if arguments.first.mcasttype.ctype != "val*" then
1098 # In order to shortcut the primitive, we need to find the most specific method
1099 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1100 var m = self.compiler.mainmodule
1101 self.compiler.mainmodule = self.compiler.realmainmodule
1102 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1103 self.compiler.mainmodule = m
1104 return res
1105 end
1106
1107 return table_send(mmethod, arguments, mmethod.const_color)
1108 end
1109
1110 # Handle common special cases before doing the effective method invocation
1111 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1112 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1113 # Client must not forget to close the } after them.
1114 #
1115 # The value returned is the result of the common special cases.
1116 # If not null, client must compile it with the result of their own effective method invocation.
1117 #
1118 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1119 # is generated to cancel the effective method invocation that will follow
1120 # TODO: find a better approach
1121 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1122 do
1123 var res: nullable RuntimeVariable = null
1124 var recv = arguments.first
1125 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
1126 var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null
1127 if maybenull then
1128 self.add("if ({recv} == NULL) \{")
1129 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
1130 res = self.new_var(bool_type)
1131 var arg = arguments[1]
1132 if arg.mcasttype isa MNullableType then
1133 self.add("{res} = ({arg} == NULL);")
1134 else if arg.mcasttype isa MNullType then
1135 self.add("{res} = 1; /* is null */")
1136 else
1137 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1138 end
1139 else if mmethod.name == "!=" then
1140 res = self.new_var(bool_type)
1141 var arg = arguments[1]
1142 if arg.mcasttype isa MNullableType then
1143 self.add("{res} = ({arg} != NULL);")
1144 else if arg.mcasttype isa MNullType then
1145 self.add("{res} = 0; /* is null */")
1146 else
1147 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1148 end
1149 else
1150 self.add_abort("Receiver is null")
1151 end
1152 self.add("\} else \{")
1153 else
1154 self.add("\{")
1155 end
1156 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
1157 # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
1158 var arg = arguments[1]
1159 if arg.mcasttype isa MNullType then
1160 if res == null then res = self.new_var(bool_type)
1161 if mmethod.name == "!=" then
1162 self.add("{res} = 1; /* arg is null and recv is not */")
1163 else # `==` and `is_same_instance`
1164 self.add("{res} = 0; /* arg is null but recv is not */")
1165 end
1166 self.add("\}") # closes the null case
1167 self.add("if (0) \{") # what follow is useless, CC will drop it
1168 end
1169 end
1170 return res
1171 end
1172
1173 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
1174 do
1175 compiler.modelbuilder.nb_invok_by_tables += 1
1176 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1177
1178 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1179 var recv = arguments.first
1180
1181 var res0 = before_send(mmethod, arguments)
1182
1183 var runtime_function = mmethod.intro.virtual_runtime_function
1184 var msignature = runtime_function.called_signature
1185
1186 var res: nullable RuntimeVariable
1187 var ret = msignature.return_mtype
1188 if ret == null then
1189 res = null
1190 else
1191 res = self.new_var(ret)
1192 end
1193
1194 var ss = new FlatBuffer
1195
1196 ss.append("{recv}")
1197 for i in [0..msignature.arity[ do
1198 var a = arguments[i+1]
1199 var t = msignature.mparameters[i].mtype
1200 if i == msignature.vararg_rank then
1201 t = arguments[i+1].mcasttype
1202 end
1203 a = self.autobox(a, t)
1204 ss.append(", {a}")
1205 end
1206
1207 self.require_declaration(const_color)
1208 var call = "(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
1209
1210 if res != null then
1211 self.add("{res} = {call};")
1212 else
1213 self.add("{call};")
1214 end
1215
1216 if res0 != null then
1217 assert res != null
1218 assign(res0,res)
1219 res = res0
1220 end
1221
1222 self.add("\}") # closes the null case
1223
1224 return res
1225 end
1226
1227 redef fun call(mmethoddef, recvtype, arguments)
1228 do
1229 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1230
1231 var res: nullable RuntimeVariable
1232 var ret = mmethoddef.msignature.return_mtype
1233 if ret == null then
1234 res = null
1235 else
1236 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1237 res = self.new_var(ret)
1238 end
1239
1240 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1241 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1242 compiler.modelbuilder.nb_invok_by_inline += 1
1243 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1244 var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
1245 frame.returnlabel = self.get_name("RET_LABEL")
1246 frame.returnvar = res
1247 var old_frame = self.frame
1248 self.frame = frame
1249 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1250 mmethoddef.compile_inside_to_c(self, arguments)
1251 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1252 self.add("\}")
1253 self.frame = old_frame
1254 return res
1255 end
1256 compiler.modelbuilder.nb_invok_by_direct += 1
1257 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1258
1259 # Autobox arguments
1260 self.adapt_signature(mmethoddef, arguments)
1261
1262 self.require_declaration(mmethoddef.c_name)
1263 if res == null then
1264 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1265 return null
1266 else
1267 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1268 end
1269
1270 return res
1271 end
1272
1273 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1274 do
1275 if arguments.first.mcasttype.ctype != "val*" then
1276 # In order to shortcut the primitive, we need to find the most specific method
1277 # However, because of performance (no flattening), we always work on the realmainmodule
1278 var main = self.compiler.mainmodule
1279 self.compiler.mainmodule = self.compiler.realmainmodule
1280 var res = self.monomorphic_super_send(m, recvtype, arguments)
1281 self.compiler.mainmodule = main
1282 return res
1283 end
1284 return table_send(m.mproperty, arguments, m.const_color)
1285 end
1286
1287 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1288 do
1289 # A vararg must be stored into an new array
1290 # The trick is that the dymaic type of the array may depends on the receiver
1291 # of the method (ie recv) if the static type is unresolved
1292 # This is more complex than usual because the unresolved type must not be resolved
1293 # with the current receiver (ie self).
1294 # Therefore to isolate the resolution from self, a local StaticFrame is created.
1295 # One can see this implementation as an inlined method of the receiver whose only
1296 # job is to allocate the array
1297 var old_frame = self.frame
1298 var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1299 self.frame = frame
1300 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1301 var res = self.array_instance(varargs, elttype)
1302 self.frame = old_frame
1303 return res
1304 end
1305
1306 redef fun isset_attribute(a, recv)
1307 do
1308 self.check_recv_notnull(recv)
1309 var res = self.new_var(bool_type)
1310
1311 # What is the declared type of the attribute?
1312 var mtype = a.intro.static_mtype.as(not null)
1313 var intromclassdef = a.intro.mclassdef
1314 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1315
1316 if mtype isa MNullableType then
1317 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1318 return res
1319 end
1320
1321 self.require_declaration(a.const_color)
1322 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1323 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1324 else
1325
1326 if mtype.ctype == "val*" then
1327 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1328 else
1329 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1330 end
1331 end
1332 return res
1333 end
1334
1335 redef fun read_attribute(a, recv)
1336 do
1337 self.check_recv_notnull(recv)
1338
1339 # What is the declared type of the attribute?
1340 var ret = a.intro.static_mtype.as(not null)
1341 var intromclassdef = a.intro.mclassdef
1342 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1343
1344 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1345 self.compiler.attr_read_count += 1
1346 self.add("count_attr_reads++;")
1347 end
1348
1349 self.require_declaration(a.const_color)
1350 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1351 # Get the attribute or a box (ie. always a val*)
1352 var cret = self.object_type.as_nullable
1353 var res = self.new_var(cret)
1354 res.mcasttype = ret
1355
1356 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1357
1358 # Check for Uninitialized attribute
1359 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1360 self.add("if (unlikely({res} == NULL)) \{")
1361 self.add_abort("Uninitialized attribute {a.name}")
1362 self.add("\}")
1363
1364 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1365 self.compiler.isset_checks_count += 1
1366 self.add("count_isset_checks++;")
1367 end
1368 end
1369
1370 # Return the attribute or its unboxed version
1371 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1372 return self.autobox(res, ret)
1373 else
1374 var res = self.new_var(ret)
1375 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1376
1377 # Check for Uninitialized attribute
1378 if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1379 self.add("if (unlikely({res} == NULL)) \{")
1380 self.add_abort("Uninitialized attribute {a.name}")
1381 self.add("\}")
1382 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1383 self.compiler.isset_checks_count += 1
1384 self.add("count_isset_checks++;")
1385 end
1386 end
1387
1388 return res
1389 end
1390 end
1391
1392 redef fun write_attribute(a, recv, value)
1393 do
1394 self.check_recv_notnull(recv)
1395
1396 # What is the declared type of the attribute?
1397 var mtype = a.intro.static_mtype.as(not null)
1398 var intromclassdef = a.intro.mclassdef
1399 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1400
1401 # Adapt the value to the declared type
1402 value = self.autobox(value, mtype)
1403
1404 self.require_declaration(a.const_color)
1405 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1406 var attr = "{recv}->attrs[{a.const_color}]"
1407 if mtype.ctype != "val*" then
1408 assert mtype isa MClassType
1409 # The attribute is primitive, thus we store it in a box
1410 # The trick is to create the box the first time then resuse the box
1411 self.add("if ({attr} != NULL) \{")
1412 self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1413 self.add("\} else \{")
1414 value = self.autobox(value, self.object_type.as_nullable)
1415 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1416 self.add("\}")
1417 else
1418 # The attribute is not primitive, thus store it direclty
1419 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1420 end
1421 else
1422 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1423 end
1424 end
1425
1426 # Check that mtype is a live open type
1427 fun hardening_live_open_type(mtype: MType)
1428 do
1429 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1430 self.require_declaration(mtype.const_color)
1431 var col = mtype.const_color
1432 self.add("if({col} == -1) \{")
1433 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1434 self.add_abort("open type dead")
1435 self.add("\}")
1436 end
1437
1438 # Check that mtype it a pointer to a live cast type
1439 fun hardening_cast_type(t: String)
1440 do
1441 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1442 add("if({t} == NULL) \{")
1443 add_abort("cast type null")
1444 add("\}")
1445 add("if({t}->id == -1 || {t}->color == -1) \{")
1446 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1447 add_abort("cast type dead")
1448 add("\}")
1449 end
1450
1451 redef fun init_instance(mtype)
1452 do
1453 self.require_declaration("NEW_{mtype.mclass.c_name}")
1454 var compiler = self.compiler
1455 if mtype isa MGenericType and mtype.need_anchor then
1456 hardening_live_open_type(mtype)
1457 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1458 var recv = self.frame.arguments.first
1459 var recv_type_info = self.type_info(recv)
1460 self.require_declaration(mtype.const_color)
1461 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1462 end
1463 compiler.undead_types.add(mtype)
1464 self.require_declaration("type_{mtype.c_name}")
1465 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1466 end
1467
1468 redef fun type_test(value, mtype, tag)
1469 do
1470 self.add("/* {value.inspect} isa {mtype} */")
1471 var compiler = self.compiler
1472
1473 var recv = self.frame.arguments.first
1474 var recv_type_info = self.type_info(recv)
1475
1476 var res = self.new_var(bool_type)
1477
1478 var cltype = self.get_name("cltype")
1479 self.add_decl("int {cltype};")
1480 var idtype = self.get_name("idtype")
1481 self.add_decl("int {idtype};")
1482
1483 var maybe_null = self.maybe_null(value)
1484 var accept_null = "0"
1485 var ntype = mtype
1486 if ntype isa MNullableType then
1487 ntype = ntype.mtype
1488 accept_null = "1"
1489 end
1490
1491 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1492 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1493 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1494 self.compiler.count_type_test_skipped[tag] += 1
1495 self.add("count_type_test_skipped_{tag}++;")
1496 end
1497 return res
1498 end
1499
1500 if ntype.need_anchor then
1501 var type_struct = self.get_name("type_struct")
1502 self.add_decl("const struct type* {type_struct};")
1503
1504 # Either with resolution_table with a direct resolution
1505 hardening_live_open_type(mtype)
1506 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1507 self.require_declaration(mtype.const_color)
1508 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1509 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1510 self.compiler.count_type_test_unresolved[tag] += 1
1511 self.add("count_type_test_unresolved_{tag}++;")
1512 end
1513 hardening_cast_type(type_struct)
1514 self.add("{cltype} = {type_struct}->color;")
1515 self.add("{idtype} = {type_struct}->id;")
1516 if maybe_null and accept_null == "0" then
1517 var is_nullable = self.get_name("is_nullable")
1518 self.add_decl("short int {is_nullable};")
1519 self.add("{is_nullable} = {type_struct}->is_nullable;")
1520 accept_null = is_nullable.to_s
1521 end
1522 else if ntype isa MClassType then
1523 compiler.undead_types.add(mtype)
1524 self.require_declaration("type_{mtype.c_name}")
1525 hardening_cast_type("(&type_{mtype.c_name})")
1526 self.add("{cltype} = type_{mtype.c_name}.color;")
1527 self.add("{idtype} = type_{mtype.c_name}.id;")
1528 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1529 self.compiler.count_type_test_resolved[tag] += 1
1530 self.add("count_type_test_resolved_{tag}++;")
1531 end
1532 else
1533 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);")
1534 end
1535
1536 # check color is in table
1537 if maybe_null then
1538 self.add("if({value} == NULL) \{")
1539 self.add("{res} = {accept_null};")
1540 self.add("\} else \{")
1541 end
1542 var value_type_info = self.type_info(value)
1543 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1544 self.add("{res} = 0;")
1545 self.add("\} else \{")
1546 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1547 self.add("\}")
1548 if maybe_null then
1549 self.add("\}")
1550 end
1551
1552 return res
1553 end
1554
1555 redef fun is_same_type_test(value1, value2)
1556 do
1557 var res = self.new_var(bool_type)
1558 # Swap values to be symetric
1559 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1560 var tmp = value1
1561 value1 = value2
1562 value2 = tmp
1563 end
1564 if value1.mtype.ctype != "val*" then
1565 if value2.mtype == value1.mtype then
1566 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1567 else if value2.mtype.ctype != "val*" then
1568 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1569 else
1570 var mtype1 = value1.mtype.as(MClassType)
1571 self.require_declaration("class_{mtype1.c_name}")
1572 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
1573 end
1574 else
1575 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {value1}->class == {value2}->class); /* is_same_type_test */")
1576 end
1577 return res
1578 end
1579
1580 redef fun class_name_string(value)
1581 do
1582 var res = self.get_name("var_class_name")
1583 self.add_decl("const char* {res};")
1584 if value.mtype.ctype == "val*" then
1585 self.add "{res} = {value} == NULL ? \"null\" : {value}->type->name;"
1586 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
1587 value.mtype.as(MClassType).name != "NativeString" then
1588 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1589 else
1590 self.require_declaration("type_{value.mtype.c_name}")
1591 self.add "{res} = type_{value.mtype.c_name}.name;"
1592 end
1593 return res
1594 end
1595
1596 redef fun equal_test(value1, value2)
1597 do
1598 var res = self.new_var(bool_type)
1599 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1600 var tmp = value1
1601 value1 = value2
1602 value2 = tmp
1603 end
1604 if value1.mtype.ctype != "val*" then
1605 if value2.mtype == value1.mtype then
1606 self.add("{res} = {value1} == {value2};")
1607 else if value2.mtype.ctype != "val*" then
1608 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1609 else
1610 var mtype1 = value1.mtype.as(MClassType)
1611 self.require_declaration("class_{mtype1.c_name}")
1612 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
1613 self.add("if ({res}) \{")
1614 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1615 self.add("\}")
1616 end
1617 return res
1618 end
1619 var maybe_null = true
1620 var test = new Array[String]
1621 var t1 = value1.mcasttype
1622 if t1 isa MNullableType then
1623 test.add("{value1} != NULL")
1624 t1 = t1.mtype
1625 else
1626 maybe_null = false
1627 end
1628 var t2 = value2.mcasttype
1629 if t2 isa MNullableType then
1630 test.add("{value2} != NULL")
1631 t2 = t2.mtype
1632 else
1633 maybe_null = false
1634 end
1635
1636 var incompatible = false
1637 var primitive
1638 if t1.ctype != "val*" then
1639 primitive = t1
1640 if t1 == t2 then
1641 # No need to compare class
1642 else if t2.ctype != "val*" then
1643 incompatible = true
1644 else if can_be_primitive(value2) then
1645 test.add("{value1}->class == {value2}->class")
1646 else
1647 incompatible = true
1648 end
1649 else if t2.ctype != "val*" then
1650 primitive = t2
1651 if can_be_primitive(value1) then
1652 test.add("{value1}->class == {value2}->class")
1653 else
1654 incompatible = true
1655 end
1656 else
1657 primitive = null
1658 end
1659
1660 if incompatible then
1661 if maybe_null then
1662 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
1663 return res
1664 else
1665 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
1666 return res
1667 end
1668 end
1669 if primitive != null then
1670 test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
1671 else if can_be_primitive(value1) and can_be_primitive(value2) then
1672 test.add("{value1}->class == {value2}->class")
1673 var s = new Array[String]
1674 for t, v in self.compiler.box_kinds do
1675 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
1676 end
1677 test.add("({s.join(" || ")})")
1678 else
1679 self.add("{res} = {value1} == {value2};")
1680 return res
1681 end
1682 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
1683 return res
1684 end
1685
1686 fun can_be_primitive(value: RuntimeVariable): Bool
1687 do
1688 var t = value.mcasttype.as_notnullable
1689 if not t isa MClassType then return false
1690 var k = t.mclass.kind
1691 return k == interface_kind or t.ctype != "val*"
1692 end
1693
1694 fun maybe_null(value: RuntimeVariable): Bool
1695 do
1696 var t = value.mcasttype
1697 return t isa MNullableType or t isa MNullType
1698 end
1699
1700 redef fun array_instance(array, elttype)
1701 do
1702 var nclass = self.get_class("NativeArray")
1703 var arrayclass = self.get_class("Array")
1704 var arraytype = arrayclass.get_mtype([elttype])
1705 var res = self.init_instance(arraytype)
1706 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
1707 var length = self.int_instance(array.length)
1708 var nat = native_array_instance(elttype, length)
1709 for i in [0..array.length[ do
1710 var r = self.autobox(array[i], self.object_type)
1711 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
1712 end
1713 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
1714 self.add("\}")
1715 return res
1716 end
1717
1718 redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
1719 do
1720 var mtype = self.get_class("NativeArray").get_mtype([elttype])
1721 self.require_declaration("NEW_{mtype.mclass.c_name}")
1722 assert mtype isa MGenericType
1723 var compiler = self.compiler
1724 if mtype.need_anchor then
1725 hardening_live_open_type(mtype)
1726 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1727 var recv = self.frame.arguments.first
1728 var recv_type_info = self.type_info(recv)
1729 self.require_declaration(mtype.const_color)
1730 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1731 end
1732 compiler.undead_types.add(mtype)
1733 self.require_declaration("type_{mtype.c_name}")
1734 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
1735 end
1736
1737 redef fun native_array_def(pname, ret_type, arguments)
1738 do
1739 var elttype = arguments.first.mtype
1740 var nclass = self.get_class("NativeArray")
1741 var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
1742 if pname == "[]" then
1743 self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
1744 return
1745 else if pname == "[]=" then
1746 self.add("{recv}[{arguments[1]}]={arguments[2]};")
1747 return
1748 else if pname == "length" then
1749 self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
1750 return
1751 else if pname == "copy_to" then
1752 var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
1753 self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
1754 return
1755 end
1756 end
1757
1758 redef fun calloc_array(ret_type, arguments)
1759 do
1760 var mclass = self.get_class("ArrayCapable")
1761 var ft = mclass.mparameters.first
1762 var res = self.native_array_instance(ft, arguments[1])
1763 self.ret(res)
1764 end
1765
1766 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
1767 assert mtype.need_anchor
1768 var compiler = self.compiler
1769 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
1770 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
1771 end
1772 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
1773 end
1774 end
1775
1776 redef class MMethodDef
1777 # The C function associated to a mmethoddef
1778 fun separate_runtime_function: SeparateRuntimeFunction
1779 do
1780 var res = self.separate_runtime_function_cache
1781 if res == null then
1782 var recv = mclassdef.bound_mtype
1783 var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
1784 res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
1785 self.separate_runtime_function_cache = res
1786 end
1787 return res
1788 end
1789 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
1790
1791 # The C function associated to a mmethoddef, that can be stored into a VFT of a class
1792 # The first parameter (the reciever) is always typed by val* in order to accept an object value
1793 # The C-signature is always compatible with the intro
1794 fun virtual_runtime_function: SeparateRuntimeFunction
1795 do
1796 var res = self.virtual_runtime_function_cache
1797 if res == null then
1798 # Because the function is virtual, the signature must match the one of the original class
1799 var intromclassdef = mproperty.intro.mclassdef
1800 var recv = intromclassdef.bound_mtype
1801
1802 res = separate_runtime_function
1803 if res.called_recv == recv then
1804 self.virtual_runtime_function_cache = res
1805 return res
1806 end
1807
1808 var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
1809
1810 if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
1811 self.virtual_runtime_function_cache = res
1812 return res
1813 end
1814
1815 res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
1816 self.virtual_runtime_function_cache = res
1817 res.is_thunk = true
1818 end
1819 return res
1820 end
1821 private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
1822 end
1823
1824 redef class MSignature
1825 # Does the C-version of `self` the same than the C-version of `other`?
1826 fun c_equiv(other: MSignature): Bool
1827 do
1828 if self == other then return true
1829 if arity != other.arity then return false
1830 for i in [0..arity[ do
1831 if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
1832 end
1833 if return_mtype != other.return_mtype then
1834 if return_mtype == null or other.return_mtype == null then return false
1835 if return_mtype.ctype != other.return_mtype.ctype then return false
1836 end
1837 return true
1838 end
1839 end
1840
1841 # The C function associated to a methoddef separately compiled
1842 class SeparateRuntimeFunction
1843 super AbstractRuntimeFunction
1844
1845 # The call-side static receiver
1846 var called_recv: MType
1847
1848 # The call-side static signature
1849 var called_signature: MSignature
1850
1851 # The name on the compiled method
1852 redef var build_c_name: String
1853
1854 # Statically call the original body instead
1855 var is_thunk = false
1856
1857 redef fun to_s do return self.mmethoddef.to_s
1858
1859 # The C return type (something or `void`)
1860 var c_ret: String is lazy do
1861 var ret = called_signature.return_mtype
1862 if ret != null then
1863 return ret.ctype
1864 else
1865 return "void"
1866 end
1867 end
1868
1869 # The C signature (only the parmeter part)
1870 var c_sig: String is lazy do
1871 var sig = new FlatBuffer
1872 sig.append("({called_recv.ctype} self")
1873 for i in [0..called_signature.arity[ do
1874 var mtype = called_signature.mparameters[i].mtype
1875 if i == called_signature.vararg_rank then
1876 mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
1877 end
1878 sig.append(", {mtype.ctype} p{i}")
1879 end
1880 sig.append(")")
1881 return sig.to_s
1882 end
1883
1884 # The C type for the function pointer.
1885 var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
1886
1887 redef fun compile_to_c(compiler)
1888 do
1889 var mmethoddef = self.mmethoddef
1890
1891 var recv = self.mmethoddef.mclassdef.bound_mtype
1892 var v = compiler.new_visitor
1893 var selfvar = new RuntimeVariable("self", called_recv, recv)
1894 var arguments = new Array[RuntimeVariable]
1895 var frame = new StaticFrame(v, mmethoddef, recv, arguments)
1896 v.frame = frame
1897
1898 var msignature = called_signature
1899 var ret = called_signature.return_mtype
1900
1901 var sig = new FlatBuffer
1902 var comment = new FlatBuffer
1903 sig.append(c_ret)
1904 sig.append(" ")
1905 sig.append(self.c_name)
1906 sig.append(c_sig)
1907 comment.append("({selfvar}: {selfvar.mtype}")
1908 arguments.add(selfvar)
1909 for i in [0..msignature.arity[ do
1910 var mtype = msignature.mparameters[i].mtype
1911 if i == msignature.vararg_rank then
1912 mtype = v.get_class("Array").get_mtype([mtype])
1913 end
1914 comment.append(", {mtype}")
1915 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
1916 arguments.add(argvar)
1917 end
1918 comment.append(")")
1919 if ret != null then
1920 comment.append(": {ret}")
1921 end
1922 compiler.provide_declaration(self.c_name, "{sig};")
1923
1924 v.add_decl("/* method {self} for {comment} */")
1925 v.add_decl("{sig} \{")
1926 if ret != null then
1927 frame.returnvar = v.new_var(ret)
1928 end
1929 frame.returnlabel = v.get_name("RET_LABEL")
1930
1931 if is_thunk then
1932 var subret = v.call(mmethoddef, recv, arguments)
1933 if ret != null then
1934 assert subret != null
1935 v.assign(frame.returnvar.as(not null), subret)
1936 end
1937 else
1938 mmethoddef.compile_inside_to_c(v, arguments)
1939 end
1940
1941 v.add("{frame.returnlabel.as(not null)}:;")
1942 if ret != null then
1943 v.add("return {frame.returnvar.as(not null)};")
1944 end
1945 v.add("\}")
1946 compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
1947 end
1948 end
1949
1950 redef class MEntity
1951 var const_color: String is lazy do return "COLOR_{c_name}"
1952 end
1953
1954 interface PropertyLayoutElement end
1955
1956 redef class MProperty
1957 super PropertyLayoutElement
1958 end
1959
1960 redef class MPropDef
1961 super PropertyLayoutElement
1962 end
1963
1964 redef class AMethPropdef
1965 # The semi-global compilation does not support inlining calls to extern news
1966 redef fun can_inline
1967 do
1968 var m = mpropdef
1969 if m != null and m.mproperty.is_init and m.is_extern then return false
1970 return super
1971 end
1972 end