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