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