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