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