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