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