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