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