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