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