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