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