sepcomp: `compute_resolution_tables` group by classes
[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
460 # Compute colors
461 var poset = poset_from_mtypes(live_types, live_cast_types)
462 var colorer = new POSetColorer[MType]
463 colorer.colorize(poset)
464 type_ids = colorer.ids
465 type_colors = colorer.colors
466 type_tables = build_type_tables(poset)
467
468 # VT and FT are stored with other unresolved types in the big resolution_tables
469 self.compute_resolution_tables(live_types)
470
471 return poset
472 end
473
474 private fun poset_from_mtypes(mtypes, cast_types: Set[MType]): POSet[MType] do
475 var poset = new POSet[MType]
476
477 # Instead of doing the full matrix mtypes X cast_types,
478 # a grouping is done by the base classes of the type so
479 # that we compare only types whose base classes are in inheritance.
480
481 var mtypes_by_class = new MultiHashMap[MClass, MType]
482 for e in mtypes do
483 var c = e.as_notnullable.as(MClassType).mclass
484 mtypes_by_class[c].add(e)
485 poset.add_node(e)
486 end
487
488 var casttypes_by_class = new MultiHashMap[MClass, MType]
489 for e in cast_types do
490 var c = e.as_notnullable.as(MClassType).mclass
491 casttypes_by_class[c].add(e)
492 poset.add_node(e)
493 end
494
495 for c1, ts1 in mtypes_by_class do
496 for c2 in c1.in_hierarchy(mainmodule).greaters do
497 var ts2 = casttypes_by_class[c2]
498 for e in ts1 do
499 for o in ts2 do
500 if e == o then continue
501 if e.is_subtype(mainmodule, null, o) then
502 poset.add_edge(e, o)
503 end
504 end
505 end
506 end
507 end
508 return poset
509 end
510
511 # Build type tables
512 fun build_type_tables(mtypes: POSet[MType]): Map[MType, Array[nullable MType]] do
513 var tables = new HashMap[MType, Array[nullable MType]]
514 for mtype in mtypes do
515 var table = new Array[nullable MType]
516 for sup in mtypes[mtype].greaters do
517 var color = type_colors[sup]
518 if table.length <= color then
519 for i in [table.length .. color[ do
520 table[i] = null
521 end
522 end
523 table[color] = sup
524 end
525 tables[mtype] = table
526 end
527 return tables
528 end
529
530 # resolution_tables is used to perform a type resolution at runtime in O(1)
531 private fun compute_resolution_tables(mtypes: Set[MType]) do
532 # During the visit of the body of classes, live_unresolved_types are collected
533 # and associated to
534 # Collect all live_unresolved_types (visited in the body of classes)
535
536 # Determinate fo each livetype what are its possible requested anchored types
537 var mtype2unresolved = new HashMap[MClass, Set[MType]]
538 for mtype in self.runtime_type_analysis.live_types do
539 var mclass = mtype.mclass
540 var set = mtype2unresolved.get_or_null(mclass)
541 if set == null then
542 set = new HashSet[MType]
543 mtype2unresolved[mclass] = set
544 end
545 for cd in mtype.collect_mclassdefs(self.mainmodule) do
546 if self.live_unresolved_types.has_key(cd) then
547 set.add_all(self.live_unresolved_types[cd])
548 end
549 end
550 end
551
552 # Compute the table layout with the prefered method
553 var colorer = new BucketsColorer[MClass, MType]
554
555 opentype_colors = colorer.colorize(mtype2unresolved)
556 resolution_tables = self.build_resolution_tables(self.runtime_type_analysis.live_types, mtype2unresolved)
557
558 # Compile a C constant for each collected unresolved type.
559 # Either to a color, or to -1 if the unresolved type is dead (no live receiver can require it)
560 var all_unresolved = new HashSet[MType]
561 for t in self.live_unresolved_types.values do
562 all_unresolved.add_all(t)
563 end
564 var all_unresolved_types_colors = new HashMap[MType, Int]
565 for t in all_unresolved do
566 if opentype_colors.has_key(t) then
567 all_unresolved_types_colors[t] = opentype_colors[t]
568 else
569 all_unresolved_types_colors[t] = -1
570 end
571 end
572 self.compile_color_consts(all_unresolved_types_colors)
573
574 #print "tables"
575 #for k, v in unresolved_types_tables.as(not null) do
576 # print "{k}: {v.join(", ")}"
577 #end
578 #print ""
579 end
580
581 fun build_resolution_tables(elements: Set[MClassType], map: Map[MClass, Set[MType]]): Map[MClassType, Array[nullable MType]] do
582 var tables = new HashMap[MClassType, Array[nullable MType]]
583 for mclasstype in elements do
584 var mtypes = map[mclasstype.mclass]
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 v = new_visitor
798
799 var rta = runtime_type_analysis
800 var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" and mclass.name != "Pointer"
801
802 v.add_decl("/* runtime class {c_name} */")
803
804 # Build class vft
805 if not is_dead then
806 self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
807 v.add_decl("const struct class class_{c_name} = \{")
808 v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
809 v.add_decl("\{")
810 var vft = self.method_tables.get_or_null(mclass)
811 if vft != null then for i in [0 .. vft.length[ do
812 var mpropdef = vft[i]
813 if mpropdef == null then
814 v.add_decl("NULL, /* empty */")
815 else
816 assert mpropdef isa MMethodDef
817 if rta != null and not rta.live_methoddefs.has(mpropdef) then
818 v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
819 continue
820 end
821 var rf = mpropdef.virtual_runtime_function
822 v.require_declaration(rf.c_name)
823 v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
824 end
825 end
826 v.add_decl("\}")
827 v.add_decl("\};")
828 end
829
830 if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
831 # Is a primitive type or the Pointer class, not any other extern class
832
833 if mtype.is_tagged then return
834
835 #Build instance struct
836 self.header.add_decl("struct instance_{c_name} \{")
837 self.header.add_decl("const struct type *type;")
838 self.header.add_decl("const struct class *class;")
839 self.header.add_decl("{mtype.ctype_extern} value;")
840 self.header.add_decl("\};")
841
842 if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
843
844 #Build BOX
845 self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
846 v.add_decl("/* allocate {mtype} */")
847 v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
848 v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
849 v.compiler.undead_types.add(mtype)
850 v.require_declaration("type_{c_name}")
851 v.add("res->type = &type_{c_name};")
852 v.require_declaration("class_{c_name}")
853 v.add("res->class = &class_{c_name};")
854 v.add("res->value = value;")
855 v.add("return (val*)res;")
856 v.add("\}")
857
858 if mtype.mclass.name != "Pointer" then return
859
860 v = new_visitor
861 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
862 v.add_decl("/* allocate {mtype} */")
863 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
864 if is_dead then
865 v.add_abort("{mclass} is DEAD")
866 else
867 var res = v.new_named_var(mtype, "self")
868 res.is_exact = true
869 v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
870 v.add("{res}->type = type;")
871 hardening_live_type(v, "type")
872 v.require_declaration("class_{c_name}")
873 v.add("{res}->class = &class_{c_name};")
874 v.add("((struct instance_{mtype.c_name}*){res})->value = NULL;")
875 v.add("return {res};")
876 end
877 v.add("\}")
878 return
879 else if mclass.name == "NativeArray" then
880 #Build instance struct
881 self.header.add_decl("struct instance_{c_name} \{")
882 self.header.add_decl("const struct type *type;")
883 self.header.add_decl("const struct class *class;")
884 # NativeArrays are just a instance header followed by a length and an array of values
885 self.header.add_decl("int length;")
886 self.header.add_decl("val* values[0];")
887 self.header.add_decl("\};")
888
889 #Build NEW
890 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
891 v.add_decl("/* allocate {mtype} */")
892 v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
893 var res = v.get_name("self")
894 v.add_decl("struct instance_{c_name} *{res};")
895 var mtype_elt = mtype.arguments.first
896 v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
897 v.add("{res}->type = type;")
898 hardening_live_type(v, "type")
899 v.require_declaration("class_{c_name}")
900 v.add("{res}->class = &class_{c_name};")
901 v.add("{res}->length = length;")
902 v.add("return (val*){res};")
903 v.add("\}")
904 return
905 else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
906 # Is an extern class (other than Pointer and NativeString)
907 # Pointer is caught in a previous `if`, and NativeString is internal
908
909 var pointer_type = mainmodule.pointer_type
910
911 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
912 v.add_decl("/* allocate {mtype} */")
913 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
914 if is_dead then
915 v.add_abort("{mclass} is DEAD")
916 else
917 var res = v.new_named_var(mtype, "self")
918 res.is_exact = true
919 v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
920 v.add("{res}->type = type;")
921 hardening_live_type(v, "type")
922 v.require_declaration("class_{c_name}")
923 v.add("{res}->class = &class_{c_name};")
924 v.add("((struct instance_{pointer_type.c_name}*){res})->value = NULL;")
925 v.add("return {res};")
926 end
927 v.add("\}")
928 return
929 end
930
931 #Build NEW
932 self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
933 v.add_decl("/* allocate {mtype} */")
934 v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
935 if is_dead then
936 v.add_abort("{mclass} is DEAD")
937 else
938 var res = v.new_named_var(mtype, "self")
939 res.is_exact = true
940 var attrs = self.attr_tables.get_or_null(mclass)
941 if attrs == null then
942 v.add("{res} = nit_alloc(sizeof(struct instance));")
943 else
944 v.add("{res} = nit_alloc(sizeof(struct instance) + {attrs.length}*sizeof(nitattribute_t));")
945 end
946 v.add("{res}->type = type;")
947 hardening_live_type(v, "type")
948 v.require_declaration("class_{c_name}")
949 v.add("{res}->class = &class_{c_name};")
950 if attrs != null then
951 self.generate_init_attr(v, res, mtype)
952 v.set_finalizer res
953 end
954 v.add("return {res};")
955 end
956 v.add("\}")
957 end
958
959 # Compile structures used to map tagged primitive values to their classes and types.
960 # This method also determines which class will be tagged.
961 fun compile_class_infos
962 do
963 if modelbuilder.toolcontext.opt_no_tag_primitives.value then return
964
965 # Note: if you change the tagging scheme, do not forget to update
966 # `autobox` and `extract_tag`
967 var class_info = new Array[nullable MClass].filled_with(null, 4)
968 for t in box_kinds.keys do
969 # Note: a same class can be associated to multiple slots if one want to
970 # use some Huffman coding.
971 if t.name == "Int" then
972 class_info[1] = t
973 else if t.name == "Char" then
974 class_info[2] = t
975 else if t.name == "Bool" then
976 class_info[3] = t
977 else
978 continue
979 end
980 t.mclass_type.is_tagged = true
981 end
982
983 # Compile the table for classes. The tag is used as an index
984 var v = self.new_visitor
985 v.add_decl "const struct class *class_info[4] = \{"
986 for t in class_info do
987 if t == null then
988 v.add_decl("NULL,")
989 else
990 var s = "class_{t.c_name}"
991 v.require_declaration(s)
992 v.add_decl("&{s},")
993 end
994 end
995 v.add_decl("\};")
996
997 # Compile the table for types. The tag is used as an index
998 v.add_decl "const struct type *type_info[4] = \{"
999 for t in class_info do
1000 if t == null then
1001 v.add_decl("NULL,")
1002 else
1003 var s = "type_{t.c_name}"
1004 undead_types.add(t.mclass_type)
1005 v.require_declaration(s)
1006 v.add_decl("&{s},")
1007 end
1008 end
1009 v.add_decl("\};")
1010 end
1011
1012 # Add a dynamic test to ensure that the type referenced by `t` is a live type
1013 fun hardening_live_type(v: VISITOR, t: String)
1014 do
1015 if not v.compiler.modelbuilder.toolcontext.opt_hardening.value then return
1016 v.add("if({t} == NULL) \{")
1017 v.add_abort("type null")
1018 v.add("\}")
1019 v.add("if({t}->table_size == 0) \{")
1020 v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
1021 v.add_abort("type dead")
1022 v.add("\}")
1023 end
1024
1025 redef fun new_visitor do return new SeparateCompilerVisitor(self)
1026
1027 # Stats
1028
1029 private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
1030 private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
1031 protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
1032 protected var attr_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
1033
1034 redef fun display_stats
1035 do
1036 super
1037 if self.modelbuilder.toolcontext.opt_tables_metrics.value then
1038 display_sizes
1039 end
1040 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1041 display_isset_checks
1042 end
1043 var tc = self.modelbuilder.toolcontext
1044 tc.info("# implementation of method invocation",2)
1045 var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
1046 tc.info("total number of invocations: {nb_invok_total}",2)
1047 tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
1048 tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
1049 tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
1050 end
1051
1052 fun display_sizes
1053 do
1054 print "# size of subtyping tables"
1055 print "\ttotal \tholes"
1056 var total = 0
1057 var holes = 0
1058 for t, table in type_tables do
1059 total += table.length
1060 for e in table do if e == null then holes += 1
1061 end
1062 print "\t{total}\t{holes}"
1063
1064 print "# size of resolution tables"
1065 print "\ttotal \tholes"
1066 total = 0
1067 holes = 0
1068 for t, table in resolution_tables do
1069 total += table.length
1070 for e in table do if e == null then holes += 1
1071 end
1072 print "\t{total}\t{holes}"
1073
1074 print "# size of methods tables"
1075 print "\ttotal \tholes"
1076 total = 0
1077 holes = 0
1078 for t, table in method_tables do
1079 total += table.length
1080 for e in table do if e == null then holes += 1
1081 end
1082 print "\t{total}\t{holes}"
1083
1084 print "# size of attributes tables"
1085 print "\ttotal \tholes"
1086 total = 0
1087 holes = 0
1088 for t, table in attr_tables do
1089 total += table.length
1090 for e in table do if e == null then holes += 1
1091 end
1092 print "\t{total}\t{holes}"
1093 end
1094
1095 protected var isset_checks_count = 0
1096 protected var attr_read_count = 0
1097
1098 fun display_isset_checks do
1099 print "# total number of compiled attribute reads"
1100 print "\t{attr_read_count}"
1101 print "# total number of compiled isset-checks"
1102 print "\t{isset_checks_count}"
1103 end
1104
1105 redef fun compile_nitni_structs
1106 do
1107 self.header.add_decl """
1108 struct nitni_instance \{
1109 struct nitni_instance *next,
1110 *prev; /* adjacent global references in global list */
1111 int count; /* number of time this global reference has been marked */
1112 struct instance *value;
1113 \};
1114 """
1115 super
1116 end
1117
1118 redef fun finalize_ffi_for_module(mmodule)
1119 do
1120 var old_module = self.mainmodule
1121 self.mainmodule = mmodule
1122 super
1123 self.mainmodule = old_module
1124 end
1125 end
1126
1127 # A visitor on the AST of property definition that generate the C code of a separate compilation process.
1128 class SeparateCompilerVisitor
1129 super AbstractCompilerVisitor
1130
1131 redef type COMPILER: SeparateCompiler
1132
1133 redef fun adapt_signature(m, args)
1134 do
1135 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1136 var recv = args.first
1137 if recv.mtype.ctype != m.mclassdef.mclass.mclass_type.ctype then
1138 args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
1139 end
1140 for i in [0..msignature.arity[ do
1141 var t = msignature.mparameters[i].mtype
1142 if i == msignature.vararg_rank then
1143 t = args[i+1].mtype
1144 end
1145 args[i+1] = self.autobox(args[i+1], t)
1146 end
1147 end
1148
1149 redef fun unbox_signature_extern(m, args)
1150 do
1151 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1152 if not m.mproperty.is_init and m.is_extern then
1153 args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
1154 end
1155 for i in [0..msignature.arity[ do
1156 var t = msignature.mparameters[i].mtype
1157 if i == msignature.vararg_rank then
1158 t = args[i+1].mtype
1159 end
1160 if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
1161 end
1162 end
1163
1164 redef fun autobox(value, mtype)
1165 do
1166 if value.mtype == mtype then
1167 return value
1168 else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
1169 return value
1170 else if value.mtype.ctype == "val*" then
1171 if mtype.is_tagged then
1172 if mtype.name == "Int" then
1173 return self.new_expr("(long)({value})>>2", mtype)
1174 else if mtype.name == "Char" then
1175 return self.new_expr("(char)((long)({value})>>2)", mtype)
1176 else if mtype.name == "Bool" then
1177 return self.new_expr("(short int)((long)({value})>>2)", mtype)
1178 else
1179 abort
1180 end
1181 end
1182 return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
1183 else if mtype.ctype == "val*" then
1184 if value.mtype.is_tagged then
1185 if value.mtype.name == "Int" then
1186 return self.new_expr("(val*)({value}<<2|1)", mtype)
1187 else if value.mtype.name == "Char" then
1188 return self.new_expr("(val*)((long)({value})<<2|2)", mtype)
1189 else if value.mtype.name == "Bool" then
1190 return self.new_expr("(val*)((long)({value})<<2|3)", mtype)
1191 else
1192 abort
1193 end
1194 end
1195 var valtype = value.mtype.as(MClassType)
1196 if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
1197 valtype = compiler.mainmodule.pointer_type
1198 end
1199 var res = self.new_var(mtype)
1200 if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
1201 self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
1202 self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
1203 return res
1204 end
1205 self.require_declaration("BOX_{valtype.c_name}")
1206 self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
1207 return res
1208 else if (value.mtype.ctype == "void*" and mtype.ctype == "void*") or
1209 (value.mtype.ctype == "char*" and mtype.ctype == "void*") or
1210 (value.mtype.ctype == "void*" and mtype.ctype == "char*") then
1211 return value
1212 else
1213 # Bad things will appen!
1214 var res = self.new_var(mtype)
1215 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1216 self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); fatal_exit(1);")
1217 return res
1218 end
1219 end
1220
1221 redef fun unbox_extern(value, mtype)
1222 do
1223 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1224 mtype.mclass.name != "NativeString" then
1225 var pointer_type = compiler.mainmodule.pointer_type
1226 var res = self.new_var_extern(mtype)
1227 self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
1228 return res
1229 else
1230 return value
1231 end
1232 end
1233
1234 redef fun box_extern(value, mtype)
1235 do
1236 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1237 mtype.mclass.name != "NativeString" then
1238 var valtype = compiler.mainmodule.pointer_type
1239 var res = self.new_var(mtype)
1240 if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
1241 self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
1242 self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
1243 return res
1244 end
1245 self.require_declaration("BOX_{valtype.c_name}")
1246 self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
1247 self.require_declaration("type_{mtype.c_name}")
1248 self.add("{res}->type = &type_{mtype.c_name};")
1249 self.require_declaration("class_{mtype.c_name}")
1250 self.add("{res}->class = &class_{mtype.c_name};")
1251 return res
1252 else
1253 return value
1254 end
1255 end
1256
1257 # Returns a C expression containing the tag of the value as a long.
1258 #
1259 # If the C expression is evaluated to 0, it means there is no tag.
1260 # Thus the expression can be used as a condition.
1261 fun extract_tag(value: RuntimeVariable): String
1262 do
1263 assert value.mtype.ctype == "val*"
1264 return "((long){value}&3)" # Get the two low bits
1265 end
1266
1267 # Returns a C expression of the runtime class structure of the value.
1268 # The point of the method is to work also with primitive types.
1269 fun class_info(value: RuntimeVariable): String
1270 do
1271 if value.mtype.ctype == "val*" then
1272 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1273 var tag = extract_tag(value)
1274 return "({tag}?class_info[{tag}]:{value}->class)"
1275 end
1276 return "{value}->class"
1277 else
1278 compiler.undead_types.add(value.mtype)
1279 self.require_declaration("class_{value.mtype.c_name}")
1280 return "(&class_{value.mtype.c_name})"
1281 end
1282 end
1283
1284 # Returns a C expression of the runtime type structure of the value.
1285 # The point of the method is to work also with primitive types.
1286 fun type_info(value: RuntimeVariable): String
1287 do
1288 if value.mtype.ctype == "val*" then
1289 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1290 var tag = extract_tag(value)
1291 return "({tag}?type_info[{tag}]:{value}->type)"
1292 end
1293 return "{value}->type"
1294 else
1295 compiler.undead_types.add(value.mtype)
1296 self.require_declaration("type_{value.mtype.c_name}")
1297 return "(&type_{value.mtype.c_name})"
1298 end
1299 end
1300
1301 redef fun compile_callsite(callsite, args)
1302 do
1303 var rta = compiler.runtime_type_analysis
1304 # TODO: Inlining of new-style constructors with initializers
1305 if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
1306 var tgs = rta.live_targets(callsite)
1307 if tgs.length == 1 then
1308 return direct_call(tgs.first, args)
1309 end
1310 end
1311 # Shortcut intern methods as they are not usually redefinable
1312 if callsite.mpropdef.is_intern and callsite.mproperty.name != "object_id" then
1313 # `object_id` is the only redefined intern method, so it can not be directly called.
1314 # TODO find a less ugly approach?
1315 return direct_call(callsite.mpropdef, args)
1316 end
1317 return super
1318 end
1319
1320 # Fully and directly call a mpropdef
1321 #
1322 # This method is used by `compile_callsite`
1323 private fun direct_call(mpropdef: MMethodDef, args: Array[RuntimeVariable]): nullable RuntimeVariable
1324 do
1325 var res0 = before_send(mpropdef.mproperty, args)
1326 var res = call(mpropdef, mpropdef.mclassdef.bound_mtype, args)
1327 if res0 != null then
1328 assert res != null
1329 self.assign(res0, res)
1330 res = res0
1331 end
1332 add("\}") # close the before_send
1333 return res
1334 end
1335 redef fun send(mmethod, arguments)
1336 do
1337 if arguments.first.mcasttype.ctype != "val*" then
1338 # In order to shortcut the primitive, we need to find the most specific method
1339 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1340 var m = self.compiler.mainmodule
1341 self.compiler.mainmodule = self.compiler.realmainmodule
1342 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1343 self.compiler.mainmodule = m
1344 return res
1345 end
1346
1347 return table_send(mmethod, arguments, mmethod)
1348 end
1349
1350 # Handle common special cases before doing the effective method invocation
1351 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1352 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1353 # Client must not forget to close the } after them.
1354 #
1355 # The value returned is the result of the common special cases.
1356 # If not null, client must compile it with the result of their own effective method invocation.
1357 #
1358 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1359 # is generated to cancel the effective method invocation that will follow
1360 # TODO: find a better approach
1361 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1362 do
1363 var res: nullable RuntimeVariable = null
1364 var recv = arguments.first
1365 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
1366 var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null
1367 if maybenull then
1368 self.add("if ({recv} == NULL) \{")
1369 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
1370 res = self.new_var(bool_type)
1371 var arg = arguments[1]
1372 if arg.mcasttype isa MNullableType then
1373 self.add("{res} = ({arg} == NULL);")
1374 else if arg.mcasttype isa MNullType then
1375 self.add("{res} = 1; /* is null */")
1376 else
1377 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1378 end
1379 else if mmethod.name == "!=" then
1380 res = self.new_var(bool_type)
1381 var arg = arguments[1]
1382 if arg.mcasttype isa MNullableType then
1383 self.add("{res} = ({arg} != NULL);")
1384 else if arg.mcasttype isa MNullType then
1385 self.add("{res} = 0; /* is null */")
1386 else
1387 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1388 end
1389 else
1390 self.add_abort("Receiver is null")
1391 end
1392 self.add("\} else \{")
1393 else
1394 self.add("\{")
1395 end
1396 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
1397 # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
1398 var arg = arguments[1]
1399 if arg.mcasttype isa MNullType then
1400 if res == null then res = self.new_var(bool_type)
1401 if mmethod.name == "!=" then
1402 self.add("{res} = 1; /* arg is null and recv is not */")
1403 else # `==` and `is_same_instance`
1404 self.add("{res} = 0; /* arg is null but recv is not */")
1405 end
1406 self.add("\}") # closes the null case
1407 self.add("if (0) \{") # what follow is useless, CC will drop it
1408 end
1409 end
1410 return res
1411 end
1412
1413 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
1414 do
1415 compiler.modelbuilder.nb_invok_by_tables += 1
1416 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1417
1418 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1419 var recv = arguments.first
1420
1421 var res0 = before_send(mmethod, arguments)
1422
1423 var runtime_function = mmethod.intro.virtual_runtime_function
1424 var msignature = runtime_function.called_signature
1425
1426 var res: nullable RuntimeVariable
1427 var ret = msignature.return_mtype
1428 if ret == null then
1429 res = null
1430 else
1431 res = self.new_var(ret)
1432 end
1433
1434 var ss = new FlatBuffer
1435
1436 ss.append("{recv}")
1437 for i in [0..msignature.arity[ do
1438 var a = arguments[i+1]
1439 var t = msignature.mparameters[i].mtype
1440 if i == msignature.vararg_rank then
1441 t = arguments[i+1].mcasttype
1442 end
1443 a = self.autobox(a, t)
1444 ss.append(", {a}")
1445 end
1446
1447 var const_color = mentity.const_color
1448 var ress
1449 if res != null then
1450 ress = "{res} = "
1451 else
1452 ress = ""
1453 end
1454 if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_direct_call_monomorph0.value then
1455 # opt_direct_call_monomorph0 is used to compare the efficiency of the alternative lookup implementation, ceteris paribus.
1456 # The difference with the non-zero option is that the monomorphism is looked-at on the mmethod level and not at the callsite level.
1457 # TODO: remove this mess and use per callsite service to detect monomorphism in a single place.
1458 var md = compiler.is_monomorphic(mentity)
1459 if md != null then
1460 var callsym = md.virtual_runtime_function.c_name
1461 self.require_declaration(callsym)
1462 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1463 else
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 end
1467 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_guard_call.value then
1468 var callsym = "CALL_" + const_color
1469 self.require_declaration(callsym)
1470 self.add "if (!{callsym}) \{"
1471 self.require_declaration(const_color)
1472 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1473 self.add "\} else \{"
1474 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1475 self.add "\}"
1476 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
1477 var callsym = "CALL_" + const_color
1478 self.require_declaration(callsym)
1479 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1480 else
1481 self.require_declaration(const_color)
1482 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1483 end
1484
1485 if res0 != null then
1486 assert res != null
1487 assign(res0,res)
1488 res = res0
1489 end
1490
1491 self.add("\}") # closes the null case
1492
1493 return res
1494 end
1495
1496 redef fun call(mmethoddef, recvtype, arguments)
1497 do
1498 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1499
1500 var res: nullable RuntimeVariable
1501 var ret = mmethoddef.msignature.return_mtype
1502 if ret == null then
1503 res = null
1504 else
1505 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1506 res = self.new_var(ret)
1507 end
1508
1509 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1510 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1511 compiler.modelbuilder.nb_invok_by_inline += 1
1512 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1513 var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
1514 frame.returnlabel = self.get_name("RET_LABEL")
1515 frame.returnvar = res
1516 var old_frame = self.frame
1517 self.frame = frame
1518 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1519 mmethoddef.compile_inside_to_c(self, arguments)
1520 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1521 self.add("\}")
1522 self.frame = old_frame
1523 return res
1524 end
1525 compiler.modelbuilder.nb_invok_by_direct += 1
1526 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1527
1528 # Autobox arguments
1529 self.adapt_signature(mmethoddef, arguments)
1530
1531 self.require_declaration(mmethoddef.c_name)
1532 if res == null then
1533 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1534 return null
1535 else
1536 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1537 end
1538
1539 return res
1540 end
1541
1542 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1543 do
1544 if arguments.first.mcasttype.ctype != "val*" then
1545 # In order to shortcut the primitive, we need to find the most specific method
1546 # However, because of performance (no flattening), we always work on the realmainmodule
1547 var main = self.compiler.mainmodule
1548 self.compiler.mainmodule = self.compiler.realmainmodule
1549 var res = self.monomorphic_super_send(m, recvtype, arguments)
1550 self.compiler.mainmodule = main
1551 return res
1552 end
1553 return table_send(m.mproperty, arguments, m)
1554 end
1555
1556 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1557 do
1558 # A vararg must be stored into an new array
1559 # The trick is that the dymaic type of the array may depends on the receiver
1560 # of the method (ie recv) if the static type is unresolved
1561 # This is more complex than usual because the unresolved type must not be resolved
1562 # with the current receiver (ie self).
1563 # Therefore to isolate the resolution from self, a local StaticFrame is created.
1564 # One can see this implementation as an inlined method of the receiver whose only
1565 # job is to allocate the array
1566 var old_frame = self.frame
1567 var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1568 self.frame = frame
1569 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1570 var res = self.array_instance(varargs, elttype)
1571 self.frame = old_frame
1572 return res
1573 end
1574
1575 redef fun isset_attribute(a, recv)
1576 do
1577 self.check_recv_notnull(recv)
1578 var res = self.new_var(bool_type)
1579
1580 # What is the declared type of the attribute?
1581 var mtype = a.intro.static_mtype.as(not null)
1582 var intromclassdef = a.intro.mclassdef
1583 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1584
1585 if mtype isa MNullableType then
1586 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1587 return res
1588 end
1589
1590 self.require_declaration(a.const_color)
1591 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1592 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1593 else
1594
1595 if mtype.ctype == "val*" then
1596 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1597 else
1598 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1599 end
1600 end
1601 return res
1602 end
1603
1604 redef fun read_attribute(a, recv)
1605 do
1606 self.check_recv_notnull(recv)
1607
1608 # What is the declared type of the attribute?
1609 var ret = a.intro.static_mtype.as(not null)
1610 var intromclassdef = a.intro.mclassdef
1611 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1612
1613 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1614 self.compiler.attr_read_count += 1
1615 self.add("count_attr_reads++;")
1616 end
1617
1618 self.require_declaration(a.const_color)
1619 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1620 # Get the attribute or a box (ie. always a val*)
1621 var cret = self.object_type.as_nullable
1622 var res = self.new_var(cret)
1623 res.mcasttype = ret
1624
1625 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1626
1627 # Check for Uninitialized attribute
1628 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1629 self.add("if (unlikely({res} == NULL)) \{")
1630 self.add_abort("Uninitialized attribute {a.name}")
1631 self.add("\}")
1632
1633 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1634 self.compiler.isset_checks_count += 1
1635 self.add("count_isset_checks++;")
1636 end
1637 end
1638
1639 # Return the attribute or its unboxed version
1640 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1641 return self.autobox(res, ret)
1642 else
1643 var res = self.new_var(ret)
1644 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1645
1646 # Check for Uninitialized attribute
1647 if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1648 self.add("if (unlikely({res} == NULL)) \{")
1649 self.add_abort("Uninitialized attribute {a.name}")
1650 self.add("\}")
1651 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1652 self.compiler.isset_checks_count += 1
1653 self.add("count_isset_checks++;")
1654 end
1655 end
1656
1657 return res
1658 end
1659 end
1660
1661 redef fun write_attribute(a, recv, value)
1662 do
1663 self.check_recv_notnull(recv)
1664
1665 # What is the declared type of the attribute?
1666 var mtype = a.intro.static_mtype.as(not null)
1667 var intromclassdef = a.intro.mclassdef
1668 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1669
1670 # Adapt the value to the declared type
1671 value = self.autobox(value, mtype)
1672
1673 self.require_declaration(a.const_color)
1674 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1675 var attr = "{recv}->attrs[{a.const_color}]"
1676 if mtype.ctype != "val*" then
1677 assert mtype isa MClassType
1678 # The attribute is primitive, thus we store it in a box
1679 # The trick is to create the box the first time then resuse the box
1680 self.add("if ({attr} != NULL) \{")
1681 self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1682 self.add("\} else \{")
1683 value = self.autobox(value, self.object_type.as_nullable)
1684 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1685 self.add("\}")
1686 else
1687 # The attribute is not primitive, thus store it direclty
1688 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1689 end
1690 else
1691 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1692 end
1693 end
1694
1695 # Check that mtype is a live open type
1696 fun hardening_live_open_type(mtype: MType)
1697 do
1698 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1699 self.require_declaration(mtype.const_color)
1700 var col = mtype.const_color
1701 self.add("if({col} == -1) \{")
1702 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1703 self.add_abort("open type dead")
1704 self.add("\}")
1705 end
1706
1707 # Check that mtype it a pointer to a live cast type
1708 fun hardening_cast_type(t: String)
1709 do
1710 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1711 add("if({t} == NULL) \{")
1712 add_abort("cast type null")
1713 add("\}")
1714 add("if({t}->id == -1 || {t}->color == -1) \{")
1715 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1716 add_abort("cast type dead")
1717 add("\}")
1718 end
1719
1720 redef fun init_instance(mtype)
1721 do
1722 self.require_declaration("NEW_{mtype.mclass.c_name}")
1723 var compiler = self.compiler
1724 if mtype isa MGenericType and mtype.need_anchor then
1725 hardening_live_open_type(mtype)
1726 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1727 var recv = self.frame.arguments.first
1728 var recv_type_info = self.type_info(recv)
1729 self.require_declaration(mtype.const_color)
1730 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1731 end
1732 compiler.undead_types.add(mtype)
1733 self.require_declaration("type_{mtype.c_name}")
1734 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1735 end
1736
1737 redef fun type_test(value, mtype, tag)
1738 do
1739 self.add("/* {value.inspect} isa {mtype} */")
1740 var compiler = self.compiler
1741
1742 var recv = self.frame.arguments.first
1743 var recv_type_info = self.type_info(recv)
1744
1745 var res = self.new_var(bool_type)
1746
1747 var cltype = self.get_name("cltype")
1748 self.add_decl("int {cltype};")
1749 var idtype = self.get_name("idtype")
1750 self.add_decl("int {idtype};")
1751
1752 var maybe_null = self.maybe_null(value)
1753 var accept_null = "0"
1754 var ntype = mtype
1755 if ntype isa MNullableType then
1756 ntype = ntype.mtype
1757 accept_null = "1"
1758 end
1759
1760 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1761 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1762 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1763 self.compiler.count_type_test_skipped[tag] += 1
1764 self.add("count_type_test_skipped_{tag}++;")
1765 end
1766 return res
1767 end
1768
1769 if ntype.need_anchor then
1770 var type_struct = self.get_name("type_struct")
1771 self.add_decl("const struct type* {type_struct};")
1772
1773 # Either with resolution_table with a direct resolution
1774 hardening_live_open_type(mtype)
1775 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1776 self.require_declaration(mtype.const_color)
1777 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1778 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1779 self.compiler.count_type_test_unresolved[tag] += 1
1780 self.add("count_type_test_unresolved_{tag}++;")
1781 end
1782 hardening_cast_type(type_struct)
1783 self.add("{cltype} = {type_struct}->color;")
1784 self.add("{idtype} = {type_struct}->id;")
1785 if maybe_null and accept_null == "0" then
1786 var is_nullable = self.get_name("is_nullable")
1787 self.add_decl("short int {is_nullable};")
1788 self.add("{is_nullable} = {type_struct}->is_nullable;")
1789 accept_null = is_nullable.to_s
1790 end
1791 else if ntype isa MClassType then
1792 compiler.undead_types.add(mtype)
1793 self.require_declaration("type_{mtype.c_name}")
1794 hardening_cast_type("(&type_{mtype.c_name})")
1795 self.add("{cltype} = type_{mtype.c_name}.color;")
1796 self.add("{idtype} = type_{mtype.c_name}.id;")
1797 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1798 self.compiler.count_type_test_resolved[tag] += 1
1799 self.add("count_type_test_resolved_{tag}++;")
1800 end
1801 else
1802 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); fatal_exit(1);")
1803 end
1804
1805 # check color is in table
1806 if maybe_null then
1807 self.add("if({value} == NULL) \{")
1808 self.add("{res} = {accept_null};")
1809 self.add("\} else \{")
1810 end
1811 var value_type_info = self.type_info(value)
1812 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1813 self.add("{res} = 0;")
1814 self.add("\} else \{")
1815 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1816 self.add("\}")
1817 if maybe_null then
1818 self.add("\}")
1819 end
1820
1821 return res
1822 end
1823
1824 redef fun is_same_type_test(value1, value2)
1825 do
1826 var res = self.new_var(bool_type)
1827 # Swap values to be symetric
1828 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1829 var tmp = value1
1830 value1 = value2
1831 value2 = tmp
1832 end
1833 if value1.mtype.ctype != "val*" then
1834 if value2.mtype == value1.mtype then
1835 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1836 else if value2.mtype.ctype != "val*" then
1837 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1838 else
1839 var mtype1 = value1.mtype.as(MClassType)
1840 self.require_declaration("class_{mtype1.c_name}")
1841 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
1842 end
1843 else
1844 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
1845 end
1846 return res
1847 end
1848
1849 redef fun class_name_string(value)
1850 do
1851 var res = self.get_name("var_class_name")
1852 self.add_decl("const char* {res};")
1853 if value.mtype.ctype == "val*" then
1854 self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
1855 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
1856 value.mtype.as(MClassType).name != "NativeString" then
1857 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1858 else
1859 self.require_declaration("type_{value.mtype.c_name}")
1860 self.add "{res} = type_{value.mtype.c_name}.name;"
1861 end
1862 return res
1863 end
1864
1865 redef fun equal_test(value1, value2)
1866 do
1867 var res = self.new_var(bool_type)
1868 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1869 var tmp = value1
1870 value1 = value2
1871 value2 = tmp
1872 end
1873 if value1.mtype.ctype != "val*" then
1874 if value2.mtype == value1.mtype then
1875 self.add("{res} = {value1} == {value2};")
1876 else if value2.mtype.ctype != "val*" then
1877 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1878 else if value1.mtype.is_tagged then
1879 self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});")
1880 else
1881 var mtype1 = value1.mtype.as(MClassType)
1882 self.require_declaration("class_{mtype1.c_name}")
1883 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
1884 self.add("if ({res}) \{")
1885 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1886 self.add("\}")
1887 end
1888 return res
1889 end
1890 var maybe_null = true
1891 var test = new Array[String]
1892 var t1 = value1.mcasttype
1893 if t1 isa MNullableType then
1894 test.add("{value1} != NULL")
1895 t1 = t1.mtype
1896 else
1897 maybe_null = false
1898 end
1899 var t2 = value2.mcasttype
1900 if t2 isa MNullableType then
1901 test.add("{value2} != NULL")
1902 t2 = t2.mtype
1903 else
1904 maybe_null = false
1905 end
1906
1907 var incompatible = false
1908 var primitive
1909 if t1.ctype != "val*" then
1910 primitive = t1
1911 if t1 == t2 then
1912 # No need to compare class
1913 else if t2.ctype != "val*" then
1914 incompatible = true
1915 else if can_be_primitive(value2) then
1916 if t1.is_tagged then
1917 self.add("{res} = {value1} == {value2};")
1918 return res
1919 end
1920 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1921 test.add("(!{extract_tag(value2)})")
1922 end
1923 test.add("{value1}->class == {value2}->class")
1924 else
1925 incompatible = true
1926 end
1927 else if t2.ctype != "val*" then
1928 primitive = t2
1929 if can_be_primitive(value1) then
1930 if t2.is_tagged then
1931 self.add("{res} = {value1} == {value2};")
1932 return res
1933 end
1934 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1935 test.add("(!{extract_tag(value1)})")
1936 end
1937 test.add("{value1}->class == {value2}->class")
1938 else
1939 incompatible = true
1940 end
1941 else
1942 primitive = null
1943 end
1944
1945 if incompatible then
1946 if maybe_null then
1947 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
1948 return res
1949 else
1950 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
1951 return res
1952 end
1953 end
1954 if primitive != null then
1955 if primitive.is_tagged then
1956 self.add("{res} = {value1} == {value2};")
1957 return res
1958 end
1959 test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
1960 else if can_be_primitive(value1) and can_be_primitive(value2) then
1961 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1962 test.add("(!{extract_tag(value1)}) && (!{extract_tag(value2)})")
1963 end
1964 test.add("{value1}->class == {value2}->class")
1965 var s = new Array[String]
1966 for t, v in self.compiler.box_kinds do
1967 if t.mclass_type.is_tagged then continue
1968 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
1969 end
1970 if s.is_empty then
1971 self.add("{res} = {value1} == {value2};")
1972 return res
1973 end
1974 test.add("({s.join(" || ")})")
1975 else
1976 self.add("{res} = {value1} == {value2};")
1977 return res
1978 end
1979 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
1980 return res
1981 end
1982
1983 fun can_be_primitive(value: RuntimeVariable): Bool
1984 do
1985 var t = value.mcasttype.as_notnullable
1986 if not t isa MClassType then return false
1987 var k = t.mclass.kind
1988 return k == interface_kind or t.ctype != "val*"
1989 end
1990
1991 fun maybe_null(value: RuntimeVariable): Bool
1992 do
1993 var t = value.mcasttype
1994 return t isa MNullableType or t isa MNullType
1995 end
1996
1997 redef fun array_instance(array, elttype)
1998 do
1999 var nclass = self.get_class("NativeArray")
2000 var arrayclass = self.get_class("Array")
2001 var arraytype = arrayclass.get_mtype([elttype])
2002 var res = self.init_instance(arraytype)
2003 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
2004 var length = self.int_instance(array.length)
2005 var nat = native_array_instance(elttype, length)
2006 for i in [0..array.length[ do
2007 var r = self.autobox(array[i], self.object_type)
2008 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
2009 end
2010 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
2011 self.add("\}")
2012 return res
2013 end
2014
2015 redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
2016 do
2017 var mtype = self.get_class("NativeArray").get_mtype([elttype])
2018 self.require_declaration("NEW_{mtype.mclass.c_name}")
2019 assert mtype isa MGenericType
2020 var compiler = self.compiler
2021 if mtype.need_anchor then
2022 hardening_live_open_type(mtype)
2023 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
2024 var recv = self.frame.arguments.first
2025 var recv_type_info = self.type_info(recv)
2026 self.require_declaration(mtype.const_color)
2027 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
2028 end
2029 compiler.undead_types.add(mtype)
2030 self.require_declaration("type_{mtype.c_name}")
2031 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
2032 end
2033
2034 redef fun native_array_def(pname, ret_type, arguments)
2035 do
2036 var elttype = arguments.first.mtype
2037 var nclass = self.get_class("NativeArray")
2038 var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
2039 if pname == "[]" then
2040 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2041 var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
2042 res.mcasttype = ret_type.as(not null)
2043 self.ret(res)
2044 return
2045 else if pname == "[]=" then
2046 self.add("{recv}[{arguments[1]}]={arguments[2]};")
2047 return
2048 else if pname == "length" then
2049 self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
2050 return
2051 else if pname == "copy_to" then
2052 var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
2053 self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
2054 return
2055 end
2056 end
2057
2058 redef fun calloc_array(ret_type, arguments)
2059 do
2060 var mclass = self.get_class("ArrayCapable")
2061 var ft = mclass.mparameters.first
2062 var res = self.native_array_instance(ft, arguments[1])
2063 self.ret(res)
2064 end
2065
2066 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
2067 assert mtype.need_anchor
2068 var compiler = self.compiler
2069 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
2070 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
2071 end
2072 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
2073 end
2074 end
2075
2076 redef class MMethodDef
2077 # The C function associated to a mmethoddef
2078 fun separate_runtime_function: SeparateRuntimeFunction
2079 do
2080 var res = self.separate_runtime_function_cache
2081 if res == null then
2082 var recv = mclassdef.bound_mtype
2083 var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
2084 res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
2085 self.separate_runtime_function_cache = res
2086 end
2087 return res
2088 end
2089 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
2090
2091 # The C function associated to a mmethoddef, that can be stored into a VFT of a class
2092 # The first parameter (the reciever) is always typed by val* in order to accept an object value
2093 # The C-signature is always compatible with the intro
2094 fun virtual_runtime_function: SeparateRuntimeFunction
2095 do
2096 var res = self.virtual_runtime_function_cache
2097 if res == null then
2098 # Because the function is virtual, the signature must match the one of the original class
2099 var intromclassdef = mproperty.intro.mclassdef
2100 var recv = intromclassdef.bound_mtype
2101
2102 res = separate_runtime_function
2103 if res.called_recv == recv then
2104 self.virtual_runtime_function_cache = res
2105 return res
2106 end
2107
2108 var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
2109
2110 if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
2111 self.virtual_runtime_function_cache = res
2112 return res
2113 end
2114
2115 res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
2116 self.virtual_runtime_function_cache = res
2117 res.is_thunk = true
2118 end
2119 return res
2120 end
2121 private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
2122 end
2123
2124 redef class MSignature
2125 # Does the C-version of `self` the same than the C-version of `other`?
2126 fun c_equiv(other: MSignature): Bool
2127 do
2128 if self == other then return true
2129 if arity != other.arity then return false
2130 for i in [0..arity[ do
2131 if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
2132 end
2133 if return_mtype != other.return_mtype then
2134 if return_mtype == null or other.return_mtype == null then return false
2135 if return_mtype.ctype != other.return_mtype.ctype then return false
2136 end
2137 return true
2138 end
2139 end
2140
2141 # The C function associated to a methoddef separately compiled
2142 class SeparateRuntimeFunction
2143 super AbstractRuntimeFunction
2144
2145 # The call-side static receiver
2146 var called_recv: MType
2147
2148 # The call-side static signature
2149 var called_signature: MSignature
2150
2151 # The name on the compiled method
2152 redef var build_c_name: String
2153
2154 # Statically call the original body instead
2155 var is_thunk = false
2156
2157 redef fun to_s do return self.mmethoddef.to_s
2158
2159 # The C return type (something or `void`)
2160 var c_ret: String is lazy do
2161 var ret = called_signature.return_mtype
2162 if ret != null then
2163 return ret.ctype
2164 else
2165 return "void"
2166 end
2167 end
2168
2169 # The C signature (only the parmeter part)
2170 var c_sig: String is lazy do
2171 var sig = new FlatBuffer
2172 sig.append("({called_recv.ctype} self")
2173 for i in [0..called_signature.arity[ do
2174 var mtype = called_signature.mparameters[i].mtype
2175 if i == called_signature.vararg_rank then
2176 mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
2177 end
2178 sig.append(", {mtype.ctype} p{i}")
2179 end
2180 sig.append(")")
2181 return sig.to_s
2182 end
2183
2184 # The C type for the function pointer.
2185 var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
2186
2187 # The arguments, as generated by `compile_to_c`
2188 private var arguments: Array[RuntimeVariable] is noinit
2189
2190 redef fun compile_to_c(compiler)
2191 do
2192 var mmethoddef = self.mmethoddef
2193
2194 var recv = self.mmethoddef.mclassdef.bound_mtype
2195 var v = compiler.new_visitor
2196 var selfvar = new RuntimeVariable("self", called_recv, recv)
2197 var arguments = new Array[RuntimeVariable]
2198 var frame = new StaticFrame(v, mmethoddef, recv, arguments)
2199 v.frame = frame
2200
2201 var msignature = called_signature
2202 var ret = called_signature.return_mtype
2203
2204 var sig = new FlatBuffer
2205 var comment = new FlatBuffer
2206 sig.append(c_ret)
2207 sig.append(" ")
2208 sig.append(self.c_name)
2209 sig.append(c_sig)
2210 comment.append("({selfvar}: {selfvar.mtype}")
2211 arguments.add(selfvar)
2212 for i in [0..msignature.arity[ do
2213 var mtype = msignature.mparameters[i].mtype
2214 if i == msignature.vararg_rank then
2215 mtype = v.get_class("Array").get_mtype([mtype])
2216 end
2217 comment.append(", {mtype}")
2218 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
2219 arguments.add(argvar)
2220 end
2221 comment.append(")")
2222 if ret != null then
2223 comment.append(": {ret}")
2224 end
2225 compiler.provide_declaration(self.c_name, "{sig};")
2226 self.arguments = arguments.to_a
2227
2228 v.add_decl("/* method {self} for {comment} */")
2229 v.add_decl("{sig} \{")
2230 if ret != null then
2231 frame.returnvar = v.new_var(ret)
2232 end
2233 frame.returnlabel = v.get_name("RET_LABEL")
2234
2235 if is_thunk then
2236 var subret = v.call(mmethoddef, recv, arguments)
2237 if ret != null then
2238 assert subret != null
2239 v.assign(frame.returnvar.as(not null), subret)
2240 end
2241 else
2242 mmethoddef.compile_inside_to_c(v, arguments)
2243 end
2244
2245 v.add("{frame.returnlabel.as(not null)}:;")
2246 if ret != null then
2247 v.add("return {frame.returnvar.as(not null)};")
2248 end
2249 v.add("\}")
2250 compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
2251 end
2252
2253 # Compile the trampolines used to implement late-binding.
2254 #
2255 # See `opt_trampoline_call`.
2256 fun compile_trampolines(compiler: SeparateCompiler)
2257 do
2258 var recv = self.mmethoddef.mclassdef.bound_mtype
2259 var selfvar = arguments.first
2260 var ret = called_signature.return_mtype
2261
2262 if mmethoddef.is_intro and recv.ctype == "val*" then
2263 var m = mmethoddef.mproperty
2264 var n2 = "CALL_" + m.const_color
2265 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2266 var v2 = compiler.new_visitor
2267 v2.add "{c_ret} {n2}{c_sig} \{"
2268 v2.require_declaration(m.const_color)
2269 var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
2270 if ret != null then
2271 v2.add "return {call}"
2272 else
2273 v2.add call
2274 end
2275
2276 v2.add "\}"
2277
2278 end
2279 if mmethoddef.has_supercall and recv.ctype == "val*" then
2280 var m = mmethoddef
2281 var n2 = "CALL_" + m.const_color
2282 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2283 var v2 = compiler.new_visitor
2284 v2.add "{c_ret} {n2}{c_sig} \{"
2285 v2.require_declaration(m.const_color)
2286 var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
2287 if ret != null then
2288 v2.add "return {call}"
2289 else
2290 v2.add call
2291 end
2292
2293 v2.add "\}"
2294 end
2295 end
2296 end
2297
2298 redef class MType
2299 # Are values of `self` tagged?
2300 # If false, it means that the type is not primitive, or is boxed.
2301 var is_tagged = false
2302 end
2303
2304 redef class MEntity
2305 var const_color: String is lazy do return "COLOR_{c_name}"
2306 end
2307
2308 interface PropertyLayoutElement end
2309
2310 redef class MProperty
2311 super PropertyLayoutElement
2312 end
2313
2314 redef class MPropDef
2315 super PropertyLayoutElement
2316 end
2317
2318 redef class AMethPropdef
2319 # The semi-global compilation does not support inlining calls to extern news
2320 redef fun can_inline
2321 do
2322 var m = mpropdef
2323 if m != null and m.mproperty.is_init and m.is_extern then return false
2324 return super
2325 end
2326 end