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