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