Callref expression support for Separate Compiler.
[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 attributes 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 # --no-tag-primitives
33 var opt_no_tag_primitives = new OptionBool("Use only boxes for primitive types", "--no-tag-primitives")
34
35 # --colors-are-symbols
36 var opt_colors_are_symbols = new OptionBool("Store colors as symbols instead of static data (link-boost)", "--colors-are-symbols")
37 # --trampoline-call
38 var opt_trampoline_call = new OptionBool("Use an indirection when calling", "--trampoline-call")
39 # --guard-call
40 var opt_guard_call = new OptionBool("Guard VFT calls with a direct call", "--guard-call")
41 # --substitute-monomorph
42 var opt_substitute_monomorph = new OptionBool("Replace monomorphic trampolines with direct calls (link-boost)", "--substitute-monomorph")
43 # --link-boost
44 var opt_link_boost = new OptionBool("Enable all link-boost optimizations", "--link-boost")
45
46 # --inline-coloring-numbers
47 var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
48 # --inline-some-methods
49 var opt_inline_some_methods = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
50 # --direct-call-monomorph
51 var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorphic sites (semi-global)", "--direct-call-monomorph")
52 # --direct-call-monomorph0
53 var opt_direct_call_monomorph0 = new OptionBool("Allow the separate compiler to direct call monomorphic sites (semi-global)", "--direct-call-monomorph0")
54 # --skip-dead-methods
55 var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
56 # --semi-global
57 var opt_semi_global = new OptionBool("Enable all semi-global optimizations", "--semi-global")
58 # --no-colo-dead-methods
59 var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods")
60 # --tables-metrics
61 var opt_tables_metrics = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
62 # --type-poset
63 var opt_type_poset = new OptionBool("Build a poset of types to create more condensed tables", "--type-poset")
64
65 redef init
66 do
67 super
68 self.option_context.add_option(self.opt_separate)
69 self.option_context.add_option(self.opt_no_inline_intern)
70 self.option_context.add_option(self.opt_no_union_attribute)
71 self.option_context.add_option(self.opt_no_shortcut_equate)
72 self.option_context.add_option(self.opt_no_tag_primitives)
73 self.option_context.add_option(opt_colors_are_symbols, opt_trampoline_call, opt_guard_call, opt_direct_call_monomorph0, opt_substitute_monomorph, opt_link_boost)
74 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)
75 self.option_context.add_option(self.opt_colo_dead_methods)
76 self.option_context.add_option(self.opt_tables_metrics)
77 self.option_context.add_option(self.opt_type_poset)
78 end
79
80 redef fun process_options(args)
81 do
82 super
83
84 var tc = self
85 if tc.opt_semi_global.value then
86 tc.opt_inline_coloring_numbers.value = true
87 tc.opt_inline_some_methods.value = true
88 tc.opt_direct_call_monomorph.value = true
89 tc.opt_skip_dead_methods.value = true
90 end
91 if tc.opt_link_boost.value then
92 tc.opt_colors_are_symbols.value = true
93 tc.opt_substitute_monomorph.value = true
94 end
95 if tc.opt_substitute_monomorph.value then
96 tc.opt_trampoline_call.value = true
97 end
98 end
99
100 var separate_compiler_phase = new SeparateCompilerPhase(self, null)
101 end
102
103 class SeparateCompilerPhase
104 super Phase
105 redef fun process_mainmodule(mainmodule, given_mmodules) do
106 if not toolcontext.opt_separate.value then return
107
108 var modelbuilder = toolcontext.modelbuilder
109 var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
110 modelbuilder.run_separate_compiler(mainmodule, analysis)
111 end
112 end
113
114 redef class ModelBuilder
115 fun run_separate_compiler(mainmodule: MModule, runtime_type_analysis: nullable RapidTypeAnalysis)
116 do
117 var time0 = get_time
118 self.toolcontext.info("*** GENERATING C ***", 1)
119
120 var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis)
121 compiler.do_compilation
122 compiler.display_stats
123
124 var time1 = get_time
125 self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2)
126 write_and_make(compiler)
127 end
128
129 # Count number of invocations by VFT
130 private var nb_invok_by_tables = 0
131 # Count number of invocations by direct call
132 private var nb_invok_by_direct = 0
133 # Count number of invocations by inlining
134 private var nb_invok_by_inline = 0
135 end
136
137 # Singleton that store the knowledge about the separate compilation process
138 class SeparateCompiler
139 super AbstractCompiler
140
141 redef type VISITOR: SeparateCompilerVisitor
142
143 # The result of the RTA (used to know live types and methods)
144 var runtime_type_analysis: nullable RapidTypeAnalysis
145
146 private var undead_types: Set[MType] = new HashSet[MType]
147 private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]]
148
149 private var type_ids: Map[MType, Int] is noinit
150 private var type_colors: Map[MType, Int] is noinit
151 private var opentype_colors: Map[MType, Int] is noinit
152 private var thunks_to_compile: Set[SeparateRuntimeFunction] = new HashSet[SeparateRuntimeFunction]
153
154 init do
155 var file = new_file("nit.common")
156 self.header = new CodeWriter(file)
157 self.compile_box_kinds
158 end
159
160 redef fun do_compilation
161 do
162 var compiler = self
163 compiler.compile_header
164
165 var c_name = mainmodule.c_name
166
167 # compile class structures
168 modelbuilder.toolcontext.info("Property coloring", 2)
169 compiler.new_file("{c_name}.classes")
170 compiler.do_property_coloring
171 compiler.compile_class_infos
172 for m in mainmodule.in_importation.greaters do
173 for mclass in m.intro_mclasses do
174 #if mclass.kind == abstract_kind or mclass.kind == interface_kind then continue
175 compiler.compile_class_to_c(mclass)
176 end
177 end
178
179 # The main function of the C
180 compiler.new_file("{c_name}.main")
181 compiler.compile_nitni_global_ref_functions
182 compiler.compile_main_function
183 compiler.compile_finalizer_function
184 compiler.link_mmethods
185
186 # compile methods
187 for m in mainmodule.in_importation.greaters do
188 modelbuilder.toolcontext.info("Generate C for module {m.full_name}", 2)
189 compiler.new_file("{m.c_name}.sep")
190 compiler.compile_module_to_c(m)
191 end
192
193 # compile live & cast type structures
194 modelbuilder.toolcontext.info("Type coloring", 2)
195 compiler.new_file("{c_name}.types")
196 compiler.compile_types
197 end
198
199 fun thunk_todo(thunk: SeparateRuntimeFunction)
200 do
201 # Concrete instance of `SeparateRuntimeFunction` are already
202 # handled by the compiler. Avoid duplicate compilation.
203 if thunk isa SeparateThunkFunction then
204 thunks_to_compile.add(thunk)
205 end
206 end
207
208 # Color and compile type structures and cast information
209 fun compile_types
210 do
211 var compiler = self
212
213 var mtypes = compiler.do_type_coloring
214 for t in mtypes do
215 compiler.compile_type_to_c(t)
216 end
217 # compile remaining types structures (useless but needed for the symbol resolution at link-time)
218 for t in compiler.undead_types do
219 if mtypes.has(t) then continue
220 compiler.compile_type_to_c(t)
221 end
222
223 end
224
225 redef fun compile_header_structs do
226 self.header.add_decl("typedef void(*nitmethod_t)(void); /* general C type representing a Nit method. */")
227 self.compile_header_attribute_structs
228 self.header.add_decl("struct class \{ int box_kind; nitmethod_t vft[]; \}; /* general C type representing a Nit class. */")
229
230 # With resolution_table_table, all live type resolution are stored in a big table: resolution_table
231 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. */")
232 self.header.add_decl("struct instance \{ const struct type *type; const struct class *class; nitattribute_t attrs[]; \}; /* general C type representing a Nit instance. */")
233 self.header.add_decl("struct types \{ int dummy; const struct type *types[]; \}; /* a list types (used for vts, fts and unresolved lists). */")
234 self.header.add_decl("typedef struct instance val; /* general C type representing a Nit instance. */")
235
236 if not modelbuilder.toolcontext.opt_no_tag_primitives.value then
237 self.header.add_decl("extern const struct class *class_info[];")
238 self.header.add_decl("extern const struct type *type_info[];")
239 end
240 end
241
242 fun compile_header_attribute_structs
243 do
244 if modelbuilder.toolcontext.opt_no_union_attribute.value then
245 self.header.add_decl("typedef void* nitattribute_t; /* general C type representing a Nit attribute. */")
246 else
247 self.header.add_decl("typedef union \{")
248 self.header.add_decl("void* val;")
249 for c, v in self.box_kinds do
250 var t = c.mclass_type
251
252 # `Pointer` reuse the `val` field
253 if t.mclass.name == "Pointer" then continue
254
255 self.header.add_decl("{t.ctype_extern} {t.ctypename};")
256 end
257 self.header.add_decl("\} nitattribute_t; /* general C type representing a Nit attribute. */")
258 end
259 end
260
261 fun compile_box_kinds
262 do
263 # Collect all bas box class
264 # FIXME: this is not completely fine with a separate compilation scheme
265 for classname in ["Int", "Bool", "Byte", "Char", "Float", "CString",
266 "Pointer", "Int8", "Int16", "UInt16", "Int32", "UInt32"] do
267 var classes = self.mainmodule.model.get_mclasses_by_name(classname)
268 if classes == null then continue
269 assert classes.length == 1 else print_error classes.join(", ")
270 self.box_kinds[classes.first] = self.box_kinds.length + 1
271 end
272 end
273
274 var box_kinds = new HashMap[MClass, Int]
275
276 fun box_kind_of(mclass: MClass): Int
277 do
278 #var pointer_type = self.mainmodule.pointer_type
279 #if mclass.mclass_type.ctype == "val*" or mclass.mclass_type.is_subtype(self.mainmodule, mclass.mclass_type pointer_type) then
280 if mclass.mclass_type.ctype_extern == "val*" then
281 return 0
282 else if mclass.kind == extern_kind and mclass.name != "CString" then
283 return self.box_kinds[self.mainmodule.pointer_type.mclass]
284 else
285 return self.box_kinds[mclass]
286 end
287 end
288
289 fun compile_color_consts(colors: Map[Object, Int]) do
290 var v = new_visitor
291 for m, c in colors do
292 compile_color_const(v, m, c)
293 end
294 end
295
296 fun compile_color_const(v: SeparateCompilerVisitor, m: Object, color: Int) do
297 if color_consts_done.has(m) then return
298 if m isa MEntity then
299 if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
300 self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
301 else if not modelbuilder.toolcontext.opt_colors_are_symbols.value or not v.compiler.target_platform.supports_linker_script then
302 self.provide_declaration(m.const_color, "extern const int {m.const_color};")
303 v.add("const int {m.const_color} = {color};")
304 else
305 # The color 'C' is the ``address'' of a false static variable 'XC'
306 self.provide_declaration(m.const_color, "#define {m.const_color} ((long)&X{m.const_color})\nextern const void X{m.const_color};")
307 if color == -1 then color = 0 # Symbols cannot be negative, so just use 0 for dead things
308 # Teach the linker that the address of 'XC' is `color`.
309 linker_script.add("X{m.const_color} = {color};")
310 end
311 else
312 abort
313 end
314 color_consts_done.add(m)
315 end
316
317 private var color_consts_done = new HashSet[Object]
318
319 # The conflict graph of classes used for coloration
320 var class_conflict_graph: POSetConflictGraph[MClass] is noinit
321
322 # colorize classe properties
323 fun do_property_coloring do
324
325 var rta = runtime_type_analysis
326
327 # Class graph
328 var mclasses = mainmodule.flatten_mclass_hierarchy
329 class_conflict_graph = mclasses.to_conflict_graph
330
331 # Prepare to collect elements to color and build layout with
332 var mmethods = new HashMap[MClass, Set[PropertyLayoutElement]]
333 var mattributes = new HashMap[MClass, Set[MAttribute]]
334
335 # The dead methods and super-call, still need to provide a dead color symbol
336 var dead_methods = new Array[PropertyLayoutElement]
337
338 for mclass in mclasses do
339 mmethods[mclass] = new HashSet[PropertyLayoutElement]
340 mattributes[mclass] = new HashSet[MAttribute]
341 end
342
343 # Pre-collect known live things
344 if rta != null then
345 for m in rta.live_methods do
346 mmethods[m.intro_mclassdef.mclass].add m
347 end
348 for m in rta.live_super_sends do
349 var mclass = m.mclassdef.mclass
350 mmethods[mclass].add m
351 end
352 end
353
354 for m in mainmodule.in_importation.greaters do for cd in m.mclassdefs do
355 var mclass = cd.mclass
356 # Collect methods and attributes
357 for p in cd.intro_mproperties do
358 if p isa MMethod then
359 if rta == null then
360 mmethods[mclass].add p
361 else if not rta.live_methods.has(p) then
362 dead_methods.add p
363 end
364 else if p isa MAttribute then
365 mattributes[mclass].add p
366 end
367 end
368
369 # Collect all super calls (dead or not)
370 for mpropdef in cd.mpropdefs do
371 if not mpropdef isa MMethodDef then continue
372 if mpropdef.has_supercall then
373 if rta == null then
374 mmethods[mclass].add mpropdef
375 else if not rta.live_super_sends.has(mpropdef) then
376 dead_methods.add mpropdef
377 end
378 end
379 end
380 end
381
382 # methods coloration
383 var meth_colorer = new POSetGroupColorer[MClass, PropertyLayoutElement](class_conflict_graph, mmethods)
384 var method_colors = meth_colorer.colors
385 compile_color_consts(method_colors)
386
387 # give null color to dead methods and supercalls
388 for mproperty in dead_methods do compile_color_const(new_visitor, mproperty, -1)
389
390 # attribute coloration
391 var attr_colorer = new POSetGroupColorer[MClass, MAttribute](class_conflict_graph, mattributes)
392 var attr_colors = attr_colorer.colors#ize(poset, mattributes)
393 compile_color_consts(attr_colors)
394
395 # Build method and attribute tables
396 method_tables = new HashMap[MClass, Array[nullable MPropDef]]
397 attr_tables = new HashMap[MClass, Array[nullable MProperty]]
398 for mclass in mclasses do
399 if not mclass.has_new_factory and (mclass.kind == abstract_kind or mclass.kind == interface_kind) then continue
400 if rta != null and not rta.live_classes.has(mclass) then continue
401
402 var mtype = mclass.intro.bound_mtype
403
404 # Resolve elements in the layout to get the final table
405 var meth_layout = meth_colorer.build_layout(mclass)
406 var meth_table = new Array[nullable MPropDef].with_capacity(meth_layout.length)
407 method_tables[mclass] = meth_table
408 for e in meth_layout do
409 if e == null then
410 meth_table.add null
411 else if e isa MMethod then
412 # Standard method call of `e`
413 meth_table.add e.lookup_first_definition(mainmodule, mtype)
414 else if e isa MMethodDef then
415 # Super-call in the methoddef `e`
416 meth_table.add e.lookup_next_definition(mainmodule, mtype)
417 else
418 abort
419 end
420 end
421
422 # Do not need to resolve attributes as only the position is used
423 attr_tables[mclass] = attr_colorer.build_layout(mclass)
424 end
425
426 end
427
428 # colorize live types of the program
429 private fun do_type_coloring: Collection[MType] do
430 # Collect types to colorize
431 var live_types = runtime_type_analysis.live_types
432 var live_cast_types = runtime_type_analysis.live_cast_types
433
434 var res = new HashSet[MType]
435 res.add_all live_types
436 res.add_all live_cast_types
437
438 if modelbuilder.toolcontext.opt_type_poset.value then
439 # Compute colors with a type poset
440 var poset = poset_from_mtypes(live_types, live_cast_types)
441 var colorer = new POSetColorer[MType]
442 colorer.colorize(poset)
443 type_ids = colorer.ids
444 type_colors = colorer.colors
445 type_tables = build_type_tables(poset)
446 else
447 # Compute colors using the class poset
448 # Faster to compute but the number of holes can degenerate
449 compute_type_test_layouts(live_types, live_cast_types)
450
451 type_ids = new HashMap[MType, Int]
452 for x in res do type_ids[x] = type_ids.length + 1
453 end
454
455 # VT and FT are stored with other unresolved types in the big resolution_tables
456 self.compute_resolution_tables(live_types)
457
458 return res
459 end
460
461 private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
462 var poset = new POSet[MType]
463
464 # Instead of doing the full matrix mtypes X cast_types,
465 # a grouping is done by the base classes of the type so
466 # that we compare only types whose base classes are in inheritance.
467
468 var mtypes_by_class = new MultiHashMap[MClass, MType]
469 for e in mtypes do
470 var c = e.undecorate.as(MClassType).mclass
471 mtypes_by_class[c].add(e)
472 poset.add_node(e)
473 end
474
475 var casttypes_by_class = new MultiHashMap[MClass, MType]
476 for e in cast_types do
477 var c = e.undecorate.as(MClassType).mclass
478 casttypes_by_class[c].add(e)
479 poset.add_node(e)
480 end
481
482 for c1, ts1 in mtypes_by_class do
483 for c2 in c1.in_hierarchy(mainmodule).greaters do
484 var ts2 = casttypes_by_class[c2]
485 for e in ts1 do
486 for o in ts2 do
487 if e == o then continue
488 if e.is_subtype(mainmodule, null, o) then
489 poset.add_edge(e, o)
490 end
491 end
492 end
493 end
494 end
495 return poset
496 end
497
498 # Build type tables
499 fun build_type_tables(mtypes: POSet[MType]): Map[MType, Array[nullable MType]] do
500 var tables = new HashMap[MType, Array[nullable MType]]
501 for mtype in mtypes do
502 var table = new Array[nullable MType]
503 for sup in mtypes[mtype].greaters do
504 var color = type_colors[sup]
505 if table.length <= color then
506 for i in [table.length .. color[ do
507 table[i] = null
508 end
509 end
510 table[color] = sup
511 end
512 tables[mtype] = table
513 end
514 return tables
515 end
516
517 private fun compute_type_test_layouts(mtypes: Set[MClassType], cast_types: Set[MType]) do
518 # Group cast_type by their classes
519 var bucklets = new HashMap[MClass, Set[MType]]
520 for e in cast_types do
521 var c = e.undecorate.as(MClassType).mclass
522 if not bucklets.has_key(c) then
523 bucklets[c] = new HashSet[MType]
524 end
525 bucklets[c].add(e)
526 end
527
528 # Colorize cast_types from the class hierarchy
529 var colorer = new POSetGroupColorer[MClass, MType](class_conflict_graph, bucklets)
530 type_colors = colorer.colors
531
532 var layouts = new HashMap[MClass, Array[nullable MType]]
533 for c in runtime_type_analysis.live_classes do
534 layouts[c] = colorer.build_layout(c)
535 end
536
537 # Build the table for each live type
538 for t in mtypes do
539 # A live type use the layout of its class
540 var c = t.mclass
541 var layout = layouts[c]
542 var table = new Array[nullable MType].with_capacity(layout.length)
543 type_tables[t] = table
544
545 # For each potential super-type in the layout
546 for sup in layout do
547 if sup == null then
548 table.add null
549 else if t.is_subtype(mainmodule, null, sup) then
550 table.add sup
551 else
552 table.add null
553 end
554 end
555 end
556 end
557
558 # resolution_tables is used to perform a type resolution at runtime in O(1)
559 private fun compute_resolution_tables(mtypes: Set[MType]) do
560 # During the visit of the body of classes, live_unresolved_types are collected
561 # and associated to
562 # Collect all live_unresolved_types (visited in the body of classes)
563
564 # Determinate fo each livetype what are its possible requested anchored types
565 var mtype2unresolved = new HashMap[MClass, Set[MType]]
566 for mtype in self.runtime_type_analysis.live_types do
567 var mclass = mtype.mclass
568 var set = mtype2unresolved.get_or_null(mclass)
569 if set == null then
570 set = new HashSet[MType]
571 mtype2unresolved[mclass] = set
572 end
573 for cd in mtype.collect_mclassdefs(self.mainmodule) do
574 if self.live_unresolved_types.has_key(cd) then
575 set.add_all(self.live_unresolved_types[cd])
576 end
577 end
578 end
579
580 # Compute the table layout with the prefered method
581 var colorer = new BucketsColorer[MClass, MType]
582
583 opentype_colors = colorer.colorize(mtype2unresolved)
584 resolution_tables = self.build_resolution_tables(self.runtime_type_analysis.live_types, mtype2unresolved)
585
586 # Compile a C constant for each collected unresolved type.
587 # Either to a color, or to -1 if the unresolved type is dead (no live receiver can require it)
588 var all_unresolved = new HashSet[MType]
589 for t in self.live_unresolved_types.values do
590 all_unresolved.add_all(t)
591 end
592 var all_unresolved_types_colors = new HashMap[MType, Int]
593 for t in all_unresolved do
594 if opentype_colors.has_key(t) then
595 all_unresolved_types_colors[t] = opentype_colors[t]
596 else
597 all_unresolved_types_colors[t] = -1
598 end
599 end
600 self.compile_color_consts(all_unresolved_types_colors)
601
602 #print "tables"
603 #for k, v in unresolved_types_tables.as(not null) do
604 # print "{k}: {v.join(", ")}"
605 #end
606 #print ""
607 end
608
609 fun build_resolution_tables(elements: Set[MClassType], map: Map[MClass, Set[MType]]): Map[MClassType, Array[nullable MType]] do
610 var tables = new HashMap[MClassType, Array[nullable MType]]
611 for mclasstype in elements do
612 var mtypes = map[mclasstype.mclass]
613 var table = new Array[nullable MType]
614 for mtype in mtypes do
615 var color = opentype_colors[mtype]
616 if table.length <= color then
617 for i in [table.length .. color[ do
618 table[i] = null
619 end
620 end
621 table[color] = mtype
622 end
623 tables[mclasstype] = table
624 end
625 return tables
626 end
627
628 # Separately compile all the method definitions of the module
629 fun compile_module_to_c(mmodule: MModule)
630 do
631 var old_module = self.mainmodule
632 self.mainmodule = mmodule
633 for cd in mmodule.mclassdefs do
634 for pd in cd.mpropdefs do
635 if not pd isa MMethodDef then continue
636 if pd.mproperty.is_broken or pd.is_broken or pd.msignature == null then continue # Skip broken method
637 var rta = runtime_type_analysis
638 if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
639 #print "compile {pd} @ {cd} @ {mmodule}"
640 var r = pd.separate_runtime_function
641 r.compile_to_c(self)
642 var r2 = pd.virtual_runtime_function
643 if r2 != r then r2.compile_to_c(self)
644
645 # Generate trampolines
646 if modelbuilder.toolcontext.opt_trampoline_call.value then
647 r2.compile_trampolines(self)
648 end
649 end
650 end
651 var compiled_thunks = new Array[SeparateRuntimeFunction]
652 # Compile thunks here to write them in the same module they are declared.
653 for thunk in thunks_to_compile do
654 if thunk.mmethoddef.mclassdef.mmodule == mmodule then
655 thunk.compile_to_c(self)
656 compiled_thunks.add(thunk)
657 end
658 end
659 thunks_to_compile.remove_all(compiled_thunks)
660 self.mainmodule = old_module
661 end
662
663 # Process all introduced methods and compile some linking information (if needed)
664 fun link_mmethods
665 do
666 if not modelbuilder.toolcontext.opt_substitute_monomorph.value and not modelbuilder.toolcontext.opt_guard_call.value then return
667
668 for mmodule in mainmodule.in_importation.greaters do
669 for cd in mmodule.mclassdefs do
670 for m in cd.intro_mproperties do
671 if not m isa MMethod then continue
672 link_mmethod(m)
673 end
674 end
675 end
676 end
677
678 # Compile some linking information (if needed)
679 fun link_mmethod(m: MMethod)
680 do
681 var n2 = "CALL_" + m.const_color
682
683 # Replace monomorphic call by a direct call to the virtual implementation
684 var md = is_monomorphic(m)
685 if md != null then
686 linker_script.add("{n2} = {md.virtual_runtime_function.c_name};")
687 end
688
689 # If opt_substitute_monomorph then a trampoline is used, else a weak symbol is used
690 if modelbuilder.toolcontext.opt_guard_call.value then
691 var r = m.intro.virtual_runtime_function
692 provide_declaration(n2, "{r.c_ret} {n2}{r.c_sig} __attribute__((weak));")
693 end
694 end
695
696 # The single mmethodef called in case of monomorphism.
697 # Returns nul if dead or polymorphic.
698 fun is_monomorphic(m: MMethod): nullable MMethodDef
699 do
700 var rta = runtime_type_analysis
701 if rta == null then
702 # Without RTA, monomorphic means alone (uniq name)
703 if m.mpropdefs.length == 1 then
704 return m.mpropdefs.first
705 else
706 return null
707 end
708 else
709 # With RTA, monomorphic means only live methoddef
710 var res: nullable MMethodDef = null
711 for md in m.mpropdefs do
712 if rta.live_methoddefs.has(md) then
713 if res != null then return null
714 res = md
715 end
716 end
717 return res
718 end
719 end
720
721 # Globaly compile the type structure of a live type
722 fun compile_type_to_c(mtype: MType)
723 do
724 assert not mtype.need_anchor
725 var is_live = mtype isa MClassType and runtime_type_analysis.live_types.has(mtype)
726 var is_cast_live = runtime_type_analysis.live_cast_types.has(mtype)
727 var c_name = mtype.c_name
728 var v = new SeparateCompilerVisitor(self)
729 v.add_decl("/* runtime type {mtype} */")
730
731 # extern const struct type_X
732 self.provide_declaration("type_{c_name}", "extern const struct type type_{c_name};")
733
734 # const struct type_X
735 v.add_decl("const struct type type_{c_name} = \{")
736
737 # type id (for cast target)
738 if is_cast_live then
739 v.add_decl("{type_ids[mtype]},")
740 else
741 v.add_decl("-1, /*CAST DEAD*/")
742 end
743
744 # type name
745 v.add_decl("\"{mtype}\", /* class_name_string */")
746
747 # type color (for cast target)
748 if is_cast_live then
749 v.add_decl("{type_colors[mtype]},")
750 else
751 v.add_decl("-1, /*CAST DEAD*/")
752 end
753
754 # is_nullable bit
755 if mtype isa MNullableType then
756 v.add_decl("1,")
757 else
758 v.add_decl("0,")
759 end
760
761 # resolution table (for receiver)
762 if is_live then
763 var mclass_type = mtype.undecorate
764 assert mclass_type isa MClassType
765 if resolution_tables[mclass_type].is_empty then
766 v.add_decl("NULL, /*NO RESOLUTIONS*/")
767 else
768 compile_type_resolution_table(mtype)
769 v.require_declaration("resolution_table_{c_name}")
770 v.add_decl("&resolution_table_{c_name},")
771 end
772 else
773 v.add_decl("NULL, /*DEAD*/")
774 end
775
776 # cast table (for receiver)
777 if is_live then
778 v.add_decl("{self.type_tables[mtype].length},")
779 v.add_decl("\{")
780 for stype in self.type_tables[mtype] do
781 if stype == null then
782 v.add_decl("-1, /* empty */")
783 else
784 v.add_decl("{type_ids[stype]}, /* {stype} */")
785 end
786 end
787 v.add_decl("\},")
788 else
789 # Use -1 to indicate dead type, the info is used by --hardening
790 v.add_decl("-1, \{\}, /*DEAD TYPE*/")
791 end
792 v.add_decl("\};")
793 end
794
795 fun compile_type_resolution_table(mtype: MType) do
796
797 var mclass_type = mtype.undecorate.as(MClassType)
798
799 # extern const struct resolution_table_X resolution_table_X
800 self.provide_declaration("resolution_table_{mtype.c_name}", "extern const struct types resolution_table_{mtype.c_name};")
801
802 # const struct fts_table_X fts_table_X
803 var v = new_visitor
804 v.add_decl("const struct types resolution_table_{mtype.c_name} = \{")
805 v.add_decl("0, /* dummy */")
806 v.add_decl("\{")
807 for t in self.resolution_tables[mclass_type] do
808 if t == null then
809 v.add_decl("NULL, /* empty */")
810 else
811 # The table stores the result of the type resolution
812 # Therefore, for a receiver `mclass_type`, and a unresolved type `t`
813 # the value stored is tv.
814 var tv = t.resolve_for(mclass_type, mclass_type, self.mainmodule, true)
815 # FIXME: What typeids means here? How can a tv not be live?
816 if type_ids.has_key(tv) then
817 v.require_declaration("type_{tv.c_name}")
818 v.add_decl("&type_{tv.c_name}, /* {t}: {tv} */")
819 else
820 v.add_decl("NULL, /* empty ({t}: {tv} not a live type) */")
821 end
822 end
823 end
824 v.add_decl("\}")
825 v.add_decl("\};")
826 end
827
828 # Globally compile the table of the class mclass
829 # In a link-time optimisation compiler, tables are globally computed
830 # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
831 fun compile_class_to_c(mclass: MClass)
832 do
833 if mclass.is_broken then return
834
835 var mtype = mclass.intro.bound_mtype
836 var c_name = mclass.c_name
837
838 var v = new_visitor
839
840 var rta = runtime_type_analysis
841 var is_dead = rta != null and not rta.live_classes.has(mclass)
842 # While the class may be dead, some part of separately compiled code may use symbols associated to the class, so
843 # in order to compile and link correctly the C code, these symbols should be declared and defined.
844 var need_corpse = is_dead and mtype.is_c_primitive or mclass.kind == extern_kind or mclass.kind == enum_kind
845
846 v.add_decl("/* runtime class {c_name}: {mclass.full_name} (dead={is_dead}; need_corpse={need_corpse})*/")
847
848 # Build class vft
849 if not is_dead or need_corpse then
850 self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
851 v.add_decl("const struct class class_{c_name} = \{")
852 v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
853 v.add_decl("\{")
854 var vft = self.method_tables.get_or_null(mclass)
855 if vft != null then for i in [0 .. vft.length[ do
856 var mpropdef = vft[i]
857 if mpropdef == null then
858 v.add_decl("NULL, /* empty */")
859 else
860 assert mpropdef isa MMethodDef
861 if rta != null and not rta.live_methoddefs.has(mpropdef) then
862 v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
863 continue
864 else if mpropdef.is_broken or mpropdef.msignature == null or mpropdef.mproperty.is_broken then
865 v.add_decl("NULL, /* DEAD (BROKEN) {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
866 continue
867 end
868 var rf = mpropdef.virtual_runtime_function
869 v.require_declaration(rf.c_name)
870 v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
871 end
872 end
873 v.add_decl("\}")
874 v.add_decl("\};")
875 end
876
877 if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
878 # Is a primitive type or the Pointer class, not any other extern class
879
880 if mtype.is_tagged then return
881
882 #Build instance struct
883 self.header.add_decl("struct instance_{c_name} \{")
884 self.header.add_decl("const struct type *type;")
885 self.header.add_decl("const struct class *class;")
886 self.header.add_decl("{mtype.ctype_extern} value;")
887 self.header.add_decl("\};")
888
889 # Pointer is needed by extern types, live or not
890 if is_dead and mtype.mclass.name != "Pointer" then return
891
892 #Build BOX
893 self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
894 v.add_decl("/* allocate {mtype} */")
895 v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
896 var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
897 v.add("struct instance_{c_name}*res = {alloc};")
898 v.compiler.undead_types.add(mtype)
899 v.require_declaration("type_{c_name}")
900 v.add("res->type = &type_{c_name};")
901 v.require_declaration("class_{c_name}")
902 v.add("res->class = &class_{c_name};")
903 v.add("res->value = value;")
904 v.add("return (val*)res;")
905 v.add("\}")
906
907 # A Pointer class also need its constructor
908 if mtype.mclass.name != "Pointer" then return
909
910 v = new_visitor
911 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
912 v.add_decl("/* allocate {mtype} */")
913 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
914 if is_dead then
915 v.add_abort("{mclass} is DEAD")
916 else
917 var res = v.new_named_var(mtype, "self")
918 res.is_exact = true
919 alloc = v.nit_alloc("sizeof(struct instance_{mtype.c_name})", mclass.full_name)
920 v.add("{res} = {alloc};")
921 v.add("{res}->type = type;")
922 hardening_live_type(v, "type")
923 v.require_declaration("class_{c_name}")
924 v.add("{res}->class = &class_{c_name};")
925 v.add("((struct instance_{mtype.c_name}*){res})->value = NULL;")
926 v.add("return {res};")
927 end
928 v.add("\}")
929 return
930 else if mclass.name == "NativeArray" then
931 #Build instance struct
932 self.header.add_decl("struct instance_{c_name} \{")
933 self.header.add_decl("const struct type *type;")
934 self.header.add_decl("const struct class *class;")
935 # NativeArrays are just a instance header followed by a length and an array of values
936 self.header.add_decl("int length;")
937 self.header.add_decl("val* values[0];")
938 self.header.add_decl("\};")
939
940 #Build NEW
941 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
942 v.add_decl("/* allocate {mtype} */")
943 v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
944 var res = v.get_name("self")
945 v.add_decl("struct instance_{c_name} *{res};")
946 var mtype_elt = mtype.arguments.first
947 var alloc = v.nit_alloc("sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype})", mclass.full_name)
948 v.add("{res} = {alloc};")
949 v.add("{res}->type = type;")
950 hardening_live_type(v, "type")
951 v.require_declaration("class_{c_name}")
952 v.add("{res}->class = &class_{c_name};")
953 v.add("{res}->length = length;")
954 v.add("return (val*){res};")
955 v.add("\}")
956 return
957 else if mclass.name == "RoutineRef" then
958 self.header.add_decl("struct instance_{c_name} \{")
959 self.header.add_decl("const struct type *type;")
960 self.header.add_decl("const struct class *class;")
961 self.header.add_decl("val* recv;")
962 self.header.add_decl("nitmethod_t method;")
963 self.header.add_decl("\};")
964
965 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class, const struct type* type);")
966 v.add_decl("/* allocate {mtype} */")
967 v.add_decl("{mtype.ctype} NEW_{c_name}(val* recv, nitmethod_t method, const struct class* class, const struct type* type)\{")
968 var res = v.get_name("self")
969 v.add_decl("struct instance_{c_name} *{res};")
970 var alloc = v.nit_alloc("sizeof(struct instance_{c_name})", mclass.full_name)
971 v.add("{res} = {alloc};")
972 v.add("{res}->type = type;")
973 hardening_live_type(v, "type")
974 v.add("{res}->class = class;")
975 v.add("{res}->recv = recv;")
976 v.add("{res}->method = method;")
977 v.add("return (val*){res};")
978 v.add("\}")
979 return
980 else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
981 # Is an extern class (other than Pointer and CString)
982 # Pointer is caught in a previous `if`, and CString is internal
983
984 var pointer_type = mainmodule.pointer_type
985
986 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
987 v.add_decl("/* allocate extern {mtype} */")
988 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
989 if is_dead then
990 v.add_abort("{mclass} is DEAD")
991 else
992 var res = v.new_named_var(mtype, "self")
993 res.is_exact = true
994 var alloc = v.nit_alloc("sizeof(struct instance_{pointer_type.c_name})", mclass.full_name)
995 v.add("{res} = {alloc};")
996 v.add("{res}->type = type;")
997 hardening_live_type(v, "type")
998 v.require_declaration("class_{c_name}")
999 v.add("{res}->class = &class_{c_name};")
1000 v.add("((struct instance_{pointer_type.c_name}*){res})->value = NULL;")
1001 v.add("return {res};")
1002 end
1003 v.add("\}")
1004 return
1005 end
1006
1007 #Build NEW
1008 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
1009 v.add_decl("/* allocate {mtype} */")
1010 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
1011 if is_dead then
1012 v.add_abort("{mclass} is DEAD")
1013 else
1014 var res = v.new_named_var(mtype, "self")
1015 res.is_exact = true
1016 var attrs = self.attr_tables.get_or_null(mclass)
1017 if attrs == null then
1018 var alloc = v.nit_alloc("sizeof(struct instance)", mclass.full_name)
1019 v.add("{res} = {alloc};")
1020 else
1021 var alloc = v.nit_alloc("sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t)", mclass.full_name)
1022 v.add("{res} = {alloc};")
1023 end
1024 if modelbuilder.toolcontext.opt_trace.value then
1025 v.add("tracepoint(Nit_Compiler, Object_Instance,\"{mtype}\", (uintptr_t)self);")
1026 v.add("GC_register_finalizer(self, object_destroy_callback, NULL, NULL, NULL);")
1027 end
1028 v.add("{res}->type = type;")
1029 hardening_live_type(v, "type")
1030 v.require_declaration("class_{c_name}")
1031 v.add("{res}->class = &class_{c_name};")
1032 if attrs != null then
1033 self.generate_init_attr(v, res, mtype)
1034 v.set_finalizer res
1035 end
1036 v.add("return {res};")
1037 end
1038 v.add("\}")
1039 end
1040
1041 # Compile structures used to map tagged primitive values to their classes and types.
1042 # This method also determines which class will be tagged.
1043 fun compile_class_infos
1044 do
1045 if modelbuilder.toolcontext.opt_no_tag_primitives.value then return
1046
1047 # Note: if you change the tagging scheme, do not forget to update
1048 # `autobox` and `extract_tag`
1049 var class_info = new Array[nullable MClass].filled_with(null, 4)
1050 for t in box_kinds.keys do
1051 # Note: a same class can be associated to multiple slots if one want to
1052 # use some Huffman coding.
1053 if t.name == "Int" then
1054 class_info[1] = t
1055 t.mclass_type.tag_value = 1
1056 else if t.name == "Char" then
1057 class_info[2] = t
1058 t.mclass_type.tag_value = 2
1059 else if t.name == "Bool" then
1060 class_info[3] = t
1061 t.mclass_type.tag_value = 3
1062 else
1063 continue
1064 end
1065 t.mclass_type.is_tagged = true
1066 end
1067
1068 # Compile the table for classes. The tag is used as an index
1069 var v = self.new_visitor
1070 v.add_decl "const struct class *class_info[4] = \{"
1071 for t in class_info do
1072 if t == null then
1073 v.add_decl("NULL,")
1074 else
1075 var s = "class_{t.c_name}"
1076 v.require_declaration(s)
1077 v.add_decl("&{s},")
1078 end
1079 end
1080 v.add_decl("\};")
1081
1082 # Compile the table for types. The tag is used as an index
1083 v.add_decl "const struct type *type_info[4] = \{"
1084 for t in class_info do
1085 if t == null then
1086 v.add_decl("NULL,")
1087 else
1088 var s = "type_{t.c_name}"
1089 undead_types.add(t.mclass_type)
1090 v.require_declaration(s)
1091 v.add_decl("&{s},")
1092 end
1093 end
1094 v.add_decl("\};")
1095 end
1096
1097 # Add a dynamic test to ensure that the type referenced by `t` is a live type
1098 fun hardening_live_type(v: VISITOR, t: String)
1099 do
1100 if not v.compiler.modelbuilder.toolcontext.opt_hardening.value then return
1101 v.add("if({t} == NULL) \{")
1102 v.add_abort("type null")
1103 v.add("\}")
1104 v.add("if({t}->table_size < 0) \{")
1105 v.add("PRINT_ERROR(\"Instantiation of a dead type: %s\\n\", {t}->name);")
1106 v.add_abort("type dead")
1107 v.add("\}")
1108 end
1109
1110 redef fun new_visitor do return new SeparateCompilerVisitor(self)
1111
1112 # Stats
1113
1114 private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
1115 private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
1116 protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
1117 protected var attr_tables: Map[MClass, Array[nullable MProperty]] = new HashMap[MClass, Array[nullable MProperty]]
1118
1119 redef fun display_stats
1120 do
1121 super
1122 if self.modelbuilder.toolcontext.opt_tables_metrics.value then
1123 display_sizes
1124 end
1125 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1126 display_isset_checks
1127 end
1128 var tc = self.modelbuilder.toolcontext
1129 tc.info("# implementation of method invocation",2)
1130 var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
1131 tc.info("total number of invocations: {nb_invok_total}",2)
1132 tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
1133 tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
1134 tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
1135 end
1136
1137 fun display_sizes
1138 do
1139 print "# size of subtyping tables"
1140 print "\ttotal \tholes"
1141 var total = 0
1142 var holes = 0
1143 for t, table in type_tables do
1144 total += table.length
1145 for e in table do if e == null then holes += 1
1146 end
1147 print "\t{total}\t{holes}"
1148
1149 print "# size of resolution tables"
1150 print "\ttotal \tholes"
1151 total = 0
1152 holes = 0
1153 for t, table in resolution_tables do
1154 total += table.length
1155 for e in table do if e == null then holes += 1
1156 end
1157 print "\t{total}\t{holes}"
1158
1159 print "# size of methods tables"
1160 print "\ttotal \tholes"
1161 total = 0
1162 holes = 0
1163 for t, table in method_tables do
1164 total += table.length
1165 for e in table do if e == null then holes += 1
1166 end
1167 print "\t{total}\t{holes}"
1168
1169 print "# size of attributes tables"
1170 print "\ttotal \tholes"
1171 total = 0
1172 holes = 0
1173 for t, table in attr_tables do
1174 total += table.length
1175 for e in table do if e == null then holes += 1
1176 end
1177 print "\t{total}\t{holes}"
1178 end
1179
1180 protected var isset_checks_count = 0
1181 protected var attr_read_count = 0
1182
1183 fun display_isset_checks do
1184 print "# total number of compiled attribute reads"
1185 print "\t{attr_read_count}"
1186 print "# total number of compiled isset-checks"
1187 print "\t{isset_checks_count}"
1188 end
1189
1190 redef fun compile_nitni_structs
1191 do
1192 self.header.add_decl """
1193 struct nitni_instance \{
1194 struct nitni_instance *next,
1195 *prev; /* adjacent global references in global list */
1196 int count; /* number of time this global reference has been marked */
1197 struct instance *value;
1198 \};
1199 """
1200 super
1201 end
1202
1203 redef fun finalize_ffi_for_module(mmodule)
1204 do
1205 var old_module = self.mainmodule
1206 self.mainmodule = mmodule
1207 super
1208 self.mainmodule = old_module
1209 end
1210 end
1211
1212 # A visitor on the AST of property definition that generate the C code of a separate compilation process.
1213 class SeparateCompilerVisitor
1214 super AbstractCompilerVisitor
1215
1216 redef type COMPILER: SeparateCompiler
1217
1218 redef fun adapt_signature(m, args)
1219 do
1220 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1221 var recv = args.first
1222 if recv.mtype.ctype != m.mclassdef.mclass.mclass_type.ctype then
1223 args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
1224 end
1225 for i in [0..msignature.arity[ do
1226 var mp = msignature.mparameters[i]
1227 var t = mp.mtype
1228 if mp.is_vararg then
1229 t = args[i+1].mtype
1230 end
1231 args[i+1] = self.autobox(args[i+1], t)
1232 end
1233 end
1234
1235 redef fun unbox_signature_extern(m, args)
1236 do
1237 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1238 if not m.mproperty.is_init and m.is_extern then
1239 args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
1240 end
1241 for i in [0..msignature.arity[ do
1242 var mp = msignature.mparameters[i]
1243 var t = mp.mtype
1244 if mp.is_vararg then
1245 t = args[i+1].mtype
1246 end
1247 if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
1248 end
1249 end
1250
1251 redef fun autobox(value, mtype)
1252 do
1253 if value.mtype == mtype then
1254 return value
1255 else if not value.mtype.is_c_primitive and not mtype.is_c_primitive then
1256 return value
1257 else if not value.mtype.is_c_primitive then
1258 if mtype.is_tagged then
1259 if mtype.name == "Int" then
1260 return self.new_expr("(long)({value})>>2", mtype)
1261 else if mtype.name == "Char" then
1262 return self.new_expr("(uint32_t)((long)({value})>>2)", mtype)
1263 else if mtype.name == "Bool" then
1264 return self.new_expr("(short int)((long)({value})>>2)", mtype)
1265 else
1266 abort
1267 end
1268 end
1269 return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
1270 else if not mtype.is_c_primitive then
1271 assert value.mtype == value.mcasttype
1272 if value.mtype.is_tagged then
1273 var res
1274 if value.mtype.name == "Int" then
1275 res = self.new_expr("(val*)({value}<<2|1)", mtype)
1276 else if value.mtype.name == "Char" then
1277 res = self.new_expr("(val*)((long)({value})<<2|2)", mtype)
1278 else if value.mtype.name == "Bool" then
1279 res = self.new_expr("(val*)((long)({value})<<2|3)", mtype)
1280 else
1281 abort
1282 end
1283 # Do not loose type info
1284 res.mcasttype = value.mcasttype
1285 return res
1286 end
1287 var valtype = value.mtype.as(MClassType)
1288 if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
1289 valtype = compiler.mainmodule.pointer_type
1290 end
1291 var res = self.new_var(mtype)
1292 # Do not loose type info
1293 res.mcasttype = value.mcasttype
1294 self.require_declaration("BOX_{valtype.c_name}")
1295 self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
1296 return res
1297 else if (value.mtype.ctype == "void*" and mtype.ctype == "void*") or
1298 (value.mtype.ctype == "char*" and mtype.ctype == "void*") or
1299 (value.mtype.ctype == "void*" and mtype.ctype == "char*") then
1300 return value
1301 else
1302 # Bad things will appen!
1303 var res = self.new_var(mtype)
1304 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1305 self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); fatal_exit(1);")
1306 return res
1307 end
1308 end
1309
1310 redef fun unbox_extern(value, mtype)
1311 do
1312 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1313 mtype.mclass.name != "CString" then
1314 var pointer_type = compiler.mainmodule.pointer_type
1315 var res = self.new_var_extern(mtype)
1316 self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
1317 return res
1318 else
1319 return value
1320 end
1321 end
1322
1323 redef fun box_extern(value, mtype)
1324 do
1325 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1326 mtype.mclass.name != "CString" then
1327 var valtype = compiler.mainmodule.pointer_type
1328 var res = self.new_var(mtype)
1329 compiler.undead_types.add(mtype)
1330 self.require_declaration("BOX_{valtype.c_name}")
1331 self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
1332 self.require_declaration("type_{mtype.c_name}")
1333 self.add("{res}->type = &type_{mtype.c_name};")
1334 self.require_declaration("class_{mtype.c_name}")
1335 self.add("{res}->class = &class_{mtype.c_name};")
1336 return res
1337 else
1338 return value
1339 end
1340 end
1341
1342 # Returns a C expression containing the tag of the value as a long.
1343 #
1344 # If the C expression is evaluated to 0, it means there is no tag.
1345 # Thus the expression can be used as a condition.
1346 fun extract_tag(value: RuntimeVariable): String
1347 do
1348 assert not value.mtype.is_c_primitive
1349 return "((long){value}&3)" # Get the two low bits
1350 end
1351
1352 # Returns a C expression of the runtime class structure of the value.
1353 # The point of the method is to work also with primitive types.
1354 fun class_info(value: RuntimeVariable): String
1355 do
1356 if not value.mtype.is_c_primitive then
1357 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1358 var tag = extract_tag(value)
1359 return "({tag}?class_info[{tag}]:{value}->class)"
1360 end
1361 return "{value}->class"
1362 else
1363 compiler.undead_types.add(value.mtype)
1364 self.require_declaration("class_{value.mtype.c_name}")
1365 return "(&class_{value.mtype.c_name})"
1366 end
1367 end
1368
1369 # Returns a C expression of the runtime type structure of the value.
1370 # The point of the method is to work also with primitive types.
1371 fun type_info(value: RuntimeVariable): String
1372 do
1373 if not value.mtype.is_c_primitive then
1374 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1375 var tag = extract_tag(value)
1376 return "({tag}?type_info[{tag}]:{value}->type)"
1377 end
1378 return "{value}->type"
1379 else
1380 compiler.undead_types.add(value.mtype)
1381 self.require_declaration("type_{value.mtype.c_name}")
1382 return "(&type_{value.mtype.c_name})"
1383 end
1384 end
1385
1386 redef fun compile_callsite(callsite, args)
1387 do
1388 var rta = compiler.runtime_type_analysis
1389 # TODO: Inlining of new-style constructors with initializers
1390 if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
1391 var tgs = rta.live_targets(callsite)
1392 if tgs.length == 1 then
1393 return direct_call(tgs.first, args)
1394 end
1395 end
1396 # Shortcut intern methods as they are not usually redefinable
1397 if callsite.mpropdef.is_intern and callsite.mproperty.name != "object_id" then
1398 # `object_id` is the only redefined intern method, so it can not be directly called.
1399 # TODO find a less ugly approach?
1400 return direct_call(callsite.mpropdef, args)
1401 end
1402 return super
1403 end
1404
1405 # Fully and directly call a mpropdef
1406 #
1407 # This method is used by `compile_callsite`
1408 private fun direct_call(mpropdef: MMethodDef, args: Array[RuntimeVariable]): nullable RuntimeVariable
1409 do
1410 var res0 = before_send(mpropdef.mproperty, args)
1411 var res = call(mpropdef, mpropdef.mclassdef.bound_mtype, args)
1412 if res0 != null then
1413 assert res != null
1414 self.assign(res0, res)
1415 res = res0
1416 end
1417 add("\}") # close the before_send
1418 return res
1419 end
1420 redef fun send(mmethod, arguments)
1421 do
1422 if arguments.first.mcasttype.is_c_primitive then
1423 # In order to shortcut the primitive, we need to find the most specific method
1424 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1425 var m = self.compiler.mainmodule
1426 self.compiler.mainmodule = self.compiler.realmainmodule
1427 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1428 self.compiler.mainmodule = m
1429 return res
1430 end
1431
1432 return table_send(mmethod, arguments, mmethod)
1433 end
1434
1435 # Handle common special cases before doing the effective method invocation
1436 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1437 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1438 # Client must not forget to close the } after them.
1439 #
1440 # The value returned is the result of the common special cases.
1441 # If not null, client must compile it with the result of their own effective method invocation.
1442 #
1443 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1444 # is generated to cancel the effective method invocation that will follow
1445 # TODO: find a better approach
1446 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1447 do
1448 var res: nullable RuntimeVariable = null
1449 var recv = arguments.first
1450 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
1451 if maybe_null(recv) and consider_null then
1452 self.add("if ({recv} == NULL) \{")
1453 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
1454 res = self.new_var(bool_type)
1455 var arg = arguments[1]
1456 if arg.mcasttype isa MNullableType then
1457 self.add("{res} = ({arg} == NULL);")
1458 else if arg.mcasttype isa MNullType then
1459 self.add("{res} = 1; /* is null */")
1460 else
1461 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1462 end
1463 else if mmethod.name == "!=" then
1464 res = self.new_var(bool_type)
1465 var arg = arguments[1]
1466 if arg.mcasttype isa MNullableType then
1467 self.add("{res} = ({arg} != NULL);")
1468 else if arg.mcasttype isa MNullType then
1469 self.add("{res} = 0; /* is null */")
1470 else
1471 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1472 end
1473 else
1474 self.add_abort("Receiver is null")
1475 end
1476 self.add("\} else \{")
1477 else
1478 self.add("\{")
1479 end
1480 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
1481 # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
1482 var arg = arguments[1]
1483 if arg.mcasttype isa MNullType then
1484 if res == null then res = self.new_var(bool_type)
1485 if mmethod.name == "!=" then
1486 self.add("{res} = 1; /* arg is null and recv is not */")
1487 else # `==` and `is_same_instance`
1488 self.add("{res} = 0; /* arg is null but recv is not */")
1489 end
1490 self.add("\}") # closes the null case
1491 self.add("if (0) \{") # what follow is useless, CC will drop it
1492 end
1493 end
1494 return res
1495 end
1496
1497 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
1498 do
1499 compiler.modelbuilder.nb_invok_by_tables += 1
1500 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1501
1502 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1503
1504 var res0 = before_send(mmethod, arguments)
1505
1506 var runtime_function = mmethod.intro.virtual_runtime_function
1507 var msignature = runtime_function.called_signature
1508
1509 adapt_signature(mmethod.intro, arguments)
1510
1511 var res: nullable RuntimeVariable
1512 var ret = msignature.return_mtype
1513 if ret == null then
1514 res = null
1515 else
1516 res = self.new_var(ret)
1517 end
1518
1519 var ss = arguments.join(", ")
1520
1521 var const_color = mentity.const_color
1522 var ress
1523 if res != null then
1524 ress = "{res} = "
1525 else
1526 ress = ""
1527 end
1528 if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_direct_call_monomorph0.value then
1529 # opt_direct_call_monomorph0 is used to compare the efficiency of the alternative lookup implementation, ceteris paribus.
1530 # The difference with the non-zero option is that the monomorphism is looked-at on the mmethod level and not at the callsite level.
1531 # TODO: remove this mess and use per callsite service to detect monomorphism in a single place.
1532 var md = compiler.is_monomorphic(mentity)
1533 if md != null then
1534 var callsym = md.virtual_runtime_function.c_name
1535 self.require_declaration(callsym)
1536 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1537 else
1538 self.require_declaration(const_color)
1539 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1540 end
1541 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_guard_call.value then
1542 var callsym = "CALL_" + const_color
1543 self.require_declaration(callsym)
1544 self.add "if (!{callsym}) \{"
1545 self.require_declaration(const_color)
1546 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1547 self.add "\} else \{"
1548 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1549 self.add "\}"
1550 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
1551 var callsym = "CALL_" + const_color
1552 self.require_declaration(callsym)
1553 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1554 else
1555 self.require_declaration(const_color)
1556 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1557 end
1558
1559 if res0 != null then
1560 assert res != null
1561 assign(res0,res)
1562 res = res0
1563 end
1564
1565 self.add("\}") # closes the null case
1566
1567 return res
1568 end
1569
1570 redef fun call(mmethoddef, recvtype, arguments)
1571 do
1572 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1573
1574 var res: nullable RuntimeVariable
1575 var ret = mmethoddef.msignature.return_mtype
1576 if ret == null then
1577 res = null
1578 else
1579 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1580 res = self.new_var(ret)
1581 end
1582
1583 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1584 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1585 compiler.modelbuilder.nb_invok_by_inline += 1
1586 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1587 var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
1588 frame.returnlabel = self.get_name("RET_LABEL")
1589 frame.returnvar = res
1590 var old_frame = self.frame
1591 self.frame = frame
1592 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1593 mmethoddef.compile_inside_to_c(self, arguments)
1594 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1595 self.add("\}")
1596 self.frame = old_frame
1597 return res
1598 end
1599 compiler.modelbuilder.nb_invok_by_direct += 1
1600 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1601
1602 # Autobox arguments
1603 self.adapt_signature(mmethoddef, arguments)
1604
1605 self.require_declaration(mmethoddef.c_name)
1606 if res == null then
1607 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1608 return null
1609 else
1610 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1611 end
1612
1613 return res
1614 end
1615
1616 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1617 do
1618 if arguments.first.mcasttype.is_c_primitive then
1619 # In order to shortcut the primitive, we need to find the most specific method
1620 # However, because of performance (no flattening), we always work on the realmainmodule
1621 var main = self.compiler.mainmodule
1622 self.compiler.mainmodule = self.compiler.realmainmodule
1623 var res = self.monomorphic_super_send(m, recvtype, arguments)
1624 self.compiler.mainmodule = main
1625 return res
1626 end
1627 return table_send(m.mproperty, arguments, m)
1628 end
1629
1630 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1631 do
1632 # A vararg must be stored into an new array
1633 # The trick is that the dymaic type of the array may depends on the receiver
1634 # of the method (ie recv) if the static type is unresolved
1635 # This is more complex than usual because the unresolved type must not be resolved
1636 # with the current receiver (ie self).
1637 # Therefore to isolate the resolution from self, a local StaticFrame is created.
1638 # One can see this implementation as an inlined method of the receiver whose only
1639 # job is to allocate the array
1640 var old_frame = self.frame
1641 var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1642 self.frame = frame
1643 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1644 var res = self.array_instance(varargs, elttype)
1645 self.frame = old_frame
1646 return res
1647 end
1648
1649 redef fun isset_attribute(a, recv)
1650 do
1651 self.check_recv_notnull(recv)
1652 var res = self.new_var(bool_type)
1653
1654 # What is the declared type of the attribute?
1655 var mtype = a.intro.static_mtype.as(not null)
1656 var intromclassdef = a.intro.mclassdef
1657 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1658
1659 if mtype isa MNullableType then
1660 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1661 return res
1662 end
1663
1664 self.require_declaration(a.const_color)
1665 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1666 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1667 else
1668
1669 if not mtype.is_c_primitive and not mtype.is_tagged then
1670 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1671 else
1672 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1673 end
1674 end
1675 return res
1676 end
1677
1678 redef fun read_attribute(a, recv)
1679 do
1680 self.check_recv_notnull(recv)
1681
1682 # What is the declared type of the attribute?
1683 var ret = a.intro.static_mtype.as(not null)
1684 var intromclassdef = a.intro.mclassdef
1685 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1686
1687 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1688 self.compiler.attr_read_count += 1
1689 self.add("count_attr_reads++;")
1690 end
1691
1692 self.require_declaration(a.const_color)
1693 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1694 # Get the attribute or a box (ie. always a val*)
1695 var cret = self.object_type.as_nullable
1696 var res = self.new_var(cret)
1697 res.mcasttype = ret
1698
1699 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1700
1701 # Check for Uninitialized attribute
1702 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1703 self.add("if (unlikely({res} == NULL)) \{")
1704 self.add_abort("Uninitialized attribute {a.name}")
1705 self.add("\}")
1706
1707 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1708 self.compiler.isset_checks_count += 1
1709 self.add("count_isset_checks++;")
1710 end
1711 end
1712
1713 # Return the attribute or its unboxed version
1714 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1715 return self.autobox(res, ret)
1716 else
1717 var res = self.new_var(ret)
1718 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1719
1720 # Check for Uninitialized attribute
1721 if not ret.is_c_primitive and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1722 self.add("if (unlikely({res} == NULL)) \{")
1723 self.add_abort("Uninitialized attribute {a.name}")
1724 self.add("\}")
1725 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1726 self.compiler.isset_checks_count += 1
1727 self.add("count_isset_checks++;")
1728 end
1729 end
1730
1731 return res
1732 end
1733 end
1734
1735 redef fun write_attribute(a, recv, value)
1736 do
1737 self.check_recv_notnull(recv)
1738
1739 # What is the declared type of the attribute?
1740 var mtype = a.intro.static_mtype.as(not null)
1741 var intromclassdef = a.intro.mclassdef
1742 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1743
1744 # Adapt the value to the declared type
1745 value = self.autobox(value, mtype)
1746
1747 self.require_declaration(a.const_color)
1748 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1749 var attr = "{recv}->attrs[{a.const_color}]"
1750 if mtype.is_tagged then
1751 # The attribute is not primitive, thus store it as tagged
1752 var tv = autobox(value, compiler.mainmodule.object_type)
1753 self.add("{attr} = {tv}; /* {a} on {recv.inspect} */")
1754 else if mtype.is_c_primitive then
1755 assert mtype isa MClassType
1756 # The attribute is primitive, thus we store it in a box
1757 # The trick is to create the box the first time then resuse the box
1758 self.add("if ({attr} != NULL) \{")
1759 self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1760 self.add("\} else \{")
1761 value = self.autobox(value, self.object_type.as_nullable)
1762 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1763 self.add("\}")
1764 else
1765 # The attribute is not primitive, thus store it direclty
1766 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1767 end
1768 else
1769 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1770 end
1771 end
1772
1773 # Check that mtype is a live open type
1774 fun hardening_live_open_type(mtype: MType)
1775 do
1776 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1777 self.require_declaration(mtype.const_color)
1778 var col = mtype.const_color
1779 self.add("if({col} == -1) \{")
1780 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1781 self.add_abort("open type dead")
1782 self.add("\}")
1783 end
1784
1785 # Check that mtype it a pointer to a live cast type
1786 fun hardening_cast_type(t: String)
1787 do
1788 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1789 add("if({t} == NULL) \{")
1790 add_abort("cast type null")
1791 add("\}")
1792 add("if({t}->id == -1 || {t}->color == -1) \{")
1793 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1794 add_abort("cast type dead")
1795 add("\}")
1796 end
1797
1798 redef fun init_instance(mtype)
1799 do
1800 self.require_declaration("NEW_{mtype.mclass.c_name}")
1801 var compiler = self.compiler
1802 if mtype isa MGenericType and mtype.need_anchor then
1803 hardening_live_open_type(mtype)
1804 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1805 var recv = self.frame.arguments.first
1806 var recv_type_info = self.type_info(recv)
1807 self.require_declaration(mtype.const_color)
1808 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1809 end
1810 compiler.undead_types.add(mtype)
1811 self.require_declaration("type_{mtype.c_name}")
1812 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1813 end
1814
1815 redef fun type_test(value, mtype, tag)
1816 do
1817 self.add("/* {value.inspect} isa {mtype} */")
1818 var compiler = self.compiler
1819
1820 var recv = self.frame.arguments.first
1821 var recv_type_info = self.type_info(recv)
1822
1823 var res = self.new_var(bool_type)
1824
1825 var cltype = self.get_name("cltype")
1826 self.add_decl("int {cltype};")
1827 var idtype = self.get_name("idtype")
1828 self.add_decl("int {idtype};")
1829
1830 var maybe_null = self.maybe_null(value)
1831 var accept_null = "0"
1832 var ntype = mtype
1833 if ntype isa MNullableType then
1834 ntype = ntype.mtype
1835 accept_null = "1"
1836 end
1837
1838 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1839 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1840 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1841 self.compiler.count_type_test_skipped[tag] += 1
1842 self.add("count_type_test_skipped_{tag}++;")
1843 end
1844 return res
1845 end
1846
1847 if ntype.need_anchor then
1848 var type_struct = self.get_name("type_struct")
1849 self.add_decl("const struct type* {type_struct};")
1850
1851 # Either with resolution_table with a direct resolution
1852 hardening_live_open_type(mtype)
1853 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1854 self.require_declaration(mtype.const_color)
1855 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1856 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1857 self.compiler.count_type_test_unresolved[tag] += 1
1858 self.add("count_type_test_unresolved_{tag}++;")
1859 end
1860 hardening_cast_type(type_struct)
1861 self.add("{cltype} = {type_struct}->color;")
1862 self.add("{idtype} = {type_struct}->id;")
1863 if maybe_null and accept_null == "0" then
1864 var is_nullable = self.get_name("is_nullable")
1865 self.add_decl("short int {is_nullable};")
1866 self.add("{is_nullable} = {type_struct}->is_nullable;")
1867 accept_null = is_nullable.to_s
1868 end
1869 else if ntype isa MClassType then
1870 compiler.undead_types.add(mtype)
1871 self.require_declaration("type_{mtype.c_name}")
1872 hardening_cast_type("(&type_{mtype.c_name})")
1873 self.add("{cltype} = type_{mtype.c_name}.color;")
1874 self.add("{idtype} = type_{mtype.c_name}.id;")
1875 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1876 self.compiler.count_type_test_resolved[tag] += 1
1877 self.add("count_type_test_resolved_{tag}++;")
1878 end
1879 else
1880 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); fatal_exit(1);")
1881 end
1882
1883 # check color is in table
1884 if maybe_null then
1885 self.add("if({value} == NULL) \{")
1886 self.add("{res} = {accept_null};")
1887 self.add("\} else \{")
1888 end
1889 var value_type_info = self.type_info(value)
1890 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1891 self.add("{res} = 0;")
1892 self.add("\} else \{")
1893 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1894 self.add("\}")
1895 if maybe_null then
1896 self.add("\}")
1897 end
1898
1899 return res
1900 end
1901
1902 redef fun is_same_type_test(value1, value2)
1903 do
1904 var res = self.new_var(bool_type)
1905 # Swap values to be symetric
1906 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1907 var tmp = value1
1908 value1 = value2
1909 value2 = tmp
1910 end
1911 if value1.mtype.is_c_primitive then
1912 if value2.mtype == value1.mtype then
1913 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1914 else if value2.mtype.is_c_primitive then
1915 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1916 else
1917 var mtype1 = value1.mtype.as(MClassType)
1918 self.require_declaration("class_{mtype1.c_name}")
1919 self.add("{res} = ({value2} != NULL) && ({class_info(value2)} == &class_{mtype1.c_name}); /* is_same_type_test */")
1920 end
1921 else
1922 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
1923 end
1924 return res
1925 end
1926
1927 redef fun class_name_string(value)
1928 do
1929 var res = self.get_name("var_class_name")
1930 self.add_decl("const char* {res};")
1931 if not value.mtype.is_c_primitive then
1932 self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
1933 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
1934 value.mtype.as(MClassType).name != "CString" then
1935 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1936 else
1937 self.require_declaration("type_{value.mtype.c_name}")
1938 self.add "{res} = type_{value.mtype.c_name}.name;"
1939 end
1940 return res
1941 end
1942
1943 redef fun equal_test(value1, value2)
1944 do
1945 var res = self.new_var(bool_type)
1946 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1947 var tmp = value1
1948 value1 = value2
1949 value2 = tmp
1950 end
1951 if value1.mtype.is_c_primitive then
1952 var t1 = value1.mtype
1953 assert t1 == value1.mcasttype
1954
1955 # Fast case: same C type.
1956 if value2.mtype == t1 then
1957 # Same exact C primitive representation.
1958 self.add("{res} = {value1} == {value2};")
1959 return res
1960 end
1961
1962 # Complex case: value2 has a different representation
1963 # Thus, it should be checked if `value2` is type-compatible with `value1`
1964 # This compatibility is done statically if possible and dynamically else
1965
1966 # Conjunction (ands) of dynamic tests according to the static knowledge
1967 var tests = new Array[String]
1968
1969 var t2 = value2.mcasttype
1970 if t2 isa MNullableType then
1971 # The destination type cannot be null
1972 tests.add("({value2} != NULL)")
1973 t2 = t2.mtype
1974 else if t2 isa MNullType then
1975 # `value2` is known to be null, thus incompatible with a primitive
1976 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
1977 return res
1978 end
1979
1980 if t2 == t1 then
1981 # Same type but different representation.
1982 else if t2.is_c_primitive then
1983 # Type of `value2` is a different primitive type, thus incompatible
1984 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
1985 return res
1986 else if t1.is_tagged then
1987 # To be equal, `value2` should also be correctly tagged
1988 tests.add("({extract_tag(value2)} == {t1.tag_value})")
1989 else
1990 # To be equal, `value2` should also be boxed with the same class
1991 self.require_declaration("class_{t1.c_name}")
1992 tests.add "({class_info(value2)} == &class_{t1.c_name})"
1993 end
1994
1995 # Compare the unboxed `value2` with `value1`
1996 if tests.not_empty then
1997 self.add "if ({tests.join(" && ")}) \{"
1998 end
1999 self.add "{res} = {self.autobox(value2, t1)} == {value1};"
2000 if tests.not_empty then
2001 self.add "\} else {res} = 0;"
2002 end
2003
2004 return res
2005 end
2006 var maybe_null = true
2007 var test = new Array[String]
2008 var t1 = value1.mcasttype
2009 if t1 isa MNullableType then
2010 test.add("{value1} != NULL")
2011 t1 = t1.mtype
2012 else
2013 maybe_null = false
2014 end
2015 var t2 = value2.mcasttype
2016 if t2 isa MNullableType then
2017 test.add("{value2} != NULL")
2018 t2 = t2.mtype
2019 else
2020 maybe_null = false
2021 end
2022
2023 var incompatible = false
2024 var primitive
2025 if t1.is_c_primitive then
2026 primitive = t1
2027 if t1 == t2 then
2028 # No need to compare class
2029 else if t2.is_c_primitive then
2030 incompatible = true
2031 else if can_be_primitive(value2) then
2032 if t1.is_tagged then
2033 self.add("{res} = {value1} == {value2};")
2034 return res
2035 end
2036 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
2037 test.add("(!{extract_tag(value2)})")
2038 end
2039 test.add("{value1}->class == {value2}->class")
2040 else
2041 incompatible = true
2042 end
2043 else if t2.is_c_primitive then
2044 primitive = t2
2045 if can_be_primitive(value1) then
2046 if t2.is_tagged then
2047 self.add("{res} = {value1} == {value2};")
2048 return res
2049 end
2050 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
2051 test.add("(!{extract_tag(value1)})")
2052 end
2053 test.add("{value1}->class == {value2}->class")
2054 else
2055 incompatible = true
2056 end
2057 else
2058 primitive = null
2059 end
2060
2061 if incompatible then
2062 if maybe_null then
2063 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
2064 return res
2065 else
2066 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
2067 return res
2068 end
2069 end
2070 if primitive != null then
2071 if primitive.is_tagged then
2072 self.add("{res} = {value1} == {value2};")
2073 return res
2074 end
2075 test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
2076 else if can_be_primitive(value1) and can_be_primitive(value2) then
2077 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
2078 test.add("(!{extract_tag(value1)}) && (!{extract_tag(value2)})")
2079 end
2080 test.add("{value1}->class == {value2}->class")
2081 var s = new Array[String]
2082 for t, v in self.compiler.box_kinds do
2083 if t.mclass_type.is_tagged then continue
2084 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
2085 end
2086 if s.is_empty then
2087 self.add("{res} = {value1} == {value2};")
2088 return res
2089 end
2090 test.add("({s.join(" || ")})")
2091 else
2092 self.add("{res} = {value1} == {value2};")
2093 return res
2094 end
2095 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
2096 return res
2097 end
2098
2099 fun can_be_primitive(value: RuntimeVariable): Bool
2100 do
2101 var t = value.mcasttype.undecorate
2102 if not t isa MClassType then return false
2103 var k = t.mclass.kind
2104 return k == interface_kind or t.is_c_primitive
2105 end
2106
2107 redef fun array_instance(array, elttype)
2108 do
2109 var nclass = mmodule.native_array_class
2110 var arrayclass = mmodule.array_class
2111 var arraytype = arrayclass.get_mtype([elttype])
2112 var res = self.init_instance(arraytype)
2113 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
2114 var length = self.int_instance(array.length)
2115 var nat = native_array_instance(elttype, length)
2116 for i in [0..array.length[ do
2117 var r = self.autobox(array[i], self.object_type)
2118 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
2119 end
2120 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
2121 self.add("\}")
2122 return res
2123 end
2124
2125 redef fun native_array_instance(elttype, length)
2126 do
2127 var mtype = mmodule.native_array_type(elttype)
2128 self.require_declaration("NEW_{mtype.mclass.c_name}")
2129 assert mtype isa MGenericType
2130 var compiler = self.compiler
2131 length = autobox(length, compiler.mainmodule.int_type)
2132 if mtype.need_anchor then
2133 hardening_live_open_type(mtype)
2134 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
2135 var recv = self.frame.arguments.first
2136 var recv_type_info = self.type_info(recv)
2137 self.require_declaration(mtype.const_color)
2138 return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
2139 end
2140 compiler.undead_types.add(mtype)
2141 self.require_declaration("type_{mtype.c_name}")
2142 return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, &type_{mtype.c_name})", mtype)
2143 end
2144
2145 redef fun native_array_def(pname, ret_type, arguments)
2146 do
2147 var elttype = arguments.first.mtype
2148 var nclass = mmodule.native_array_class
2149 var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
2150 if pname == "[]" then
2151 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2152 var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
2153 res.mcasttype = ret_type.as(not null)
2154 self.ret(res)
2155 return true
2156 else if pname == "[]=" then
2157 self.add("{recv}[{arguments[1]}]={arguments[2]};")
2158 return true
2159 else if pname == "length" then
2160 self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
2161 return true
2162 else if pname == "copy_to" then
2163 var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
2164 self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
2165 return true
2166 else if pname == "memmove" then
2167 # fun memmove(start: Int, length: Int, dest: NativeArray[E], dest_start: Int) is intern do
2168 var recv1 = "((struct instance_{nclass.c_name}*){arguments[3]})->values"
2169 self.add("memmove({recv1}+{arguments[4]}, {recv}+{arguments[1]}, {arguments[2]}*sizeof({elttype.ctype}));")
2170 return true
2171 end
2172 return false
2173 end
2174
2175 redef fun native_array_get(nat, i)
2176 do
2177 var nclass = mmodule.native_array_class
2178 var recv = "((struct instance_{nclass.c_name}*){nat})->values"
2179 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2180 var res = self.new_expr("{recv}[{i}]", compiler.mainmodule.object_type)
2181 return res
2182 end
2183
2184 redef fun native_array_set(nat, i, val)
2185 do
2186 var nclass = mmodule.native_array_class
2187 var recv = "((struct instance_{nclass.c_name}*){nat})->values"
2188 self.add("{recv}[{i}]={val};")
2189 end
2190
2191 redef fun routine_ref_instance(routine_type, recv, mmethoddef)
2192 do
2193 #debug "ENTER ref_instance"
2194 var mmethod = mmethoddef.mproperty
2195 # routine_mclass is the specialized one, e.g: FunRef1, ProcRef2, etc..
2196 var routine_mclass = routine_type.mclass
2197
2198 var nclasses = mmodule.model.get_mclasses_by_name("RoutineRef").as(not null)
2199 var base_routine_mclass = nclasses.first
2200
2201 # All routine classes use the same `NEW` constructor.
2202 # However, they have different declared `class` and `type` value.
2203 self.require_declaration("NEW_{base_routine_mclass.c_name}")
2204
2205 var recv_class_cname = recv.mcasttype.as(MClassType).mclass.c_name
2206 var my_recv = recv
2207
2208 if recv.mtype.is_c_primitive then
2209 my_recv = autobox(recv, mmodule.object_type)
2210 end
2211 var my_recv_mclass_type = my_recv.mtype.as(MClassType)
2212
2213 # The class of the concrete Routine must exist (e.g ProcRef0, FunRef0, etc.)
2214 self.require_declaration("class_{routine_mclass.c_name}")
2215 self.require_declaration("type_{routine_type.c_name}")
2216
2217 compiler.undead_types.add(routine_type)
2218 self.require_declaration(mmethoddef.c_name)
2219
2220 var thunk_function = mmethoddef.callref_thunk(my_recv_mclass_type)
2221 # If the receiver is exact, then there's no need to make a
2222 # polymorph call to the underlying method.
2223 thunk_function.polymorph_call_flag = not my_recv.is_exact
2224 var runtime_function = mmethoddef.virtual_runtime_function
2225
2226 var is_c_equiv = runtime_function.msignature.c_equiv(thunk_function.msignature)
2227
2228 var c_ref = thunk_function.c_ref
2229 if is_c_equiv then
2230 var const_color = mmethoddef.mproperty.const_color
2231 c_ref = "{class_info(my_recv)}->vft[{const_color}]"
2232 self.require_declaration(const_color)
2233 else
2234 self.require_declaration(thunk_function.c_name)
2235 compiler.thunk_todo(thunk_function)
2236 end
2237
2238 # Each RoutineRef points to a receiver AND a callref_thunk
2239 var res = self.new_expr("NEW_{base_routine_mclass.c_name}({my_recv}, (nitmethod_t){c_ref}, &class_{routine_mclass.c_name}, &type_{routine_type.c_name})", routine_type)
2240 #debug "LEAVING ref_instance"
2241 return res
2242 end
2243
2244 redef fun routine_ref_call(mmethoddef, arguments)
2245 do
2246 #debug "ENTER ref_call"
2247 compiler.modelbuilder.nb_invok_by_tables += 1
2248 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
2249 var nclasses = mmodule.model.get_mclasses_by_name("RoutineRef").as(not null)
2250 var nclass = nclasses.first
2251 var runtime_function = mmethoddef.virtual_runtime_function
2252
2253 # Save the current receiver since adapt_signature will autobox
2254 # the routine receiver which is not the underlying receiver.
2255 # The underlying receiver has already been adapted in the
2256 # `routine_ref_instance` method. Here we just want to adapt the
2257 # rest of the signature, but it's easier to pass the wrong
2258 # receiver in adapt_signature then discards it with `shift`.
2259 #
2260 # ~~~~nitish
2261 # class A; def toto do print "toto"; end
2262 # var a = new A
2263 # var f = &a.toto <- `a` is the underlying receiver
2264 # f.call <- here `f` is the routine receiver
2265 # ~~~~
2266 var routine = arguments.first
2267
2268 # Retrieve the concrete routine type
2269 var original_recv_c = "(((struct instance_{nclass.c_name}*){arguments[0]})->recv)"
2270 var nitmethod = "(({runtime_function.c_funptrtype})(((struct instance_{nclass.c_name}*){arguments[0]})->method))"
2271 if arguments.length > 1 then
2272 adapt_signature(mmethoddef, arguments)
2273 end
2274
2275 var ret_mtype = runtime_function.called_signature.return_mtype
2276
2277 if ret_mtype != null then
2278 # `ret` is actually always nullable Object. When invoking
2279 # a callref, we don't have the original callsite information.
2280 # Thus, we need to recompute the return type of the callsite.
2281 ret_mtype = resolve_for(ret_mtype, routine)
2282 end
2283
2284 # remove the routine's receiver
2285 arguments.shift
2286 var ss = arguments.join(", ")
2287 # replace the receiver with the original one
2288 if arguments.length > 0 then
2289 ss = "{original_recv_c}, {ss}"
2290 else
2291 ss = original_recv_c
2292 end
2293
2294 arguments.unshift routine # put back the routine ref receiver
2295 add "/* {mmethoddef.mproperty} on {arguments.first.inspect}*/"
2296 var callsite = "{nitmethod}({ss})"
2297 if ret_mtype != null then
2298 var subres = new_expr("{callsite}", ret_mtype)
2299 ret(subres)
2300 else
2301 add("{callsite};")
2302 end
2303 end
2304
2305 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
2306 assert mtype.need_anchor
2307 var compiler = self.compiler
2308 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
2309 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
2310 end
2311 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
2312 end
2313 end
2314
2315 redef class MMethodDef
2316 # The C function associated to a mmethoddef
2317 fun separate_runtime_function: SeparateRuntimeFunction
2318 do
2319 var res = self.separate_runtime_function_cache
2320 if res == null then
2321 var recv = mclassdef.bound_mtype
2322 var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
2323 res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
2324 self.separate_runtime_function_cache = res
2325 end
2326 return res
2327 end
2328
2329 # Returns true if the current method definition differ from
2330 # its original introduction in terms of receiver type.
2331 fun recv_differ_from_intro: Bool
2332 do
2333 var intromclassdef = mproperty.intro.mclassdef
2334 var introrecv = intromclassdef.bound_mtype
2335 return self.mclassdef.bound_mtype != introrecv
2336 end
2337
2338 # The C thunk function associated to a mmethoddef. Receives only nullable
2339 # Object and cast them to the original mmethoddef signature.
2340 fun callref_thunk(recv_mtype: MClassType): SeparateThunkFunction
2341 do
2342 var res = callref_thunk_cache
2343 if res == null then
2344 #var runtime_function = virtual_runtime_function
2345 var object_type = mclassdef.mmodule.object_type
2346 var nullable_object = object_type.as_nullable
2347 var msignature2 = msignature.change_all_mtype_for(nullable_object)
2348 var intromclassdef = mproperty.intro.mclassdef
2349
2350 #var introrecv = intromclassdef.bound_mtype
2351 ## If the thunk signature is equivalent to its
2352 ## virtual counterpart, then nothing to do.
2353 #print "recv vs intro : {recv_mtype} vs {introrecv}"
2354 #if msignature2.c_equiv(runtime_function.called_signature) and recv_mtype == introrecv then
2355 # callref_thunk_cache = res
2356 # return runtime_function
2357 #end
2358 # receiver cannot be null
2359 res = new SeparateThunkFunction(self, recv_mtype, msignature2, "THUNK_{c_name}", mclassdef.bound_mtype)
2360 res.polymorph_call_flag = true
2361 callref_thunk_cache = res
2362 end
2363 return res
2364 end
2365
2366 private var callref_thunk_cache: nullable SeparateThunkFunction
2367 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
2368
2369 # The C function associated to a mmethoddef, that can be stored into a VFT of a class
2370 # The first parameter (the reciever) is always typed by val* in order to accept an object value
2371 # The C-signature is always compatible with the intro
2372 fun virtual_runtime_function: SeparateRuntimeFunction
2373 do
2374 var res = self.virtual_runtime_function_cache
2375 if res == null then
2376 # Because the function is virtual, the signature must match the one of the original class
2377 var intromclassdef = mproperty.intro.mclassdef
2378 var recv = intromclassdef.bound_mtype
2379
2380 res = separate_runtime_function
2381 if res.called_recv == recv then
2382 self.virtual_runtime_function_cache = res
2383 return res
2384 end
2385
2386 var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
2387
2388 if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
2389 self.virtual_runtime_function_cache = res
2390 return res
2391 end
2392 res = new SeparateThunkFunction(self, recv, msignature, "VIRTUAL_{c_name}", mclassdef.bound_mtype)
2393 end
2394 return res
2395 end
2396 private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
2397 end
2398
2399 redef class MSignature
2400 # Does the C-version of `self` the same than the C-version of `other`?
2401 fun c_equiv(other: MSignature): Bool
2402 do
2403 if self == other then return true
2404 if arity != other.arity then return false
2405 for i in [0..arity[ do
2406 if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
2407 end
2408 if return_mtype != other.return_mtype then
2409 if return_mtype == null or other.return_mtype == null then return false
2410 if return_mtype.ctype != other.return_mtype.ctype then return false
2411 end
2412 return true
2413 end
2414 end
2415
2416 # The C function associated to a methoddef separately compiled
2417 class SeparateRuntimeFunction
2418 super AbstractRuntimeFunction
2419
2420 # The call-side static receiver
2421 var called_recv: MType
2422
2423 # The call-side static signature
2424 var called_signature: MSignature
2425
2426 # The name on the compiled method
2427 redef var build_c_name: String
2428
2429 redef fun to_s do return self.mmethoddef.to_s
2430
2431 redef fun msignature
2432 do
2433 return called_signature
2434 end
2435
2436 redef fun recv_mtype
2437 do
2438 return called_recv
2439 end
2440
2441 redef fun return_mtype
2442 do
2443 return called_signature.return_mtype
2444 end
2445
2446 # The C return type (something or `void`)
2447 var c_ret: String is lazy do
2448 var ret = called_signature.return_mtype
2449 if ret != null then
2450 return ret.ctype
2451 else
2452 return "void"
2453 end
2454 end
2455
2456 # The C signature (only the parmeter part)
2457 var c_sig: String is lazy do
2458 var sig = new FlatBuffer
2459 sig.append("({called_recv.ctype} self")
2460 for i in [0..called_signature.arity[ do
2461 var mp = called_signature.mparameters[i]
2462 var mtype = mp.mtype
2463 if mp.is_vararg then
2464 mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
2465 end
2466 sig.append(", {mtype.ctype} p{i}")
2467 end
2468 sig.append(")")
2469 return sig.to_s
2470 end
2471
2472 # The C type for the function pointer.
2473 var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
2474
2475 redef fun declare_signature(v, sig)
2476 do
2477 v.compiler.provide_declaration(c_name, "{sig};")
2478 end
2479
2480 redef fun body_to_c(v)
2481 do
2482 var rta = v.compiler.as(SeparateCompiler).runtime_type_analysis
2483 if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
2484 v.add_abort("FATAL: Dead method executed.")
2485 else
2486 super
2487 end
2488 end
2489
2490 redef fun end_compile_to_c(v)
2491 do
2492 var compiler = v.compiler
2493 compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
2494 end
2495
2496 redef fun build_frame(v, arguments)
2497 do
2498 var recv = mmethoddef.mclassdef.bound_mtype
2499 return new StaticFrame(v, mmethoddef, recv, arguments)
2500 end
2501
2502 # Compile the trampolines used to implement late-binding.
2503 #
2504 # See `opt_trampoline_call`.
2505 fun compile_trampolines(compiler: SeparateCompiler)
2506 do
2507 var recv = self.mmethoddef.mclassdef.bound_mtype
2508 var selfvar = new RuntimeVariable("self", called_recv, recv)
2509 var ret = called_signature.return_mtype
2510 var arguments = ["self"]
2511 for i in [0..called_signature.arity[ do arguments.add "p{i}"
2512
2513 if mmethoddef.is_intro and not recv.is_c_primitive then
2514 var m = mmethoddef.mproperty
2515 var n2 = "CALL_" + m.const_color
2516 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2517 var v2 = compiler.new_visitor
2518 v2.add "{c_ret} {n2}{c_sig} \{"
2519 v2.require_declaration(m.const_color)
2520 var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
2521 if ret != null then
2522 v2.add "return {call}"
2523 else
2524 v2.add call
2525 end
2526
2527 v2.add "\}"
2528
2529 end
2530 if mmethoddef.has_supercall and not recv.is_c_primitive then
2531 var m = mmethoddef
2532 var n2 = "CALL_" + m.const_color
2533 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2534 var v2 = compiler.new_visitor
2535 v2.add "{c_ret} {n2}{c_sig} \{"
2536 v2.require_declaration(m.const_color)
2537 var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
2538 if ret != null then
2539 v2.add "return {call}"
2540 else
2541 v2.add call
2542 end
2543
2544 v2.add "\}"
2545 end
2546 end
2547 end
2548
2549 class SeparateThunkFunction
2550 super ThunkFunction
2551 super SeparateRuntimeFunction
2552 redef var target_recv
2553 end
2554
2555 redef class MType
2556 # Are values of `self` tagged?
2557 # If false, it means that the type is not primitive, or is boxed.
2558 var is_tagged = false
2559
2560 # The tag value of the type
2561 #
2562 # ENSURE `is_tagged == (tag_value > 0)`
2563 # ENSURE `not is_tagged == (tag_value == 0)`
2564 var tag_value = 0
2565 end
2566
2567 redef class MEntity
2568 var const_color: String is lazy do return "COLOR_{c_name}"
2569 end
2570
2571 interface PropertyLayoutElement end
2572
2573 redef class MProperty
2574 super PropertyLayoutElement
2575 end
2576
2577 redef class MPropDef
2578 super PropertyLayoutElement
2579 end
2580
2581 redef class AMethPropdef
2582 # The semi-global compilation does not support inlining calls to extern news
2583 redef fun can_inline
2584 do
2585 var m = mpropdef
2586 if m != null and m.mproperty.is_init and m.is_extern then return false
2587 return super
2588 end
2589 end
2590
2591 redef class AAttrPropdef
2592 redef fun init_expr(v, recv)
2593 do
2594 super
2595 if is_lazy and v.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
2596 var guard = self.mlazypropdef.mproperty
2597 v.write_attribute(guard, recv, v.bool_instance(false))
2598 end
2599 end
2600 end