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