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