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