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