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