sepcomp: isset is implemented on tagged attributes
[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.pointer_type.mclass]
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 not mtype.is_c_primitive 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.is_c_primitive 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 not value.mtype.is_c_primitive and not mtype.is_c_primitive then
1157 return value
1158 else if not value.mtype.is_c_primitive 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 not mtype.is_c_primitive 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 not value.mtype.is_c_primitive
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 not value.mtype.is_c_primitive 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 not value.mtype.is_c_primitive 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.is_c_primitive 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.is_c_primitive 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 not mtype.is_c_primitive and not mtype.is_tagged 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 not ret.is_c_primitive 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.is_tagged then
1665 # The attribute is not primitive, thus store it as tagged
1666 var tv = autobox(value, compiler.mainmodule.object_type)
1667 self.add("{attr} = {tv}; /* {a} on {recv.inspect} */")
1668 else if mtype.is_c_primitive then
1669 assert mtype isa MClassType
1670 # The attribute is primitive, thus we store it in a box
1671 # The trick is to create the box the first time then resuse the box
1672 self.add("if ({attr} != NULL) \{")
1673 self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1674 self.add("\} else \{")
1675 value = self.autobox(value, self.object_type.as_nullable)
1676 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1677 self.add("\}")
1678 else
1679 # The attribute is not primitive, thus store it direclty
1680 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1681 end
1682 else
1683 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1684 end
1685 end
1686
1687 # Check that mtype is a live open type
1688 fun hardening_live_open_type(mtype: MType)
1689 do
1690 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1691 self.require_declaration(mtype.const_color)
1692 var col = mtype.const_color
1693 self.add("if({col} == -1) \{")
1694 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1695 self.add_abort("open type dead")
1696 self.add("\}")
1697 end
1698
1699 # Check that mtype it a pointer to a live cast type
1700 fun hardening_cast_type(t: String)
1701 do
1702 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1703 add("if({t} == NULL) \{")
1704 add_abort("cast type null")
1705 add("\}")
1706 add("if({t}->id == -1 || {t}->color == -1) \{")
1707 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1708 add_abort("cast type dead")
1709 add("\}")
1710 end
1711
1712 redef fun init_instance(mtype)
1713 do
1714 self.require_declaration("NEW_{mtype.mclass.c_name}")
1715 var compiler = self.compiler
1716 if mtype isa MGenericType and mtype.need_anchor then
1717 hardening_live_open_type(mtype)
1718 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1719 var recv = self.frame.arguments.first
1720 var recv_type_info = self.type_info(recv)
1721 self.require_declaration(mtype.const_color)
1722 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1723 end
1724 compiler.undead_types.add(mtype)
1725 self.require_declaration("type_{mtype.c_name}")
1726 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1727 end
1728
1729 redef fun type_test(value, mtype, tag)
1730 do
1731 self.add("/* {value.inspect} isa {mtype} */")
1732 var compiler = self.compiler
1733
1734 var recv = self.frame.arguments.first
1735 var recv_type_info = self.type_info(recv)
1736
1737 var res = self.new_var(bool_type)
1738
1739 var cltype = self.get_name("cltype")
1740 self.add_decl("int {cltype};")
1741 var idtype = self.get_name("idtype")
1742 self.add_decl("int {idtype};")
1743
1744 var maybe_null = self.maybe_null(value)
1745 var accept_null = "0"
1746 var ntype = mtype
1747 if ntype isa MNullableType then
1748 ntype = ntype.mtype
1749 accept_null = "1"
1750 end
1751
1752 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1753 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1754 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1755 self.compiler.count_type_test_skipped[tag] += 1
1756 self.add("count_type_test_skipped_{tag}++;")
1757 end
1758 return res
1759 end
1760
1761 if ntype.need_anchor then
1762 var type_struct = self.get_name("type_struct")
1763 self.add_decl("const struct type* {type_struct};")
1764
1765 # Either with resolution_table with a direct resolution
1766 hardening_live_open_type(mtype)
1767 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1768 self.require_declaration(mtype.const_color)
1769 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1770 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1771 self.compiler.count_type_test_unresolved[tag] += 1
1772 self.add("count_type_test_unresolved_{tag}++;")
1773 end
1774 hardening_cast_type(type_struct)
1775 self.add("{cltype} = {type_struct}->color;")
1776 self.add("{idtype} = {type_struct}->id;")
1777 if maybe_null and accept_null == "0" then
1778 var is_nullable = self.get_name("is_nullable")
1779 self.add_decl("short int {is_nullable};")
1780 self.add("{is_nullable} = {type_struct}->is_nullable;")
1781 accept_null = is_nullable.to_s
1782 end
1783 else if ntype isa MClassType then
1784 compiler.undead_types.add(mtype)
1785 self.require_declaration("type_{mtype.c_name}")
1786 hardening_cast_type("(&type_{mtype.c_name})")
1787 self.add("{cltype} = type_{mtype.c_name}.color;")
1788 self.add("{idtype} = type_{mtype.c_name}.id;")
1789 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1790 self.compiler.count_type_test_resolved[tag] += 1
1791 self.add("count_type_test_resolved_{tag}++;")
1792 end
1793 else
1794 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); fatal_exit(1);")
1795 end
1796
1797 # check color is in table
1798 if maybe_null then
1799 self.add("if({value} == NULL) \{")
1800 self.add("{res} = {accept_null};")
1801 self.add("\} else \{")
1802 end
1803 var value_type_info = self.type_info(value)
1804 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1805 self.add("{res} = 0;")
1806 self.add("\} else \{")
1807 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1808 self.add("\}")
1809 if maybe_null then
1810 self.add("\}")
1811 end
1812
1813 return res
1814 end
1815
1816 redef fun is_same_type_test(value1, value2)
1817 do
1818 var res = self.new_var(bool_type)
1819 # Swap values to be symetric
1820 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1821 var tmp = value1
1822 value1 = value2
1823 value2 = tmp
1824 end
1825 if value1.mtype.is_c_primitive then
1826 if value2.mtype == value1.mtype then
1827 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1828 else if value2.mtype.is_c_primitive then
1829 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1830 else
1831 var mtype1 = value1.mtype.as(MClassType)
1832 self.require_declaration("class_{mtype1.c_name}")
1833 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
1834 end
1835 else
1836 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
1837 end
1838 return res
1839 end
1840
1841 redef fun class_name_string(value)
1842 do
1843 var res = self.get_name("var_class_name")
1844 self.add_decl("const char* {res};")
1845 if not value.mtype.is_c_primitive then
1846 self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
1847 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
1848 value.mtype.as(MClassType).name != "NativeString" then
1849 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1850 else
1851 self.require_declaration("type_{value.mtype.c_name}")
1852 self.add "{res} = type_{value.mtype.c_name}.name;"
1853 end
1854 return res
1855 end
1856
1857 redef fun equal_test(value1, value2)
1858 do
1859 var res = self.new_var(bool_type)
1860 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1861 var tmp = value1
1862 value1 = value2
1863 value2 = tmp
1864 end
1865 if value1.mtype.is_c_primitive then
1866 if value2.mtype == value1.mtype then
1867 self.add("{res} = {value1} == {value2};")
1868 else if value2.mtype.is_c_primitive then
1869 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1870 else if value1.mtype.is_tagged then
1871 self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});")
1872 else
1873 var mtype1 = value1.mtype.as(MClassType)
1874 self.require_declaration("class_{mtype1.c_name}")
1875 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
1876 self.add("if ({res}) \{")
1877 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1878 self.add("\}")
1879 end
1880 return res
1881 end
1882 var maybe_null = true
1883 var test = new Array[String]
1884 var t1 = value1.mcasttype
1885 if t1 isa MNullableType then
1886 test.add("{value1} != NULL")
1887 t1 = t1.mtype
1888 else
1889 maybe_null = false
1890 end
1891 var t2 = value2.mcasttype
1892 if t2 isa MNullableType then
1893 test.add("{value2} != NULL")
1894 t2 = t2.mtype
1895 else
1896 maybe_null = false
1897 end
1898
1899 var incompatible = false
1900 var primitive
1901 if t1.is_c_primitive then
1902 primitive = t1
1903 if t1 == t2 then
1904 # No need to compare class
1905 else if t2.is_c_primitive then
1906 incompatible = true
1907 else if can_be_primitive(value2) then
1908 if t1.is_tagged then
1909 self.add("{res} = {value1} == {value2};")
1910 return res
1911 end
1912 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1913 test.add("(!{extract_tag(value2)})")
1914 end
1915 test.add("{value1}->class == {value2}->class")
1916 else
1917 incompatible = true
1918 end
1919 else if t2.is_c_primitive then
1920 primitive = t2
1921 if can_be_primitive(value1) then
1922 if t2.is_tagged then
1923 self.add("{res} = {value1} == {value2};")
1924 return res
1925 end
1926 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1927 test.add("(!{extract_tag(value1)})")
1928 end
1929 test.add("{value1}->class == {value2}->class")
1930 else
1931 incompatible = true
1932 end
1933 else
1934 primitive = null
1935 end
1936
1937 if incompatible then
1938 if maybe_null then
1939 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
1940 return res
1941 else
1942 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
1943 return res
1944 end
1945 end
1946 if primitive != null then
1947 if primitive.is_tagged then
1948 self.add("{res} = {value1} == {value2};")
1949 return res
1950 end
1951 test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
1952 else if can_be_primitive(value1) and can_be_primitive(value2) then
1953 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1954 test.add("(!{extract_tag(value1)}) && (!{extract_tag(value2)})")
1955 end
1956 test.add("{value1}->class == {value2}->class")
1957 var s = new Array[String]
1958 for t, v in self.compiler.box_kinds do
1959 if t.mclass_type.is_tagged then continue
1960 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
1961 end
1962 if s.is_empty then
1963 self.add("{res} = {value1} == {value2};")
1964 return res
1965 end
1966 test.add("({s.join(" || ")})")
1967 else
1968 self.add("{res} = {value1} == {value2};")
1969 return res
1970 end
1971 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
1972 return res
1973 end
1974
1975 fun can_be_primitive(value: RuntimeVariable): Bool
1976 do
1977 var t = value.mcasttype.as_notnullable
1978 if not t isa MClassType then return false
1979 var k = t.mclass.kind
1980 return k == interface_kind or t.is_c_primitive
1981 end
1982
1983 fun maybe_null(value: RuntimeVariable): Bool
1984 do
1985 var t = value.mcasttype
1986 return t isa MNullableType or t isa MNullType
1987 end
1988
1989 redef fun array_instance(array, elttype)
1990 do
1991 var nclass = mmodule.native_array_class
1992 var arrayclass = mmodule.array_class
1993 var arraytype = arrayclass.get_mtype([elttype])
1994 var res = self.init_instance(arraytype)
1995 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
1996 var length = self.int_instance(array.length)
1997 var nat = native_array_instance(elttype, length)
1998 for i in [0..array.length[ do
1999 var r = self.autobox(array[i], self.object_type)
2000 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
2001 end
2002 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
2003 self.add("\}")
2004 return res
2005 end
2006
2007 redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
2008 do
2009 var mtype = mmodule.native_array_type(elttype)
2010 self.require_declaration("NEW_{mtype.mclass.c_name}")
2011 assert mtype isa MGenericType
2012 var compiler = self.compiler
2013 if mtype.need_anchor then
2014 hardening_live_open_type(mtype)
2015 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
2016 var recv = self.frame.arguments.first
2017 var recv_type_info = self.type_info(recv)
2018 self.require_declaration(mtype.const_color)
2019 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
2020 end
2021 compiler.undead_types.add(mtype)
2022 self.require_declaration("type_{mtype.c_name}")
2023 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
2024 end
2025
2026 redef fun native_array_def(pname, ret_type, arguments)
2027 do
2028 var elttype = arguments.first.mtype
2029 var nclass = mmodule.native_array_class
2030 var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
2031 if pname == "[]" then
2032 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2033 var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
2034 res.mcasttype = ret_type.as(not null)
2035 self.ret(res)
2036 return
2037 else if pname == "[]=" then
2038 self.add("{recv}[{arguments[1]}]={arguments[2]};")
2039 return
2040 else if pname == "length" then
2041 self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
2042 return
2043 else if pname == "copy_to" then
2044 var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
2045 self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
2046 return
2047 end
2048 end
2049
2050 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
2051 assert mtype.need_anchor
2052 var compiler = self.compiler
2053 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
2054 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
2055 end
2056 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
2057 end
2058 end
2059
2060 redef class MMethodDef
2061 # The C function associated to a mmethoddef
2062 fun separate_runtime_function: SeparateRuntimeFunction
2063 do
2064 var res = self.separate_runtime_function_cache
2065 if res == null then
2066 var recv = mclassdef.bound_mtype
2067 var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
2068 res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
2069 self.separate_runtime_function_cache = res
2070 end
2071 return res
2072 end
2073 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
2074
2075 # The C function associated to a mmethoddef, that can be stored into a VFT of a class
2076 # The first parameter (the reciever) is always typed by val* in order to accept an object value
2077 # The C-signature is always compatible with the intro
2078 fun virtual_runtime_function: SeparateRuntimeFunction
2079 do
2080 var res = self.virtual_runtime_function_cache
2081 if res == null then
2082 # Because the function is virtual, the signature must match the one of the original class
2083 var intromclassdef = mproperty.intro.mclassdef
2084 var recv = intromclassdef.bound_mtype
2085
2086 res = separate_runtime_function
2087 if res.called_recv == recv then
2088 self.virtual_runtime_function_cache = res
2089 return res
2090 end
2091
2092 var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
2093
2094 if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
2095 self.virtual_runtime_function_cache = res
2096 return res
2097 end
2098
2099 res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
2100 self.virtual_runtime_function_cache = res
2101 res.is_thunk = true
2102 end
2103 return res
2104 end
2105 private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
2106 end
2107
2108 redef class MSignature
2109 # Does the C-version of `self` the same than the C-version of `other`?
2110 fun c_equiv(other: MSignature): Bool
2111 do
2112 if self == other then return true
2113 if arity != other.arity then return false
2114 for i in [0..arity[ do
2115 if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
2116 end
2117 if return_mtype != other.return_mtype then
2118 if return_mtype == null or other.return_mtype == null then return false
2119 if return_mtype.ctype != other.return_mtype.ctype then return false
2120 end
2121 return true
2122 end
2123 end
2124
2125 # The C function associated to a methoddef separately compiled
2126 class SeparateRuntimeFunction
2127 super AbstractRuntimeFunction
2128
2129 # The call-side static receiver
2130 var called_recv: MType
2131
2132 # The call-side static signature
2133 var called_signature: MSignature
2134
2135 # The name on the compiled method
2136 redef var build_c_name: String
2137
2138 # Statically call the original body instead
2139 var is_thunk = false
2140
2141 redef fun to_s do return self.mmethoddef.to_s
2142
2143 # The C return type (something or `void`)
2144 var c_ret: String is lazy do
2145 var ret = called_signature.return_mtype
2146 if ret != null then
2147 return ret.ctype
2148 else
2149 return "void"
2150 end
2151 end
2152
2153 # The C signature (only the parmeter part)
2154 var c_sig: String is lazy do
2155 var sig = new FlatBuffer
2156 sig.append("({called_recv.ctype} self")
2157 for i in [0..called_signature.arity[ do
2158 var mtype = called_signature.mparameters[i].mtype
2159 if i == called_signature.vararg_rank then
2160 mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
2161 end
2162 sig.append(", {mtype.ctype} p{i}")
2163 end
2164 sig.append(")")
2165 return sig.to_s
2166 end
2167
2168 # The C type for the function pointer.
2169 var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
2170
2171 # The arguments, as generated by `compile_to_c`
2172 private var arguments: Array[RuntimeVariable] is noinit
2173
2174 redef fun compile_to_c(compiler)
2175 do
2176 var mmethoddef = self.mmethoddef
2177
2178 var recv = self.mmethoddef.mclassdef.bound_mtype
2179 var v = compiler.new_visitor
2180 var selfvar = new RuntimeVariable("self", called_recv, recv)
2181 var arguments = new Array[RuntimeVariable]
2182 var frame = new StaticFrame(v, mmethoddef, recv, arguments)
2183 v.frame = frame
2184
2185 var msignature = called_signature
2186 var ret = called_signature.return_mtype
2187
2188 var sig = new FlatBuffer
2189 var comment = new FlatBuffer
2190 sig.append(c_ret)
2191 sig.append(" ")
2192 sig.append(self.c_name)
2193 sig.append(c_sig)
2194 comment.append("({selfvar}: {selfvar.mtype}")
2195 arguments.add(selfvar)
2196 for i in [0..msignature.arity[ do
2197 var mtype = msignature.mparameters[i].mtype
2198 if i == msignature.vararg_rank then
2199 mtype = v.mmodule.array_type(mtype)
2200 end
2201 comment.append(", {mtype}")
2202 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
2203 arguments.add(argvar)
2204 end
2205 comment.append(")")
2206 if ret != null then
2207 comment.append(": {ret}")
2208 end
2209 compiler.provide_declaration(self.c_name, "{sig};")
2210 self.arguments = arguments.to_a
2211
2212 v.add_decl("/* method {self} for {comment} */")
2213 v.add_decl("{sig} \{")
2214 if ret != null then
2215 frame.returnvar = v.new_var(ret)
2216 end
2217 frame.returnlabel = v.get_name("RET_LABEL")
2218
2219 if is_thunk then
2220 var subret = v.call(mmethoddef, recv, arguments)
2221 if ret != null then
2222 assert subret != null
2223 v.assign(frame.returnvar.as(not null), subret)
2224 end
2225 else
2226 mmethoddef.compile_inside_to_c(v, arguments)
2227 end
2228
2229 v.add("{frame.returnlabel.as(not null)}:;")
2230 if ret != null then
2231 v.add("return {frame.returnvar.as(not null)};")
2232 end
2233 v.add("\}")
2234 compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
2235 end
2236
2237 # Compile the trampolines used to implement late-binding.
2238 #
2239 # See `opt_trampoline_call`.
2240 fun compile_trampolines(compiler: SeparateCompiler)
2241 do
2242 var recv = self.mmethoddef.mclassdef.bound_mtype
2243 var selfvar = arguments.first
2244 var ret = called_signature.return_mtype
2245
2246 if mmethoddef.is_intro and not recv.is_c_primitive then
2247 var m = mmethoddef.mproperty
2248 var n2 = "CALL_" + m.const_color
2249 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2250 var v2 = compiler.new_visitor
2251 v2.add "{c_ret} {n2}{c_sig} \{"
2252 v2.require_declaration(m.const_color)
2253 var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
2254 if ret != null then
2255 v2.add "return {call}"
2256 else
2257 v2.add call
2258 end
2259
2260 v2.add "\}"
2261
2262 end
2263 if mmethoddef.has_supercall and not recv.is_c_primitive then
2264 var m = mmethoddef
2265 var n2 = "CALL_" + m.const_color
2266 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2267 var v2 = compiler.new_visitor
2268 v2.add "{c_ret} {n2}{c_sig} \{"
2269 v2.require_declaration(m.const_color)
2270 var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
2271 if ret != null then
2272 v2.add "return {call}"
2273 else
2274 v2.add call
2275 end
2276
2277 v2.add "\}"
2278 end
2279 end
2280 end
2281
2282 redef class MType
2283 # Are values of `self` tagged?
2284 # If false, it means that the type is not primitive, or is boxed.
2285 var is_tagged = false
2286 end
2287
2288 redef class MEntity
2289 var const_color: String is lazy do return "COLOR_{c_name}"
2290 end
2291
2292 interface PropertyLayoutElement end
2293
2294 redef class MProperty
2295 super PropertyLayoutElement
2296 end
2297
2298 redef class MPropDef
2299 super PropertyLayoutElement
2300 end
2301
2302 redef class AMethPropdef
2303 # The semi-global compilation does not support inlining calls to extern news
2304 redef fun can_inline
2305 do
2306 var m = mpropdef
2307 if m != null and m.mproperty.is_init and m.is_extern then return false
2308 return super
2309 end
2310 end