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