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