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