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