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