sepcomp: shortcut an inline most intern methods of Object
[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 # Shortcut intern methods as they are not usually redefinable
1167 if callsite.mpropdef.is_intern and callsite.mproperty.name != "object_id" then
1168 # `object_id` is the only redefined intern method, so it can not be directly called.
1169 # TODO find a less ugly approach?
1170 return direct_call(callsite.mpropdef, args)
1171 end
1172 return super
1173 end
1174
1175 # Fully and directly call a mpropdef
1176 #
1177 # This method is used by `compile_callsite`
1178 private fun direct_call(mpropdef: MMethodDef, args: Array[RuntimeVariable]): nullable RuntimeVariable
1179 do
1180 var res0 = before_send(mpropdef.mproperty, args)
1181 var res = call(mpropdef, mpropdef.mclassdef.bound_mtype, args)
1182 if res0 != null then
1183 assert res != null
1184 self.assign(res0, res)
1185 res = res0
1186 end
1187 add("\}") # close the before_send
1188 return res
1189 end
1190 redef fun send(mmethod, arguments)
1191 do
1192 if arguments.first.mcasttype.ctype != "val*" then
1193 # In order to shortcut the primitive, we need to find the most specific method
1194 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1195 var m = self.compiler.mainmodule
1196 self.compiler.mainmodule = self.compiler.realmainmodule
1197 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1198 self.compiler.mainmodule = m
1199 return res
1200 end
1201
1202 return table_send(mmethod, arguments, mmethod)
1203 end
1204
1205 # Handle common special cases before doing the effective method invocation
1206 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1207 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1208 # Client must not forget to close the } after them.
1209 #
1210 # The value returned is the result of the common special cases.
1211 # If not null, client must compile it with the result of their own effective method invocation.
1212 #
1213 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1214 # is generated to cancel the effective method invocation that will follow
1215 # TODO: find a better approach
1216 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1217 do
1218 var res: nullable RuntimeVariable = null
1219 var recv = arguments.first
1220 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
1221 var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null
1222 if maybenull then
1223 self.add("if ({recv} == NULL) \{")
1224 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
1225 res = self.new_var(bool_type)
1226 var arg = arguments[1]
1227 if arg.mcasttype isa MNullableType then
1228 self.add("{res} = ({arg} == NULL);")
1229 else if arg.mcasttype isa MNullType then
1230 self.add("{res} = 1; /* is null */")
1231 else
1232 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1233 end
1234 else if mmethod.name == "!=" then
1235 res = self.new_var(bool_type)
1236 var arg = arguments[1]
1237 if arg.mcasttype isa MNullableType then
1238 self.add("{res} = ({arg} != NULL);")
1239 else if arg.mcasttype isa MNullType then
1240 self.add("{res} = 0; /* is null */")
1241 else
1242 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1243 end
1244 else
1245 self.add_abort("Receiver is null")
1246 end
1247 self.add("\} else \{")
1248 else
1249 self.add("\{")
1250 end
1251 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
1252 # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
1253 var arg = arguments[1]
1254 if arg.mcasttype isa MNullType then
1255 if res == null then res = self.new_var(bool_type)
1256 if mmethod.name == "!=" then
1257 self.add("{res} = 1; /* arg is null and recv is not */")
1258 else # `==` and `is_same_instance`
1259 self.add("{res} = 0; /* arg is null but recv is not */")
1260 end
1261 self.add("\}") # closes the null case
1262 self.add("if (0) \{") # what follow is useless, CC will drop it
1263 end
1264 end
1265 return res
1266 end
1267
1268 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
1269 do
1270 compiler.modelbuilder.nb_invok_by_tables += 1
1271 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1272
1273 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1274 var recv = arguments.first
1275
1276 var res0 = before_send(mmethod, arguments)
1277
1278 var runtime_function = mmethod.intro.virtual_runtime_function
1279 var msignature = runtime_function.called_signature
1280
1281 var res: nullable RuntimeVariable
1282 var ret = msignature.return_mtype
1283 if ret == null then
1284 res = null
1285 else
1286 res = self.new_var(ret)
1287 end
1288
1289 var ss = new FlatBuffer
1290
1291 ss.append("{recv}")
1292 for i in [0..msignature.arity[ do
1293 var a = arguments[i+1]
1294 var t = msignature.mparameters[i].mtype
1295 if i == msignature.vararg_rank then
1296 t = arguments[i+1].mcasttype
1297 end
1298 a = self.autobox(a, t)
1299 ss.append(", {a}")
1300 end
1301
1302 var const_color = mentity.const_color
1303 var ress
1304 if res != null then
1305 ress = "{res} = "
1306 else
1307 ress = ""
1308 end
1309 if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_direct_call_monomorph0.value then
1310 # opt_direct_call_monomorph0 is used to compare the efficiency of the alternative lookup implementation, ceteris paribus.
1311 # The difference with the non-zero option is that the monomorphism is looked-at on the mmethod level and not at the callsite level.
1312 # TODO: remove this mess and use per callsite service to detect monomorphism in a single place.
1313 var md = compiler.is_monomorphic(mentity)
1314 if md != null then
1315 var callsym = md.virtual_runtime_function.c_name
1316 self.require_declaration(callsym)
1317 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1318 else
1319 self.require_declaration(const_color)
1320 self.add "{ress}(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1321 end
1322 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_guard_call.value then
1323 var callsym = "CALL_" + const_color
1324 self.require_declaration(callsym)
1325 self.add "if (!{callsym}) \{"
1326 self.require_declaration(const_color)
1327 self.add "{ress}(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1328 self.add "\} else \{"
1329 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1330 self.add "\}"
1331 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
1332 var callsym = "CALL_" + const_color
1333 self.require_declaration(callsym)
1334 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1335 else
1336 self.require_declaration(const_color)
1337 self.add "{ress}(({runtime_function.c_funptrtype})({arguments.first}->class->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1338 end
1339
1340 if res0 != null then
1341 assert res != null
1342 assign(res0,res)
1343 res = res0
1344 end
1345
1346 self.add("\}") # closes the null case
1347
1348 return res
1349 end
1350
1351 redef fun call(mmethoddef, recvtype, arguments)
1352 do
1353 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1354
1355 var res: nullable RuntimeVariable
1356 var ret = mmethoddef.msignature.return_mtype
1357 if ret == null then
1358 res = null
1359 else
1360 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1361 res = self.new_var(ret)
1362 end
1363
1364 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1365 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1366 compiler.modelbuilder.nb_invok_by_inline += 1
1367 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1368 var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
1369 frame.returnlabel = self.get_name("RET_LABEL")
1370 frame.returnvar = res
1371 var old_frame = self.frame
1372 self.frame = frame
1373 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1374 mmethoddef.compile_inside_to_c(self, arguments)
1375 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1376 self.add("\}")
1377 self.frame = old_frame
1378 return res
1379 end
1380 compiler.modelbuilder.nb_invok_by_direct += 1
1381 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1382
1383 # Autobox arguments
1384 self.adapt_signature(mmethoddef, arguments)
1385
1386 self.require_declaration(mmethoddef.c_name)
1387 if res == null then
1388 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1389 return null
1390 else
1391 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1392 end
1393
1394 return res
1395 end
1396
1397 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1398 do
1399 if arguments.first.mcasttype.ctype != "val*" then
1400 # In order to shortcut the primitive, we need to find the most specific method
1401 # However, because of performance (no flattening), we always work on the realmainmodule
1402 var main = self.compiler.mainmodule
1403 self.compiler.mainmodule = self.compiler.realmainmodule
1404 var res = self.monomorphic_super_send(m, recvtype, arguments)
1405 self.compiler.mainmodule = main
1406 return res
1407 end
1408 return table_send(m.mproperty, arguments, m)
1409 end
1410
1411 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1412 do
1413 # A vararg must be stored into an new array
1414 # The trick is that the dymaic type of the array may depends on the receiver
1415 # of the method (ie recv) if the static type is unresolved
1416 # This is more complex than usual because the unresolved type must not be resolved
1417 # with the current receiver (ie self).
1418 # Therefore to isolate the resolution from self, a local StaticFrame is created.
1419 # One can see this implementation as an inlined method of the receiver whose only
1420 # job is to allocate the array
1421 var old_frame = self.frame
1422 var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1423 self.frame = frame
1424 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1425 var res = self.array_instance(varargs, elttype)
1426 self.frame = old_frame
1427 return res
1428 end
1429
1430 redef fun isset_attribute(a, recv)
1431 do
1432 self.check_recv_notnull(recv)
1433 var res = self.new_var(bool_type)
1434
1435 # What is the declared type of the attribute?
1436 var mtype = a.intro.static_mtype.as(not null)
1437 var intromclassdef = a.intro.mclassdef
1438 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1439
1440 if mtype isa MNullableType then
1441 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1442 return res
1443 end
1444
1445 self.require_declaration(a.const_color)
1446 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1447 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1448 else
1449
1450 if mtype.ctype == "val*" then
1451 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1452 else
1453 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1454 end
1455 end
1456 return res
1457 end
1458
1459 redef fun read_attribute(a, recv)
1460 do
1461 self.check_recv_notnull(recv)
1462
1463 # What is the declared type of the attribute?
1464 var ret = a.intro.static_mtype.as(not null)
1465 var intromclassdef = a.intro.mclassdef
1466 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1467
1468 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1469 self.compiler.attr_read_count += 1
1470 self.add("count_attr_reads++;")
1471 end
1472
1473 self.require_declaration(a.const_color)
1474 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1475 # Get the attribute or a box (ie. always a val*)
1476 var cret = self.object_type.as_nullable
1477 var res = self.new_var(cret)
1478 res.mcasttype = ret
1479
1480 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1481
1482 # Check for Uninitialized attribute
1483 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1484 self.add("if (unlikely({res} == NULL)) \{")
1485 self.add_abort("Uninitialized attribute {a.name}")
1486 self.add("\}")
1487
1488 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1489 self.compiler.isset_checks_count += 1
1490 self.add("count_isset_checks++;")
1491 end
1492 end
1493
1494 # Return the attribute or its unboxed version
1495 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1496 return self.autobox(res, ret)
1497 else
1498 var res = self.new_var(ret)
1499 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1500
1501 # Check for Uninitialized attribute
1502 if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1503 self.add("if (unlikely({res} == NULL)) \{")
1504 self.add_abort("Uninitialized attribute {a.name}")
1505 self.add("\}")
1506 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1507 self.compiler.isset_checks_count += 1
1508 self.add("count_isset_checks++;")
1509 end
1510 end
1511
1512 return res
1513 end
1514 end
1515
1516 redef fun write_attribute(a, recv, value)
1517 do
1518 self.check_recv_notnull(recv)
1519
1520 # What is the declared type of the attribute?
1521 var mtype = a.intro.static_mtype.as(not null)
1522 var intromclassdef = a.intro.mclassdef
1523 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1524
1525 # Adapt the value to the declared type
1526 value = self.autobox(value, mtype)
1527
1528 self.require_declaration(a.const_color)
1529 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1530 var attr = "{recv}->attrs[{a.const_color}]"
1531 if mtype.ctype != "val*" then
1532 assert mtype isa MClassType
1533 # The attribute is primitive, thus we store it in a box
1534 # The trick is to create the box the first time then resuse the box
1535 self.add("if ({attr} != NULL) \{")
1536 self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1537 self.add("\} else \{")
1538 value = self.autobox(value, self.object_type.as_nullable)
1539 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1540 self.add("\}")
1541 else
1542 # The attribute is not primitive, thus store it direclty
1543 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1544 end
1545 else
1546 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1547 end
1548 end
1549
1550 # Check that mtype is a live open type
1551 fun hardening_live_open_type(mtype: MType)
1552 do
1553 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1554 self.require_declaration(mtype.const_color)
1555 var col = mtype.const_color
1556 self.add("if({col} == -1) \{")
1557 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1558 self.add_abort("open type dead")
1559 self.add("\}")
1560 end
1561
1562 # Check that mtype it a pointer to a live cast type
1563 fun hardening_cast_type(t: String)
1564 do
1565 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1566 add("if({t} == NULL) \{")
1567 add_abort("cast type null")
1568 add("\}")
1569 add("if({t}->id == -1 || {t}->color == -1) \{")
1570 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1571 add_abort("cast type dead")
1572 add("\}")
1573 end
1574
1575 redef fun init_instance(mtype)
1576 do
1577 self.require_declaration("NEW_{mtype.mclass.c_name}")
1578 var compiler = self.compiler
1579 if mtype isa MGenericType and mtype.need_anchor then
1580 hardening_live_open_type(mtype)
1581 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1582 var recv = self.frame.arguments.first
1583 var recv_type_info = self.type_info(recv)
1584 self.require_declaration(mtype.const_color)
1585 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1586 end
1587 compiler.undead_types.add(mtype)
1588 self.require_declaration("type_{mtype.c_name}")
1589 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1590 end
1591
1592 redef fun type_test(value, mtype, tag)
1593 do
1594 self.add("/* {value.inspect} isa {mtype} */")
1595 var compiler = self.compiler
1596
1597 var recv = self.frame.arguments.first
1598 var recv_type_info = self.type_info(recv)
1599
1600 var res = self.new_var(bool_type)
1601
1602 var cltype = self.get_name("cltype")
1603 self.add_decl("int {cltype};")
1604 var idtype = self.get_name("idtype")
1605 self.add_decl("int {idtype};")
1606
1607 var maybe_null = self.maybe_null(value)
1608 var accept_null = "0"
1609 var ntype = mtype
1610 if ntype isa MNullableType then
1611 ntype = ntype.mtype
1612 accept_null = "1"
1613 end
1614
1615 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1616 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1617 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1618 self.compiler.count_type_test_skipped[tag] += 1
1619 self.add("count_type_test_skipped_{tag}++;")
1620 end
1621 return res
1622 end
1623
1624 if ntype.need_anchor then
1625 var type_struct = self.get_name("type_struct")
1626 self.add_decl("const struct type* {type_struct};")
1627
1628 # Either with resolution_table with a direct resolution
1629 hardening_live_open_type(mtype)
1630 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1631 self.require_declaration(mtype.const_color)
1632 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1633 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1634 self.compiler.count_type_test_unresolved[tag] += 1
1635 self.add("count_type_test_unresolved_{tag}++;")
1636 end
1637 hardening_cast_type(type_struct)
1638 self.add("{cltype} = {type_struct}->color;")
1639 self.add("{idtype} = {type_struct}->id;")
1640 if maybe_null and accept_null == "0" then
1641 var is_nullable = self.get_name("is_nullable")
1642 self.add_decl("short int {is_nullable};")
1643 self.add("{is_nullable} = {type_struct}->is_nullable;")
1644 accept_null = is_nullable.to_s
1645 end
1646 else if ntype isa MClassType then
1647 compiler.undead_types.add(mtype)
1648 self.require_declaration("type_{mtype.c_name}")
1649 hardening_cast_type("(&type_{mtype.c_name})")
1650 self.add("{cltype} = type_{mtype.c_name}.color;")
1651 self.add("{idtype} = type_{mtype.c_name}.id;")
1652 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1653 self.compiler.count_type_test_resolved[tag] += 1
1654 self.add("count_type_test_resolved_{tag}++;")
1655 end
1656 else
1657 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); show_backtrace(1);")
1658 end
1659
1660 # check color is in table
1661 if maybe_null then
1662 self.add("if({value} == NULL) \{")
1663 self.add("{res} = {accept_null};")
1664 self.add("\} else \{")
1665 end
1666 var value_type_info = self.type_info(value)
1667 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1668 self.add("{res} = 0;")
1669 self.add("\} else \{")
1670 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1671 self.add("\}")
1672 if maybe_null then
1673 self.add("\}")
1674 end
1675
1676 return res
1677 end
1678
1679 redef fun is_same_type_test(value1, value2)
1680 do
1681 var res = self.new_var(bool_type)
1682 # Swap values to be symetric
1683 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1684 var tmp = value1
1685 value1 = value2
1686 value2 = tmp
1687 end
1688 if value1.mtype.ctype != "val*" then
1689 if value2.mtype == value1.mtype then
1690 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1691 else if value2.mtype.ctype != "val*" then
1692 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1693 else
1694 var mtype1 = value1.mtype.as(MClassType)
1695 self.require_declaration("class_{mtype1.c_name}")
1696 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name}); /* is_same_type_test */")
1697 end
1698 else
1699 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {value1}->class == {value2}->class); /* is_same_type_test */")
1700 end
1701 return res
1702 end
1703
1704 redef fun class_name_string(value)
1705 do
1706 var res = self.get_name("var_class_name")
1707 self.add_decl("const char* {res};")
1708 if value.mtype.ctype == "val*" then
1709 self.add "{res} = {value} == NULL ? \"null\" : {value}->type->name;"
1710 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
1711 value.mtype.as(MClassType).name != "NativeString" then
1712 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1713 else
1714 self.require_declaration("type_{value.mtype.c_name}")
1715 self.add "{res} = type_{value.mtype.c_name}.name;"
1716 end
1717 return res
1718 end
1719
1720 redef fun equal_test(value1, value2)
1721 do
1722 var res = self.new_var(bool_type)
1723 if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
1724 var tmp = value1
1725 value1 = value2
1726 value2 = tmp
1727 end
1728 if value1.mtype.ctype != "val*" then
1729 if value2.mtype == value1.mtype then
1730 self.add("{res} = {value1} == {value2};")
1731 else if value2.mtype.ctype != "val*" then
1732 self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
1733 else
1734 var mtype1 = value1.mtype.as(MClassType)
1735 self.require_declaration("class_{mtype1.c_name}")
1736 self.add("{res} = ({value2} != NULL) && ({value2}->class == &class_{mtype1.c_name});")
1737 self.add("if ({res}) \{")
1738 self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
1739 self.add("\}")
1740 end
1741 return res
1742 end
1743 var maybe_null = true
1744 var test = new Array[String]
1745 var t1 = value1.mcasttype
1746 if t1 isa MNullableType then
1747 test.add("{value1} != NULL")
1748 t1 = t1.mtype
1749 else
1750 maybe_null = false
1751 end
1752 var t2 = value2.mcasttype
1753 if t2 isa MNullableType then
1754 test.add("{value2} != NULL")
1755 t2 = t2.mtype
1756 else
1757 maybe_null = false
1758 end
1759
1760 var incompatible = false
1761 var primitive
1762 if t1.ctype != "val*" then
1763 primitive = t1
1764 if t1 == t2 then
1765 # No need to compare class
1766 else if t2.ctype != "val*" then
1767 incompatible = true
1768 else if can_be_primitive(value2) then
1769 test.add("{value1}->class == {value2}->class")
1770 else
1771 incompatible = true
1772 end
1773 else if t2.ctype != "val*" then
1774 primitive = t2
1775 if can_be_primitive(value1) then
1776 test.add("{value1}->class == {value2}->class")
1777 else
1778 incompatible = true
1779 end
1780 else
1781 primitive = null
1782 end
1783
1784 if incompatible then
1785 if maybe_null then
1786 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
1787 return res
1788 else
1789 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
1790 return res
1791 end
1792 end
1793 if primitive != null then
1794 test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
1795 else if can_be_primitive(value1) and can_be_primitive(value2) then
1796 test.add("{value1}->class == {value2}->class")
1797 var s = new Array[String]
1798 for t, v in self.compiler.box_kinds do
1799 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
1800 end
1801 test.add("({s.join(" || ")})")
1802 else
1803 self.add("{res} = {value1} == {value2};")
1804 return res
1805 end
1806 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
1807 return res
1808 end
1809
1810 fun can_be_primitive(value: RuntimeVariable): Bool
1811 do
1812 var t = value.mcasttype.as_notnullable
1813 if not t isa MClassType then return false
1814 var k = t.mclass.kind
1815 return k == interface_kind or t.ctype != "val*"
1816 end
1817
1818 fun maybe_null(value: RuntimeVariable): Bool
1819 do
1820 var t = value.mcasttype
1821 return t isa MNullableType or t isa MNullType
1822 end
1823
1824 redef fun array_instance(array, elttype)
1825 do
1826 var nclass = self.get_class("NativeArray")
1827 var arrayclass = self.get_class("Array")
1828 var arraytype = arrayclass.get_mtype([elttype])
1829 var res = self.init_instance(arraytype)
1830 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
1831 var length = self.int_instance(array.length)
1832 var nat = native_array_instance(elttype, length)
1833 for i in [0..array.length[ do
1834 var r = self.autobox(array[i], self.object_type)
1835 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
1836 end
1837 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
1838 self.add("\}")
1839 return res
1840 end
1841
1842 redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
1843 do
1844 var mtype = self.get_class("NativeArray").get_mtype([elttype])
1845 self.require_declaration("NEW_{mtype.mclass.c_name}")
1846 assert mtype isa MGenericType
1847 var compiler = self.compiler
1848 if mtype.need_anchor then
1849 hardening_live_open_type(mtype)
1850 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1851 var recv = self.frame.arguments.first
1852 var recv_type_info = self.type_info(recv)
1853 self.require_declaration(mtype.const_color)
1854 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1855 end
1856 compiler.undead_types.add(mtype)
1857 self.require_declaration("type_{mtype.c_name}")
1858 return self.new_expr("NEW_{mtype.mclass.c_name}({length}, &type_{mtype.c_name})", mtype)
1859 end
1860
1861 redef fun native_array_def(pname, ret_type, arguments)
1862 do
1863 var elttype = arguments.first.mtype
1864 var nclass = self.get_class("NativeArray")
1865 var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
1866 if pname == "[]" then
1867 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
1868 var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
1869 res.mcasttype = ret_type.as(not null)
1870 self.ret(res)
1871 return
1872 else if pname == "[]=" then
1873 self.add("{recv}[{arguments[1]}]={arguments[2]};")
1874 return
1875 else if pname == "length" then
1876 self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
1877 return
1878 else if pname == "copy_to" then
1879 var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
1880 self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
1881 return
1882 end
1883 end
1884
1885 redef fun calloc_array(ret_type, arguments)
1886 do
1887 var mclass = self.get_class("ArrayCapable")
1888 var ft = mclass.mparameters.first
1889 var res = self.native_array_instance(ft, arguments[1])
1890 self.ret(res)
1891 end
1892
1893 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
1894 assert mtype.need_anchor
1895 var compiler = self.compiler
1896 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
1897 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
1898 end
1899 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
1900 end
1901 end
1902
1903 redef class MMethodDef
1904 # The C function associated to a mmethoddef
1905 fun separate_runtime_function: SeparateRuntimeFunction
1906 do
1907 var res = self.separate_runtime_function_cache
1908 if res == null then
1909 var recv = mclassdef.bound_mtype
1910 var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
1911 res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
1912 self.separate_runtime_function_cache = res
1913 end
1914 return res
1915 end
1916 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
1917
1918 # The C function associated to a mmethoddef, that can be stored into a VFT of a class
1919 # The first parameter (the reciever) is always typed by val* in order to accept an object value
1920 # The C-signature is always compatible with the intro
1921 fun virtual_runtime_function: SeparateRuntimeFunction
1922 do
1923 var res = self.virtual_runtime_function_cache
1924 if res == null then
1925 # Because the function is virtual, the signature must match the one of the original class
1926 var intromclassdef = mproperty.intro.mclassdef
1927 var recv = intromclassdef.bound_mtype
1928
1929 res = separate_runtime_function
1930 if res.called_recv == recv then
1931 self.virtual_runtime_function_cache = res
1932 return res
1933 end
1934
1935 var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
1936
1937 if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
1938 self.virtual_runtime_function_cache = res
1939 return res
1940 end
1941
1942 res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
1943 self.virtual_runtime_function_cache = res
1944 res.is_thunk = true
1945 end
1946 return res
1947 end
1948 private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
1949 end
1950
1951 redef class MSignature
1952 # Does the C-version of `self` the same than the C-version of `other`?
1953 fun c_equiv(other: MSignature): Bool
1954 do
1955 if self == other then return true
1956 if arity != other.arity then return false
1957 for i in [0..arity[ do
1958 if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
1959 end
1960 if return_mtype != other.return_mtype then
1961 if return_mtype == null or other.return_mtype == null then return false
1962 if return_mtype.ctype != other.return_mtype.ctype then return false
1963 end
1964 return true
1965 end
1966 end
1967
1968 # The C function associated to a methoddef separately compiled
1969 class SeparateRuntimeFunction
1970 super AbstractRuntimeFunction
1971
1972 # The call-side static receiver
1973 var called_recv: MType
1974
1975 # The call-side static signature
1976 var called_signature: MSignature
1977
1978 # The name on the compiled method
1979 redef var build_c_name: String
1980
1981 # Statically call the original body instead
1982 var is_thunk = false
1983
1984 redef fun to_s do return self.mmethoddef.to_s
1985
1986 # The C return type (something or `void`)
1987 var c_ret: String is lazy do
1988 var ret = called_signature.return_mtype
1989 if ret != null then
1990 return ret.ctype
1991 else
1992 return "void"
1993 end
1994 end
1995
1996 # The C signature (only the parmeter part)
1997 var c_sig: String is lazy do
1998 var sig = new FlatBuffer
1999 sig.append("({called_recv.ctype} self")
2000 for i in [0..called_signature.arity[ do
2001 var mtype = called_signature.mparameters[i].mtype
2002 if i == called_signature.vararg_rank then
2003 mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
2004 end
2005 sig.append(", {mtype.ctype} p{i}")
2006 end
2007 sig.append(")")
2008 return sig.to_s
2009 end
2010
2011 # The C type for the function pointer.
2012 var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
2013
2014 # The arguments, as generated by `compile_to_c`
2015 private var arguments: Array[RuntimeVariable] is noinit
2016
2017 redef fun compile_to_c(compiler)
2018 do
2019 var mmethoddef = self.mmethoddef
2020
2021 var recv = self.mmethoddef.mclassdef.bound_mtype
2022 var v = compiler.new_visitor
2023 var selfvar = new RuntimeVariable("self", called_recv, recv)
2024 var arguments = new Array[RuntimeVariable]
2025 var frame = new StaticFrame(v, mmethoddef, recv, arguments)
2026 v.frame = frame
2027
2028 var msignature = called_signature
2029 var ret = called_signature.return_mtype
2030
2031 var sig = new FlatBuffer
2032 var comment = new FlatBuffer
2033 sig.append(c_ret)
2034 sig.append(" ")
2035 sig.append(self.c_name)
2036 sig.append(c_sig)
2037 comment.append("({selfvar}: {selfvar.mtype}")
2038 arguments.add(selfvar)
2039 for i in [0..msignature.arity[ do
2040 var mtype = msignature.mparameters[i].mtype
2041 if i == msignature.vararg_rank then
2042 mtype = v.get_class("Array").get_mtype([mtype])
2043 end
2044 comment.append(", {mtype}")
2045 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
2046 arguments.add(argvar)
2047 end
2048 comment.append(")")
2049 if ret != null then
2050 comment.append(": {ret}")
2051 end
2052 compiler.provide_declaration(self.c_name, "{sig};")
2053 self.arguments = arguments.to_a
2054
2055 v.add_decl("/* method {self} for {comment} */")
2056 v.add_decl("{sig} \{")
2057 if ret != null then
2058 frame.returnvar = v.new_var(ret)
2059 end
2060 frame.returnlabel = v.get_name("RET_LABEL")
2061
2062 if is_thunk then
2063 var subret = v.call(mmethoddef, recv, arguments)
2064 if ret != null then
2065 assert subret != null
2066 v.assign(frame.returnvar.as(not null), subret)
2067 end
2068 else
2069 mmethoddef.compile_inside_to_c(v, arguments)
2070 end
2071
2072 v.add("{frame.returnlabel.as(not null)}:;")
2073 if ret != null then
2074 v.add("return {frame.returnvar.as(not null)};")
2075 end
2076 v.add("\}")
2077 compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
2078 end
2079
2080 # Compile the trampolines used to implement late-binding.
2081 #
2082 # See `opt_trampoline_call`.
2083 fun compile_trampolines(compiler: SeparateCompiler)
2084 do
2085 var recv = self.mmethoddef.mclassdef.bound_mtype
2086 var selfvar = arguments.first
2087 var ret = called_signature.return_mtype
2088
2089 if mmethoddef.is_intro and recv.ctype == "val*" then
2090 var m = mmethoddef.mproperty
2091 var n2 = "CALL_" + m.const_color
2092 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2093 var v2 = compiler.new_visitor
2094 v2.add "{c_ret} {n2}{c_sig} \{"
2095 v2.require_declaration(m.const_color)
2096 var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
2097 if ret != null then
2098 v2.add "return {call}"
2099 else
2100 v2.add call
2101 end
2102
2103 v2.add "\}"
2104
2105 end
2106 if mmethoddef.has_supercall and recv.ctype == "val*" then
2107 var m = mmethoddef
2108 var n2 = "CALL_" + m.const_color
2109 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2110 var v2 = compiler.new_visitor
2111 v2.add "{c_ret} {n2}{c_sig} \{"
2112 v2.require_declaration(m.const_color)
2113 var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
2114 if ret != null then
2115 v2.add "return {call}"
2116 else
2117 v2.add call
2118 end
2119
2120 v2.add "\}"
2121 end
2122 end
2123 end
2124
2125 redef class MEntity
2126 var const_color: String is lazy do return "COLOR_{c_name}"
2127 end
2128
2129 interface PropertyLayoutElement end
2130
2131 redef class MProperty
2132 super PropertyLayoutElement
2133 end
2134
2135 redef class MPropDef
2136 super PropertyLayoutElement
2137 end
2138
2139 redef class AMethPropdef
2140 # The semi-global compilation does not support inlining calls to extern news
2141 redef fun can_inline
2142 do
2143 var m = mpropdef
2144 if m != null and m.mproperty.is_init and m.is_extern then return false
2145 return super
2146 end
2147 end