Merge: Rewrite the coloration for properties and types.
[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", "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.as_notnullable.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.as_notnullable.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.as_notnullable.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 var rta = runtime_type_analysis
629 if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue
630 #print "compile {pd} @ {cd} @ {mmodule}"
631 var r = pd.separate_runtime_function
632 r.compile_to_c(self)
633 var r2 = pd.virtual_runtime_function
634 if r2 != r then r2.compile_to_c(self)
635
636 # Generate trampolines
637 if modelbuilder.toolcontext.opt_trampoline_call.value then
638 r2.compile_trampolines(self)
639 end
640 end
641 end
642 self.mainmodule = old_module
643 end
644
645 # Process all introduced methods and compile some linking information (if needed)
646 fun link_mmethods
647 do
648 if not modelbuilder.toolcontext.opt_substitute_monomorph.value and not modelbuilder.toolcontext.opt_guard_call.value then return
649
650 for mmodule in mainmodule.in_importation.greaters do
651 for cd in mmodule.mclassdefs do
652 for m in cd.intro_mproperties do
653 if not m isa MMethod then continue
654 link_mmethod(m)
655 end
656 end
657 end
658 end
659
660 # Compile some linking information (if needed)
661 fun link_mmethod(m: MMethod)
662 do
663 var n2 = "CALL_" + m.const_color
664
665 # Replace monomorphic call by a direct call to the virtual implementation
666 var md = is_monomorphic(m)
667 if md != null then
668 linker_script.add("{n2} = {md.virtual_runtime_function.c_name};")
669 end
670
671 # If opt_substitute_monomorph then a trampoline is used, else a weak symbol is used
672 if modelbuilder.toolcontext.opt_guard_call.value then
673 var r = m.intro.virtual_runtime_function
674 provide_declaration(n2, "{r.c_ret} {n2}{r.c_sig} __attribute__((weak));")
675 end
676 end
677
678 # The single mmethodef called in case of monomorphism.
679 # Returns nul if dead or polymorphic.
680 fun is_monomorphic(m: MMethod): nullable MMethodDef
681 do
682 var rta = runtime_type_analysis
683 if rta == null then
684 # Without RTA, monomorphic means alone (uniq name)
685 if m.mpropdefs.length == 1 then
686 return m.mpropdefs.first
687 else
688 return null
689 end
690 else
691 # With RTA, monomorphic means only live methoddef
692 var res: nullable MMethodDef = null
693 for md in m.mpropdefs do
694 if rta.live_methoddefs.has(md) then
695 if res != null then return null
696 res = md
697 end
698 end
699 return res
700 end
701 end
702
703 # Globaly compile the type structure of a live type
704 fun compile_type_to_c(mtype: MType)
705 do
706 assert not mtype.need_anchor
707 var is_live = mtype isa MClassType and runtime_type_analysis.live_types.has(mtype)
708 var is_cast_live = runtime_type_analysis.live_cast_types.has(mtype)
709 var c_name = mtype.c_name
710 var v = new SeparateCompilerVisitor(self)
711 v.add_decl("/* runtime type {mtype} */")
712
713 # extern const struct type_X
714 self.provide_declaration("type_{c_name}", "extern const struct type type_{c_name};")
715
716 # const struct type_X
717 v.add_decl("const struct type type_{c_name} = \{")
718
719 # type id (for cast target)
720 if is_cast_live then
721 v.add_decl("{type_ids[mtype]},")
722 else
723 v.add_decl("-1, /*CAST DEAD*/")
724 end
725
726 # type name
727 v.add_decl("\"{mtype}\", /* class_name_string */")
728
729 # type color (for cast target)
730 if is_cast_live then
731 v.add_decl("{type_colors[mtype]},")
732 else
733 v.add_decl("-1, /*CAST DEAD*/")
734 end
735
736 # is_nullable bit
737 if mtype isa MNullableType then
738 v.add_decl("1,")
739 else
740 v.add_decl("0,")
741 end
742
743 # resolution table (for receiver)
744 if is_live then
745 var mclass_type = mtype.as_notnullable
746 assert mclass_type isa MClassType
747 if resolution_tables[mclass_type].is_empty then
748 v.add_decl("NULL, /*NO RESOLUTIONS*/")
749 else
750 compile_type_resolution_table(mtype)
751 v.require_declaration("resolution_table_{c_name}")
752 v.add_decl("&resolution_table_{c_name},")
753 end
754 else
755 v.add_decl("NULL, /*DEAD*/")
756 end
757
758 # cast table (for receiver)
759 if is_live then
760 v.add_decl("{self.type_tables[mtype].length},")
761 v.add_decl("\{")
762 for stype in self.type_tables[mtype] do
763 if stype == null then
764 v.add_decl("-1, /* empty */")
765 else
766 v.add_decl("{type_ids[stype]}, /* {stype} */")
767 end
768 end
769 v.add_decl("\},")
770 else
771 v.add_decl("0, \{\}, /*DEAD TYPE*/")
772 end
773 v.add_decl("\};")
774 end
775
776 fun compile_type_resolution_table(mtype: MType) do
777
778 var mclass_type = mtype.as_notnullable.as(MClassType)
779
780 # extern const struct resolution_table_X resolution_table_X
781 self.provide_declaration("resolution_table_{mtype.c_name}", "extern const struct types resolution_table_{mtype.c_name};")
782
783 # const struct fts_table_X fts_table_X
784 var v = new_visitor
785 v.add_decl("const struct types resolution_table_{mtype.c_name} = \{")
786 v.add_decl("0, /* dummy */")
787 v.add_decl("\{")
788 for t in self.resolution_tables[mclass_type] do
789 if t == null then
790 v.add_decl("NULL, /* empty */")
791 else
792 # The table stores the result of the type resolution
793 # Therefore, for a receiver `mclass_type`, and a unresolved type `t`
794 # the value stored is tv.
795 var tv = t.resolve_for(mclass_type, mclass_type, self.mainmodule, true)
796 # FIXME: What typeids means here? How can a tv not be live?
797 if type_ids.has_key(tv) then
798 v.require_declaration("type_{tv.c_name}")
799 v.add_decl("&type_{tv.c_name}, /* {t}: {tv} */")
800 else
801 v.add_decl("NULL, /* empty ({t}: {tv} not a live type) */")
802 end
803 end
804 end
805 v.add_decl("\}")
806 v.add_decl("\};")
807 end
808
809 # Globally compile the table of the class mclass
810 # In a link-time optimisation compiler, tables are globally computed
811 # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
812 fun compile_class_to_c(mclass: MClass)
813 do
814 var mtype = mclass.intro.bound_mtype
815 var c_name = mclass.c_name
816
817 var v = new_visitor
818
819 var rta = runtime_type_analysis
820 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"
821
822 v.add_decl("/* runtime class {c_name} */")
823
824 # Build class vft
825 if not is_dead then
826 self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
827 v.add_decl("const struct class class_{c_name} = \{")
828 v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
829 v.add_decl("\{")
830 var vft = self.method_tables.get_or_null(mclass)
831 if vft != null then for i in [0 .. vft.length[ do
832 var mpropdef = vft[i]
833 if mpropdef == null then
834 v.add_decl("NULL, /* empty */")
835 else
836 assert mpropdef isa MMethodDef
837 if rta != null and not rta.live_methoddefs.has(mpropdef) then
838 v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
839 continue
840 end
841 var rf = mpropdef.virtual_runtime_function
842 v.require_declaration(rf.c_name)
843 v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
844 end
845 end
846 v.add_decl("\}")
847 v.add_decl("\};")
848 end
849
850 if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
851 # Is a primitive type or the Pointer class, not any other extern class
852
853 if mtype.is_tagged then return
854
855 #Build instance struct
856 self.header.add_decl("struct instance_{c_name} \{")
857 self.header.add_decl("const struct type *type;")
858 self.header.add_decl("const struct class *class;")
859 self.header.add_decl("{mtype.ctype_extern} value;")
860 self.header.add_decl("\};")
861
862 if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
863
864 #Build BOX
865 self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
866 v.add_decl("/* allocate {mtype} */")
867 v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
868 v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
869 v.compiler.undead_types.add(mtype)
870 v.require_declaration("type_{c_name}")
871 v.add("res->type = &type_{c_name};")
872 v.require_declaration("class_{c_name}")
873 v.add("res->class = &class_{c_name};")
874 v.add("res->value = value;")
875 v.add("return (val*)res;")
876 v.add("\}")
877
878 if mtype.mclass.name != "Pointer" then return
879
880 v = new_visitor
881 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
882 v.add_decl("/* allocate {mtype} */")
883 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
884 if is_dead then
885 v.add_abort("{mclass} is DEAD")
886 else
887 var res = v.new_named_var(mtype, "self")
888 res.is_exact = true
889 v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
890 v.add("{res}->type = type;")
891 hardening_live_type(v, "type")
892 v.require_declaration("class_{c_name}")
893 v.add("{res}->class = &class_{c_name};")
894 v.add("((struct instance_{mtype.c_name}*){res})->value = NULL;")
895 v.add("return {res};")
896 end
897 v.add("\}")
898 return
899 else if mclass.name == "NativeArray" then
900 #Build instance struct
901 self.header.add_decl("struct instance_{c_name} \{")
902 self.header.add_decl("const struct type *type;")
903 self.header.add_decl("const struct class *class;")
904 # NativeArrays are just a instance header followed by a length and an array of values
905 self.header.add_decl("int length;")
906 self.header.add_decl("val* values[0];")
907 self.header.add_decl("\};")
908
909 #Build NEW
910 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
911 v.add_decl("/* allocate {mtype} */")
912 v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
913 var res = v.get_name("self")
914 v.add_decl("struct instance_{c_name} *{res};")
915 var mtype_elt = mtype.arguments.first
916 v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
917 v.add("{res}->type = type;")
918 hardening_live_type(v, "type")
919 v.require_declaration("class_{c_name}")
920 v.add("{res}->class = &class_{c_name};")
921 v.add("{res}->length = length;")
922 v.add("return (val*){res};")
923 v.add("\}")
924 return
925 else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
926 # Is an extern class (other than Pointer and NativeString)
927 # Pointer is caught in a previous `if`, and NativeString is internal
928
929 var pointer_type = mainmodule.pointer_type
930
931 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
932 v.add_decl("/* allocate {mtype} */")
933 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
934 if is_dead then
935 v.add_abort("{mclass} is DEAD")
936 else
937 var res = v.new_named_var(mtype, "self")
938 res.is_exact = true
939 v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
940 v.add("{res}->type = type;")
941 hardening_live_type(v, "type")
942 v.require_declaration("class_{c_name}")
943 v.add("{res}->class = &class_{c_name};")
944 v.add("((struct instance_{pointer_type.c_name}*){res})->value = NULL;")
945 v.add("return {res};")
946 end
947 v.add("\}")
948 return
949 end
950
951 #Build NEW
952 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
953 v.add_decl("/* allocate {mtype} */")
954 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
955 if is_dead then
956 v.add_abort("{mclass} is DEAD")
957 else
958 var res = v.new_named_var(mtype, "self")
959 res.is_exact = true
960 var attrs = self.attr_tables.get_or_null(mclass)
961 if attrs == null then
962 v.add("{res} = nit_alloc(sizeof(struct instance));")
963 else
964 v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
965 end
966 v.add("{res}->type = type;")
967 hardening_live_type(v, "type")
968 v.require_declaration("class_{c_name}")
969 v.add("{res}->class = &class_{c_name};")
970 if attrs != null then
971 self.generate_init_attr(v, res, mtype)
972 v.set_finalizer res
973 end
974 v.add("return {res};")
975 end
976 v.add("\}")
977 end
978
979 # Compile structures used to map tagged primitive values to their classes and types.
980 # This method also determines which class will be tagged.
981 fun compile_class_infos
982 do
983 if modelbuilder.toolcontext.opt_no_tag_primitives.value then return
984
985 # Note: if you change the tagging scheme, do not forget to update
986 # `autobox` and `extract_tag`
987 var class_info = new Array[nullable MClass].filled_with(null, 4)
988 for t in box_kinds.keys do
989 # Note: a same class can be associated to multiple slots if one want to
990 # use some Huffman coding.
991 if t.name == "Int" then
992 class_info[1] = t
993 else if t.name == "Char" then
994 class_info[2] = t
995 else if t.name == "Bool" then
996 class_info[3] = t
997 else
998 continue
999 end
1000 t.mclass_type.is_tagged = true
1001 end
1002
1003 # Compile the table for classes. The tag is used as an index
1004 var v = self.new_visitor
1005 v.add_decl "const struct class *class_info[4] = \{"
1006 for t in class_info do
1007 if t == null then
1008 v.add_decl("NULL,")
1009 else
1010 var s = "class_{t.c_name}"
1011 v.require_declaration(s)
1012 v.add_decl("&{s},")
1013 end
1014 end
1015 v.add_decl("\};")
1016
1017 # Compile the table for types. The tag is used as an index
1018 v.add_decl "const struct type *type_info[4] = \{"
1019 for t in class_info do
1020 if t == null then
1021 v.add_decl("NULL,")
1022 else
1023 var s = "type_{t.c_name}"
1024 undead_types.add(t.mclass_type)
1025 v.require_declaration(s)
1026 v.add_decl("&{s},")
1027 end
1028 end
1029 v.add_decl("\};")
1030 end
1031
1032 # Add a dynamic test to ensure that the type referenced by `t` is a live type
1033 fun hardening_live_type(v: VISITOR, t: String)
1034 do
1035 if not v.compiler.modelbuilder.toolcontext.opt_hardening.value then return
1036 v.add("if({t} == NULL) \{")
1037 v.add_abort("type null")
1038 v.add("\}")
1039 v.add("if({t}->table_size == 0) \{")
1040 v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
1041 v.add_abort("type dead")
1042 v.add("\}")
1043 end
1044
1045 redef fun new_visitor do return new SeparateCompilerVisitor(self)
1046
1047 # Stats
1048
1049 private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
1050 private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
1051 protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
1052 protected var attr_tables: Map[MClass, Array[nullable MProperty]] = new HashMap[MClass, Array[nullable MProperty]]
1053
1054 redef fun display_stats
1055 do
1056 super
1057 if self.modelbuilder.toolcontext.opt_tables_metrics.value then
1058 display_sizes
1059 end
1060 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1061 display_isset_checks
1062 end
1063 var tc = self.modelbuilder.toolcontext
1064 tc.info("# implementation of method invocation",2)
1065 var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
1066 tc.info("total number of invocations: {nb_invok_total}",2)
1067 tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
1068 tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
1069 tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
1070 end
1071
1072 fun display_sizes
1073 do
1074 print "# size of subtyping tables"
1075 print "\ttotal \tholes"
1076 var total = 0
1077 var holes = 0
1078 for t, table in type_tables do
1079 total += table.length
1080 for e in table do if e == null then holes += 1
1081 end
1082 print "\t{total}\t{holes}"
1083
1084 print "# size of resolution tables"
1085 print "\ttotal \tholes"
1086 total = 0
1087 holes = 0
1088 for t, table in resolution_tables do
1089 total += table.length
1090 for e in table do if e == null then holes += 1
1091 end
1092 print "\t{total}\t{holes}"
1093
1094 print "# size of methods tables"
1095 print "\ttotal \tholes"
1096 total = 0
1097 holes = 0
1098 for t, table in method_tables do
1099 total += table.length
1100 for e in table do if e == null then holes += 1
1101 end
1102 print "\t{total}\t{holes}"
1103
1104 print "# size of attributes tables"
1105 print "\ttotal \tholes"
1106 total = 0
1107 holes = 0
1108 for t, table in attr_tables do
1109 total += table.length
1110 for e in table do if e == null then holes += 1
1111 end
1112 print "\t{total}\t{holes}"
1113 end
1114
1115 protected var isset_checks_count = 0
1116 protected var attr_read_count = 0
1117
1118 fun display_isset_checks do
1119 print "# total number of compiled attribute reads"
1120 print "\t{attr_read_count}"
1121 print "# total number of compiled isset-checks"
1122 print "\t{isset_checks_count}"
1123 end
1124
1125 redef fun compile_nitni_structs
1126 do
1127 self.header.add_decl """
1128 struct nitni_instance \{
1129 struct nitni_instance *next,
1130 *prev; /* adjacent global references in global list */
1131 int count; /* number of time this global reference has been marked */
1132 struct instance *value;
1133 \};
1134 """
1135 super
1136 end
1137
1138 redef fun finalize_ffi_for_module(mmodule)
1139 do
1140 var old_module = self.mainmodule
1141 self.mainmodule = mmodule
1142 super
1143 self.mainmodule = old_module
1144 end
1145 end
1146
1147 # A visitor on the AST of property definition that generate the C code of a separate compilation process.
1148 class SeparateCompilerVisitor
1149 super AbstractCompilerVisitor
1150
1151 redef type COMPILER: SeparateCompiler
1152
1153 redef fun adapt_signature(m, args)
1154 do
1155 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1156 var recv = args.first
1157 if recv.mtype.ctype != m.mclassdef.mclass.mclass_type.ctype then
1158 args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
1159 end
1160 for i in [0..msignature.arity[ do
1161 var t = msignature.mparameters[i].mtype
1162 if i == msignature.vararg_rank then
1163 t = args[i+1].mtype
1164 end
1165 args[i+1] = self.autobox(args[i+1], t)
1166 end
1167 end
1168
1169 redef fun unbox_signature_extern(m, args)
1170 do
1171 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1172 if not m.mproperty.is_init and m.is_extern then
1173 args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
1174 end
1175 for i in [0..msignature.arity[ do
1176 var t = msignature.mparameters[i].mtype
1177 if i == msignature.vararg_rank then
1178 t = args[i+1].mtype
1179 end
1180 if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
1181 end
1182 end
1183
1184 redef fun autobox(value, mtype)
1185 do
1186 if value.mtype == mtype then
1187 return value
1188 else if not value.mtype.is_c_primitive and not mtype.is_c_primitive then
1189 return value
1190 else if not value.mtype.is_c_primitive then
1191 if mtype.is_tagged then
1192 if mtype.name == "Int" then
1193 return self.new_expr("(long)({value})>>2", mtype)
1194 else if mtype.name == "Char" then
1195 return self.new_expr("(char)((long)({value})>>2)", mtype)
1196 else if mtype.name == "Bool" then
1197 return self.new_expr("(short int)((long)({value})>>2)", mtype)
1198 else
1199 abort
1200 end
1201 end
1202 return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
1203 else if not mtype.is_c_primitive then
1204 if value.mtype.is_tagged then
1205 if value.mtype.name == "Int" then
1206 return self.new_expr("(val*)({value}<<2|1)", mtype)
1207 else if value.mtype.name == "Char" then
1208 return self.new_expr("(val*)((long)({value})<<2|2)", mtype)
1209 else if value.mtype.name == "Bool" then
1210 return self.new_expr("(val*)((long)({value})<<2|3)", mtype)
1211 else
1212 abort
1213 end
1214 end
1215 var valtype = value.mtype.as(MClassType)
1216 if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
1217 valtype = compiler.mainmodule.pointer_type
1218 end
1219 var res = self.new_var(mtype)
1220 if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
1221 self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
1222 self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
1223 return res
1224 end
1225 self.require_declaration("BOX_{valtype.c_name}")
1226 self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
1227 return res
1228 else if (value.mtype.ctype == "void*" and mtype.ctype == "void*") or
1229 (value.mtype.ctype == "char*" and mtype.ctype == "void*") or
1230 (value.mtype.ctype == "void*" and mtype.ctype == "char*") then
1231 return value
1232 else
1233 # Bad things will appen!
1234 var res = self.new_var(mtype)
1235 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1236 self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); fatal_exit(1);")
1237 return res
1238 end
1239 end
1240
1241 redef fun unbox_extern(value, mtype)
1242 do
1243 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1244 mtype.mclass.name != "NativeString" then
1245 var pointer_type = compiler.mainmodule.pointer_type
1246 var res = self.new_var_extern(mtype)
1247 self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
1248 return res
1249 else
1250 return value
1251 end
1252 end
1253
1254 redef fun box_extern(value, mtype)
1255 do
1256 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1257 mtype.mclass.name != "NativeString" then
1258 var valtype = compiler.mainmodule.pointer_type
1259 var res = self.new_var(mtype)
1260 if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
1261 self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
1262 self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
1263 return res
1264 end
1265 self.require_declaration("BOX_{valtype.c_name}")
1266 self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
1267 self.require_declaration("type_{mtype.c_name}")
1268 self.add("{res}->type = &type_{mtype.c_name};")
1269 self.require_declaration("class_{mtype.c_name}")
1270 self.add("{res}->class = &class_{mtype.c_name};")
1271 return res
1272 else
1273 return value
1274 end
1275 end
1276
1277 # Returns a C expression containing the tag of the value as a long.
1278 #
1279 # If the C expression is evaluated to 0, it means there is no tag.
1280 # Thus the expression can be used as a condition.
1281 fun extract_tag(value: RuntimeVariable): String
1282 do
1283 assert not value.mtype.is_c_primitive
1284 return "((long){value}&3)" # Get the two low bits
1285 end
1286
1287 # Returns a C expression of the runtime class structure of the value.
1288 # The point of the method is to work also with primitive types.
1289 fun class_info(value: RuntimeVariable): String
1290 do
1291 if not value.mtype.is_c_primitive then
1292 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1293 var tag = extract_tag(value)
1294 return "({tag}?class_info[{tag}]:{value}->class)"
1295 end
1296 return "{value}->class"
1297 else
1298 compiler.undead_types.add(value.mtype)
1299 self.require_declaration("class_{value.mtype.c_name}")
1300 return "(&class_{value.mtype.c_name})"
1301 end
1302 end
1303
1304 # Returns a C expression of the runtime type structure of the value.
1305 # The point of the method is to work also with primitive types.
1306 fun type_info(value: RuntimeVariable): String
1307 do
1308 if not value.mtype.is_c_primitive then
1309 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1310 var tag = extract_tag(value)
1311 return "({tag}?type_info[{tag}]:{value}->type)"
1312 end
1313 return "{value}->type"
1314 else
1315 compiler.undead_types.add(value.mtype)
1316 self.require_declaration("type_{value.mtype.c_name}")
1317 return "(&type_{value.mtype.c_name})"
1318 end
1319 end
1320
1321 redef fun compile_callsite(callsite, args)
1322 do
1323 var rta = compiler.runtime_type_analysis
1324 # TODO: Inlining of new-style constructors with initializers
1325 if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
1326 var tgs = rta.live_targets(callsite)
1327 if tgs.length == 1 then
1328 return direct_call(tgs.first, args)
1329 end
1330 end
1331 # Shortcut intern methods as they are not usually redefinable
1332 if callsite.mpropdef.is_intern and callsite.mproperty.name != "object_id" then
1333 # `object_id` is the only redefined intern method, so it can not be directly called.
1334 # TODO find a less ugly approach?
1335 return direct_call(callsite.mpropdef, args)
1336 end
1337 return super
1338 end
1339
1340 # Fully and directly call a mpropdef
1341 #
1342 # This method is used by `compile_callsite`
1343 private fun direct_call(mpropdef: MMethodDef, args: Array[RuntimeVariable]): nullable RuntimeVariable
1344 do
1345 var res0 = before_send(mpropdef.mproperty, args)
1346 var res = call(mpropdef, mpropdef.mclassdef.bound_mtype, args)
1347 if res0 != null then
1348 assert res != null
1349 self.assign(res0, res)
1350 res = res0
1351 end
1352 add("\}") # close the before_send
1353 return res
1354 end
1355 redef fun send(mmethod, arguments)
1356 do
1357 if arguments.first.mcasttype.is_c_primitive then
1358 # In order to shortcut the primitive, we need to find the most specific method
1359 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1360 var m = self.compiler.mainmodule
1361 self.compiler.mainmodule = self.compiler.realmainmodule
1362 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1363 self.compiler.mainmodule = m
1364 return res
1365 end
1366
1367 return table_send(mmethod, arguments, mmethod)
1368 end
1369
1370 # Handle common special cases before doing the effective method invocation
1371 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1372 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1373 # Client must not forget to close the } after them.
1374 #
1375 # The value returned is the result of the common special cases.
1376 # If not null, client must compile it with the result of their own effective method invocation.
1377 #
1378 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1379 # is generated to cancel the effective method invocation that will follow
1380 # TODO: find a better approach
1381 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1382 do
1383 var res: nullable RuntimeVariable = null
1384 var recv = arguments.first
1385 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
1386 var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null
1387 if maybenull then
1388 self.add("if ({recv} == NULL) \{")
1389 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
1390 res = self.new_var(bool_type)
1391 var arg = arguments[1]
1392 if arg.mcasttype isa MNullableType then
1393 self.add("{res} = ({arg} == NULL);")
1394 else if arg.mcasttype isa MNullType then
1395 self.add("{res} = 1; /* is null */")
1396 else
1397 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1398 end
1399 else if mmethod.name == "!=" then
1400 res = self.new_var(bool_type)
1401 var arg = arguments[1]
1402 if arg.mcasttype isa MNullableType then
1403 self.add("{res} = ({arg} != NULL);")
1404 else if arg.mcasttype isa MNullType then
1405 self.add("{res} = 0; /* is null */")
1406 else
1407 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1408 end
1409 else
1410 self.add_abort("Receiver is null")
1411 end
1412 self.add("\} else \{")
1413 else
1414 self.add("\{")
1415 end
1416 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
1417 # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
1418 var arg = arguments[1]
1419 if arg.mcasttype isa MNullType then
1420 if res == null then res = self.new_var(bool_type)
1421 if mmethod.name == "!=" then
1422 self.add("{res} = 1; /* arg is null and recv is not */")
1423 else # `==` and `is_same_instance`
1424 self.add("{res} = 0; /* arg is null but recv is not */")
1425 end
1426 self.add("\}") # closes the null case
1427 self.add("if (0) \{") # what follow is useless, CC will drop it
1428 end
1429 end
1430 return res
1431 end
1432
1433 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
1434 do
1435 compiler.modelbuilder.nb_invok_by_tables += 1
1436 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1437
1438 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1439 var recv = arguments.first
1440
1441 var res0 = before_send(mmethod, arguments)
1442
1443 var runtime_function = mmethod.intro.virtual_runtime_function
1444 var msignature = runtime_function.called_signature
1445
1446 var res: nullable RuntimeVariable
1447 var ret = msignature.return_mtype
1448 if ret == null then
1449 res = null
1450 else
1451 res = self.new_var(ret)
1452 end
1453
1454 var ss = new FlatBuffer
1455
1456 ss.append("{recv}")
1457 for i in [0..msignature.arity[ do
1458 var a = arguments[i+1]
1459 var t = msignature.mparameters[i].mtype
1460 if i == msignature.vararg_rank then
1461 t = arguments[i+1].mcasttype
1462 end
1463 a = self.autobox(a, t)
1464 ss.append(", {a}")
1465 end
1466
1467 var const_color = mentity.const_color
1468 var ress
1469 if res != null then
1470 ress = "{res} = "
1471 else
1472 ress = ""
1473 end
1474 if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_direct_call_monomorph0.value then
1475 # opt_direct_call_monomorph0 is used to compare the efficiency of the alternative lookup implementation, ceteris paribus.
1476 # The difference with the non-zero option is that the monomorphism is looked-at on the mmethod level and not at the callsite level.
1477 # TODO: remove this mess and use per callsite service to detect monomorphism in a single place.
1478 var md = compiler.is_monomorphic(mentity)
1479 if md != null then
1480 var callsym = md.virtual_runtime_function.c_name
1481 self.require_declaration(callsym)
1482 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1483 else
1484 self.require_declaration(const_color)
1485 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1486 end
1487 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_guard_call.value then
1488 var callsym = "CALL_" + const_color
1489 self.require_declaration(callsym)
1490 self.add "if (!{callsym}) \{"
1491 self.require_declaration(const_color)
1492 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1493 self.add "\} else \{"
1494 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1495 self.add "\}"
1496 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
1497 var callsym = "CALL_" + const_color
1498 self.require_declaration(callsym)
1499 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1500 else
1501 self.require_declaration(const_color)
1502 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1503 end
1504
1505 if res0 != null then
1506 assert res != null
1507 assign(res0,res)
1508 res = res0
1509 end
1510
1511 self.add("\}") # closes the null case
1512
1513 return res
1514 end
1515
1516 redef fun call(mmethoddef, recvtype, arguments)
1517 do
1518 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1519
1520 var res: nullable RuntimeVariable
1521 var ret = mmethoddef.msignature.return_mtype
1522 if ret == null then
1523 res = null
1524 else
1525 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1526 res = self.new_var(ret)
1527 end
1528
1529 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1530 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1531 compiler.modelbuilder.nb_invok_by_inline += 1
1532 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1533 var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
1534 frame.returnlabel = self.get_name("RET_LABEL")
1535 frame.returnvar = res
1536 var old_frame = self.frame
1537 self.frame = frame
1538 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1539 mmethoddef.compile_inside_to_c(self, arguments)
1540 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1541 self.add("\}")
1542 self.frame = old_frame
1543 return res
1544 end
1545 compiler.modelbuilder.nb_invok_by_direct += 1
1546 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1547
1548 # Autobox arguments
1549 self.adapt_signature(mmethoddef, arguments)
1550
1551 self.require_declaration(mmethoddef.c_name)
1552 if res == null then
1553 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1554 return null
1555 else
1556 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1557 end
1558
1559 return res
1560 end
1561
1562 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1563 do
1564 if arguments.first.mcasttype.is_c_primitive then
1565 # In order to shortcut the primitive, we need to find the most specific method
1566 # However, because of performance (no flattening), we always work on the realmainmodule
1567 var main = self.compiler.mainmodule
1568 self.compiler.mainmodule = self.compiler.realmainmodule
1569 var res = self.monomorphic_super_send(m, recvtype, arguments)
1570 self.compiler.mainmodule = main
1571 return res
1572 end
1573 return table_send(m.mproperty, arguments, m)
1574 end
1575
1576 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1577 do
1578 # A vararg must be stored into an new array
1579 # The trick is that the dymaic type of the array may depends on the receiver
1580 # of the method (ie recv) if the static type is unresolved
1581 # This is more complex than usual because the unresolved type must not be resolved
1582 # with the current receiver (ie self).
1583 # Therefore to isolate the resolution from self, a local StaticFrame is created.
1584 # One can see this implementation as an inlined method of the receiver whose only
1585 # job is to allocate the array
1586 var old_frame = self.frame
1587 var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1588 self.frame = frame
1589 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1590 var res = self.array_instance(varargs, elttype)
1591 self.frame = old_frame
1592 return res
1593 end
1594
1595 redef fun isset_attribute(a, recv)
1596 do
1597 self.check_recv_notnull(recv)
1598 var res = self.new_var(bool_type)
1599
1600 # What is the declared type of the attribute?
1601 var mtype = a.intro.static_mtype.as(not null)
1602 var intromclassdef = a.intro.mclassdef
1603 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1604
1605 if mtype isa MNullableType then
1606 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1607 return res
1608 end
1609
1610 self.require_declaration(a.const_color)
1611 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1612 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1613 else
1614
1615 if not mtype.is_c_primitive and not mtype.is_tagged then
1616 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1617 else
1618 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1619 end
1620 end
1621 return res
1622 end
1623
1624 redef fun read_attribute(a, recv)
1625 do
1626 self.check_recv_notnull(recv)
1627
1628 # What is the declared type of the attribute?
1629 var ret = a.intro.static_mtype.as(not null)
1630 var intromclassdef = a.intro.mclassdef
1631 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1632
1633 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1634 self.compiler.attr_read_count += 1
1635 self.add("count_attr_reads++;")
1636 end
1637
1638 self.require_declaration(a.const_color)
1639 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1640 # Get the attribute or a box (ie. always a val*)
1641 var cret = self.object_type.as_nullable
1642 var res = self.new_var(cret)
1643 res.mcasttype = ret
1644
1645 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1646
1647 # Check for Uninitialized attribute
1648 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1649 self.add("if (unlikely({res} == NULL)) \{")
1650 self.add_abort("Uninitialized attribute {a.name}")
1651 self.add("\}")
1652
1653 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1654 self.compiler.isset_checks_count += 1
1655 self.add("count_isset_checks++;")
1656 end
1657 end
1658
1659 # Return the attribute or its unboxed version
1660 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1661 return self.autobox(res, ret)
1662 else
1663 var res = self.new_var(ret)
1664 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1665
1666 # Check for Uninitialized attribute
1667 if not ret.is_c_primitive and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1668 self.add("if (unlikely({res} == NULL)) \{")
1669 self.add_abort("Uninitialized attribute {a.name}")
1670 self.add("\}")
1671 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1672 self.compiler.isset_checks_count += 1
1673 self.add("count_isset_checks++;")
1674 end
1675 end
1676
1677 return res
1678 end
1679 end
1680
1681 redef fun write_attribute(a, recv, value)
1682 do
1683 self.check_recv_notnull(recv)
1684
1685 # What is the declared type of the attribute?
1686 var mtype = a.intro.static_mtype.as(not null)
1687 var intromclassdef = a.intro.mclassdef
1688 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1689
1690 # Adapt the value to the declared type
1691 value = self.autobox(value, mtype)
1692
1693 self.require_declaration(a.const_color)
1694 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1695 var attr = "{recv}->attrs[{a.const_color}]"
1696 if mtype.is_tagged then
1697 # The attribute is not primitive, thus store it as tagged
1698 var tv = autobox(value, compiler.mainmodule.object_type)
1699 self.add("{attr} = {tv}; /* {a} on {recv.inspect} */")
1700 else if mtype.is_c_primitive then
1701 assert mtype isa MClassType
1702 # The attribute is primitive, thus we store it in a box
1703 # The trick is to create the box the first time then resuse the box
1704 self.add("if ({attr} != NULL) \{")
1705 self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1706 self.add("\} else \{")
1707 value = self.autobox(value, self.object_type.as_nullable)
1708 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1709 self.add("\}")
1710 else
1711 # The attribute is not primitive, thus store it direclty
1712 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1713 end
1714 else
1715 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1716 end
1717 end
1718
1719 # Check that mtype is a live open type
1720 fun hardening_live_open_type(mtype: MType)
1721 do
1722 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1723 self.require_declaration(mtype.const_color)
1724 var col = mtype.const_color
1725 self.add("if({col} == -1) \{")
1726 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1727 self.add_abort("open type dead")
1728 self.add("\}")
1729 end
1730
1731 # Check that mtype it a pointer to a live cast type
1732 fun hardening_cast_type(t: String)
1733 do
1734 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1735 add("if({t} == NULL) \{")
1736 add_abort("cast type null")
1737 add("\}")
1738 add("if({t}->id == -1 || {t}->color == -1) \{")
1739 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1740 add_abort("cast type dead")
1741 add("\}")
1742 end
1743
1744 redef fun init_instance(mtype)
1745 do
1746 self.require_declaration("NEW_{mtype.mclass.c_name}")
1747 var compiler = self.compiler
1748 if mtype isa MGenericType and mtype.need_anchor then
1749 hardening_live_open_type(mtype)
1750 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1751 var recv = self.frame.arguments.first
1752 var recv_type_info = self.type_info(recv)
1753 self.require_declaration(mtype.const_color)
1754 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1755 end
1756 compiler.undead_types.add(mtype)
1757 self.require_declaration("type_{mtype.c_name}")
1758 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1759 end
1760
1761 redef fun type_test(value, mtype, tag)
1762 do
1763 self.add("/* {value.inspect} isa {mtype} */")
1764 var compiler = self.compiler
1765
1766 var recv = self.frame.arguments.first
1767 var recv_type_info = self.type_info(recv)
1768
1769 var res = self.new_var(bool_type)
1770
1771 var cltype = self.get_name("cltype")
1772 self.add_decl("int {cltype};")
1773 var idtype = self.get_name("idtype")
1774 self.add_decl("int {idtype};")
1775
1776 var maybe_null = self.maybe_null(value)
1777 var accept_null = "0"
1778 var ntype = mtype
1779 if ntype isa MNullableType then
1780 ntype = ntype.mtype
1781 accept_null = "1"
1782 end
1783
1784 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1785 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1786 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1787 self.compiler.count_type_test_skipped[tag] += 1
1788 self.add("count_type_test_skipped_{tag}++;")
1789 end
1790 return res
1791 end
1792
1793 if ntype.need_anchor then
1794 var type_struct = self.get_name("type_struct")
1795 self.add_decl("const struct type* {type_struct};")
1796
1797 # Either with resolution_table with a direct resolution
1798 hardening_live_open_type(mtype)
1799 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1800 self.require_declaration(mtype.const_color)
1801 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1802 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1803 self.compiler.count_type_test_unresolved[tag] += 1
1804 self.add("count_type_test_unresolved_{tag}++;")
1805 end
1806 hardening_cast_type(type_struct)
1807 self.add("{cltype} = {type_struct}->color;")
1808 self.add("{idtype} = {type_struct}->id;")
1809 if maybe_null and accept_null == "0" then
1810 var is_nullable = self.get_name("is_nullable")
1811 self.add_decl("short int {is_nullable};")
1812 self.add("{is_nullable} = {type_struct}->is_nullable;")
1813 accept_null = is_nullable.to_s
1814 end
1815 else if ntype isa MClassType then
1816 compiler.undead_types.add(mtype)
1817 self.require_declaration("type_{mtype.c_name}")
1818 hardening_cast_type("(&type_{mtype.c_name})")
1819 self.add("{cltype} = type_{mtype.c_name}.color;")
1820 self.add("{idtype} = type_{mtype.c_name}.id;")
1821 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1822 self.compiler.count_type_test_resolved[tag] += 1
1823 self.add("count_type_test_resolved_{tag}++;")
1824 end
1825 else
1826 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); fatal_exit(1);")
1827 end
1828
1829 # check color is in table
1830 if maybe_null then
1831 self.add("if({value} == NULL) \{")
1832 self.add("{res} = {accept_null};")
1833 self.add("\} else \{")
1834 end
1835 var value_type_info = self.type_info(value)
1836 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1837 self.add("{res} = 0;")
1838 self.add("\} else \{")
1839 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1840 self.add("\}")
1841 if maybe_null then
1842 self.add("\}")
1843 end
1844
1845 return res
1846 end
1847
1848 redef fun is_same_type_test(value1, value2)
1849 do
1850 var res = self.new_var(bool_type)
1851 # Swap values to be symetric
1852 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1853 var tmp = value1
1854 value1 = value2
1855 value2 = tmp
1856 end
1857 if value1.mtype.is_c_primitive then
1858 if value2.mtype == value1.mtype then
1859 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1860 else if value2.mtype.is_c_primitive then
1861 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1862 else
1863 var mtype1 = value1.mtype.as(MClassType)
1864 self.require_declaration("class_{mtype1.c_name}")
1865 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
1866 end
1867 else
1868 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
1869 end
1870 return res
1871 end
1872
1873 redef fun class_name_string(value)
1874 do
1875 var res = self.get_name("var_class_name")
1876 self.add_decl("const char* {res};")
1877 if not value.mtype.is_c_primitive then
1878 self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
1879 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
1880 value.mtype.as(MClassType).name != "NativeString" then
1881 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1882 else
1883 self.require_declaration("type_{value.mtype.c_name}")
1884 self.add "{res} = type_{value.mtype.c_name}.name;"
1885 end
1886 return res
1887 end
1888
1889 redef fun equal_test(value1, value2)
1890 do
1891 var res = self.new_var(bool_type)
1892 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1893 var tmp = value1
1894 value1 = value2
1895 value2 = tmp
1896 end
1897 if value1.mtype.is_c_primitive then
1898 if value2.mtype == value1.mtype then
1899 self.add("{res} = {value1} == {value2};")
1900 else if value2.mtype.is_c_primitive then
1901 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1902 else if value1.mtype.is_tagged then
1903 self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});")
1904 else
1905 var mtype1 = value1.mtype.as(MClassType)
1906 self.require_declaration("class_{mtype1.c_name}")
1907 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
1908 self.add("if ({res}) \{")
1909 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1910 self.add("\}")
1911 end
1912 return res
1913 end
1914 var maybe_null = true
1915 var test = new Array[String]
1916 var t1 = value1.mcasttype
1917 if t1 isa MNullableType then
1918 test.add("{value1} != NULL")
1919 t1 = t1.mtype
1920 else
1921 maybe_null = false
1922 end
1923 var t2 = value2.mcasttype
1924 if t2 isa MNullableType then
1925 test.add("{value2} != NULL")
1926 t2 = t2.mtype
1927 else
1928 maybe_null = false
1929 end
1930
1931 var incompatible = false
1932 var primitive
1933 if t1.is_c_primitive then
1934 primitive = t1
1935 if t1 == t2 then
1936 # No need to compare class
1937 else if t2.is_c_primitive then
1938 incompatible = true
1939 else if can_be_primitive(value2) then
1940 if t1.is_tagged then
1941 self.add("{res} = {value1} == {value2};")
1942 return res
1943 end
1944 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1945 test.add("(!{extract_tag(value2)})")
1946 end
1947 test.add("{value1}->class == {value2}->class")
1948 else
1949 incompatible = true
1950 end
1951 else if t2.is_c_primitive then
1952 primitive = t2
1953 if can_be_primitive(value1) then
1954 if t2.is_tagged then
1955 self.add("{res} = {value1} == {value2};")
1956 return res
1957 end
1958 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1959 test.add("(!{extract_tag(value1)})")
1960 end
1961 test.add("{value1}->class == {value2}->class")
1962 else
1963 incompatible = true
1964 end
1965 else
1966 primitive = null
1967 end
1968
1969 if incompatible then
1970 if maybe_null then
1971 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
1972 return res
1973 else
1974 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
1975 return res
1976 end
1977 end
1978 if primitive != null then
1979 if primitive.is_tagged then
1980 self.add("{res} = {value1} == {value2};")
1981 return res
1982 end
1983 test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
1984 else if can_be_primitive(value1) and can_be_primitive(value2) then
1985 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1986 test.add("(!{extract_tag(value1)}) && (!{extract_tag(value2)})")
1987 end
1988 test.add("{value1}->class == {value2}->class")
1989 var s = new Array[String]
1990 for t, v in self.compiler.box_kinds do
1991 if t.mclass_type.is_tagged then continue
1992 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
1993 end
1994 if s.is_empty then
1995 self.add("{res} = {value1} == {value2};")
1996 return res
1997 end
1998 test.add("({s.join(" || ")})")
1999 else
2000 self.add("{res} = {value1} == {value2};")
2001 return res
2002 end
2003 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
2004 return res
2005 end
2006
2007 fun can_be_primitive(value: RuntimeVariable): Bool
2008 do
2009 var t = value.mcasttype.as_notnullable
2010 if not t isa MClassType then return false
2011 var k = t.mclass.kind
2012 return k == interface_kind or t.is_c_primitive
2013 end
2014
2015 fun maybe_null(value: RuntimeVariable): Bool
2016 do
2017 var t = value.mcasttype
2018 return t isa MNullableType or t isa MNullType
2019 end
2020
2021 redef fun array_instance(array, elttype)
2022 do
2023 var nclass = mmodule.native_array_class
2024 var arrayclass = mmodule.array_class
2025 var arraytype = arrayclass.get_mtype([elttype])
2026 var res = self.init_instance(arraytype)
2027 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
2028 var length = self.int_instance(array.length)
2029 var nat = native_array_instance(elttype, length)
2030 for i in [0..array.length[ do
2031 var r = self.autobox(array[i], self.object_type)
2032 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
2033 end
2034 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
2035 self.add("\}")
2036 return res
2037 end
2038
2039 redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
2040 do
2041 var mtype = mmodule.native_array_type(elttype)
2042 self.require_declaration("NEW_{mtype.mclass.c_name}")
2043 assert mtype isa MGenericType
2044 var compiler = self.compiler
2045 if mtype.need_anchor then
2046 hardening_live_open_type(mtype)
2047 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
2048 var recv = self.frame.arguments.first
2049 var recv_type_info = self.type_info(recv)
2050 self.require_declaration(mtype.const_color)
2051 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
2052 end
2053 compiler.undead_types.add(mtype)
2054 self.require_declaration("type_{mtype.c_name}")
2055 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
2056 end
2057
2058 redef fun native_array_def(pname, ret_type, arguments)
2059 do
2060 var elttype = arguments.first.mtype
2061 var nclass = mmodule.native_array_class
2062 var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
2063 if pname == "[]" then
2064 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2065 var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
2066 res.mcasttype = ret_type.as(not null)
2067 self.ret(res)
2068 return
2069 else if pname == "[]=" then
2070 self.add("{recv}[{arguments[1]}]={arguments[2]};")
2071 return
2072 else if pname == "length" then
2073 self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
2074 return
2075 else if pname == "copy_to" then
2076 var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
2077 self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
2078 return
2079 end
2080 end
2081
2082 redef fun native_array_get(nat, i)
2083 do
2084 var nclass = mmodule.native_array_class
2085 var recv = "((struct instance_{nclass.c_name}*){nat})->values"
2086 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2087 var res = self.new_expr("{recv}[{i}]", compiler.mainmodule.object_type)
2088 return res
2089 end
2090
2091 redef fun native_array_set(nat, i, val)
2092 do
2093 var nclass = mmodule.native_array_class
2094 var recv = "((struct instance_{nclass.c_name}*){nat})->values"
2095 self.add("{recv}[{i}]={val};")
2096 end
2097
2098 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
2099 assert mtype.need_anchor
2100 var compiler = self.compiler
2101 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
2102 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
2103 end
2104 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
2105 end
2106 end
2107
2108 redef class MMethodDef
2109 # The C function associated to a mmethoddef
2110 fun separate_runtime_function: SeparateRuntimeFunction
2111 do
2112 var res = self.separate_runtime_function_cache
2113 if res == null then
2114 var recv = mclassdef.bound_mtype
2115 var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
2116 res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
2117 self.separate_runtime_function_cache = res
2118 end
2119 return res
2120 end
2121 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
2122
2123 # The C function associated to a mmethoddef, that can be stored into a VFT of a class
2124 # The first parameter (the reciever) is always typed by val* in order to accept an object value
2125 # The C-signature is always compatible with the intro
2126 fun virtual_runtime_function: SeparateRuntimeFunction
2127 do
2128 var res = self.virtual_runtime_function_cache
2129 if res == null then
2130 # Because the function is virtual, the signature must match the one of the original class
2131 var intromclassdef = mproperty.intro.mclassdef
2132 var recv = intromclassdef.bound_mtype
2133
2134 res = separate_runtime_function
2135 if res.called_recv == recv then
2136 self.virtual_runtime_function_cache = res
2137 return res
2138 end
2139
2140 var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
2141
2142 if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
2143 self.virtual_runtime_function_cache = res
2144 return res
2145 end
2146
2147 res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
2148 self.virtual_runtime_function_cache = res
2149 res.is_thunk = true
2150 end
2151 return res
2152 end
2153 private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
2154 end
2155
2156 redef class MSignature
2157 # Does the C-version of `self` the same than the C-version of `other`?
2158 fun c_equiv(other: MSignature): Bool
2159 do
2160 if self == other then return true
2161 if arity != other.arity then return false
2162 for i in [0..arity[ do
2163 if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
2164 end
2165 if return_mtype != other.return_mtype then
2166 if return_mtype == null or other.return_mtype == null then return false
2167 if return_mtype.ctype != other.return_mtype.ctype then return false
2168 end
2169 return true
2170 end
2171 end
2172
2173 # The C function associated to a methoddef separately compiled
2174 class SeparateRuntimeFunction
2175 super AbstractRuntimeFunction
2176
2177 # The call-side static receiver
2178 var called_recv: MType
2179
2180 # The call-side static signature
2181 var called_signature: MSignature
2182
2183 # The name on the compiled method
2184 redef var build_c_name: String
2185
2186 # Statically call the original body instead
2187 var is_thunk = false
2188
2189 redef fun to_s do return self.mmethoddef.to_s
2190
2191 # The C return type (something or `void`)
2192 var c_ret: String is lazy do
2193 var ret = called_signature.return_mtype
2194 if ret != null then
2195 return ret.ctype
2196 else
2197 return "void"
2198 end
2199 end
2200
2201 # The C signature (only the parmeter part)
2202 var c_sig: String is lazy do
2203 var sig = new FlatBuffer
2204 sig.append("({called_recv.ctype} self")
2205 for i in [0..called_signature.arity[ do
2206 var mtype = called_signature.mparameters[i].mtype
2207 if i == called_signature.vararg_rank then
2208 mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
2209 end
2210 sig.append(", {mtype.ctype} p{i}")
2211 end
2212 sig.append(")")
2213 return sig.to_s
2214 end
2215
2216 # The C type for the function pointer.
2217 var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
2218
2219 # The arguments, as generated by `compile_to_c`
2220 private var arguments: Array[RuntimeVariable] is noinit
2221
2222 redef fun compile_to_c(compiler)
2223 do
2224 var mmethoddef = self.mmethoddef
2225
2226 var recv = self.mmethoddef.mclassdef.bound_mtype
2227 var v = compiler.new_visitor
2228 var selfvar = new RuntimeVariable("self", called_recv, recv)
2229 var arguments = new Array[RuntimeVariable]
2230 var frame = new StaticFrame(v, mmethoddef, recv, arguments)
2231 v.frame = frame
2232
2233 var msignature = called_signature
2234 var ret = called_signature.return_mtype
2235
2236 var sig = new FlatBuffer
2237 var comment = new FlatBuffer
2238 sig.append(c_ret)
2239 sig.append(" ")
2240 sig.append(self.c_name)
2241 sig.append(c_sig)
2242 comment.append("({selfvar}: {selfvar.mtype}")
2243 arguments.add(selfvar)
2244 for i in [0..msignature.arity[ do
2245 var mtype = msignature.mparameters[i].mtype
2246 if i == msignature.vararg_rank then
2247 mtype = v.mmodule.array_type(mtype)
2248 end
2249 comment.append(", {mtype}")
2250 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
2251 arguments.add(argvar)
2252 end
2253 comment.append(")")
2254 if ret != null then
2255 comment.append(": {ret}")
2256 end
2257 compiler.provide_declaration(self.c_name, "{sig};")
2258 self.arguments = arguments.to_a
2259
2260 v.add_decl("/* method {self} for {comment} */")
2261 v.add_decl("{sig} \{")
2262 if ret != null then
2263 frame.returnvar = v.new_var(ret)
2264 end
2265 frame.returnlabel = v.get_name("RET_LABEL")
2266
2267 if is_thunk then
2268 var subret = v.call(mmethoddef, recv, arguments)
2269 if ret != null then
2270 assert subret != null
2271 v.assign(frame.returnvar.as(not null), subret)
2272 end
2273 else
2274 mmethoddef.compile_inside_to_c(v, arguments)
2275 end
2276
2277 v.add("{frame.returnlabel.as(not null)}:;")
2278 if ret != null then
2279 v.add("return {frame.returnvar.as(not null)};")
2280 end
2281 v.add("\}")
2282 compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
2283 end
2284
2285 # Compile the trampolines used to implement late-binding.
2286 #
2287 # See `opt_trampoline_call`.
2288 fun compile_trampolines(compiler: SeparateCompiler)
2289 do
2290 var recv = self.mmethoddef.mclassdef.bound_mtype
2291 var selfvar = arguments.first
2292 var ret = called_signature.return_mtype
2293
2294 if mmethoddef.is_intro and not recv.is_c_primitive then
2295 var m = mmethoddef.mproperty
2296 var n2 = "CALL_" + m.const_color
2297 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2298 var v2 = compiler.new_visitor
2299 v2.add "{c_ret} {n2}{c_sig} \{"
2300 v2.require_declaration(m.const_color)
2301 var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
2302 if ret != null then
2303 v2.add "return {call}"
2304 else
2305 v2.add call
2306 end
2307
2308 v2.add "\}"
2309
2310 end
2311 if mmethoddef.has_supercall and not recv.is_c_primitive then
2312 var m = mmethoddef
2313 var n2 = "CALL_" + m.const_color
2314 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2315 var v2 = compiler.new_visitor
2316 v2.add "{c_ret} {n2}{c_sig} \{"
2317 v2.require_declaration(m.const_color)
2318 var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
2319 if ret != null then
2320 v2.add "return {call}"
2321 else
2322 v2.add call
2323 end
2324
2325 v2.add "\}"
2326 end
2327 end
2328 end
2329
2330 redef class MType
2331 # Are values of `self` tagged?
2332 # If false, it means that the type is not primitive, or is boxed.
2333 var is_tagged = false
2334 end
2335
2336 redef class MEntity
2337 var const_color: String is lazy do return "COLOR_{c_name}"
2338 end
2339
2340 interface PropertyLayoutElement end
2341
2342 redef class MProperty
2343 super PropertyLayoutElement
2344 end
2345
2346 redef class MPropDef
2347 super PropertyLayoutElement
2348 end
2349
2350 redef class AMethPropdef
2351 # The semi-global compilation does not support inlining calls to extern news
2352 redef fun can_inline
2353 do
2354 var m = mpropdef
2355 if m != null and m.mproperty.is_init and m.is_extern then return false
2356 return super
2357 end
2358 end
2359
2360 redef class AAttrPropdef
2361 redef fun init_expr(v, recv)
2362 do
2363 super
2364 if is_lazy and v.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
2365 var guard = self.mlazypropdef.mproperty
2366 v.write_attribute(guard, recv, v.bool_instance(false))
2367 end
2368 end
2369 end