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