nitc: explicitly cast long to int
[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 ad 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 v.add("{res}->type = type;")
986 hardening_live_type(v, "type")
987 v.require_declaration("class_{c_name}")
988 v.add("{res}->class = &class_{c_name};")
989 if attrs != null then
990 self.generate_init_attr(v, res, mtype)
991 v.set_finalizer res
992 end
993 v.add("return {res};")
994 end
995 v.add("\}")
996 end
997
998 # Compile structures used to map tagged primitive values to their classes and types.
999 # This method also determines which class will be tagged.
1000 fun compile_class_infos
1001 do
1002 if modelbuilder.toolcontext.opt_no_tag_primitives.value then return
1003
1004 # Note: if you change the tagging scheme, do not forget to update
1005 # `autobox` and `extract_tag`
1006 var class_info = new Array[nullable MClass].filled_with(null, 4)
1007 for t in box_kinds.keys do
1008 # Note: a same class can be associated to multiple slots if one want to
1009 # use some Huffman coding.
1010 if t.name == "Int" then
1011 class_info[1] = t
1012 t.mclass_type.tag_value = 1
1013 else if t.name == "Char" then
1014 class_info[2] = t
1015 t.mclass_type.tag_value = 2
1016 else if t.name == "Bool" then
1017 class_info[3] = t
1018 t.mclass_type.tag_value = 3
1019 else
1020 continue
1021 end
1022 t.mclass_type.is_tagged = true
1023 end
1024
1025 # Compile the table for classes. The tag is used as an index
1026 var v = self.new_visitor
1027 v.add_decl "const struct class *class_info[4] = \{"
1028 for t in class_info do
1029 if t == null then
1030 v.add_decl("NULL,")
1031 else
1032 var s = "class_{t.c_name}"
1033 v.require_declaration(s)
1034 v.add_decl("&{s},")
1035 end
1036 end
1037 v.add_decl("\};")
1038
1039 # Compile the table for types. The tag is used as an index
1040 v.add_decl "const struct type *type_info[4] = \{"
1041 for t in class_info do
1042 if t == null then
1043 v.add_decl("NULL,")
1044 else
1045 var s = "type_{t.c_name}"
1046 undead_types.add(t.mclass_type)
1047 v.require_declaration(s)
1048 v.add_decl("&{s},")
1049 end
1050 end
1051 v.add_decl("\};")
1052 end
1053
1054 # Add a dynamic test to ensure that the type referenced by `t` is a live type
1055 fun hardening_live_type(v: VISITOR, t: String)
1056 do
1057 if not v.compiler.modelbuilder.toolcontext.opt_hardening.value then return
1058 v.add("if({t} == NULL) \{")
1059 v.add_abort("type null")
1060 v.add("\}")
1061 v.add("if({t}->table_size < 0) \{")
1062 v.add("PRINT_ERROR(\"Instantiation of a dead type: %s\\n\", {t}->name);")
1063 v.add_abort("type dead")
1064 v.add("\}")
1065 end
1066
1067 redef fun new_visitor do return new SeparateCompilerVisitor(self)
1068
1069 # Stats
1070
1071 private var type_tables: Map[MType, Array[nullable MType]] = new HashMap[MType, Array[nullable MType]]
1072 private var resolution_tables: Map[MClassType, Array[nullable MType]] = new HashMap[MClassType, Array[nullable MType]]
1073 protected var method_tables: Map[MClass, Array[nullable MPropDef]] = new HashMap[MClass, Array[nullable MPropDef]]
1074 protected var attr_tables: Map[MClass, Array[nullable MProperty]] = new HashMap[MClass, Array[nullable MProperty]]
1075
1076 redef fun display_stats
1077 do
1078 super
1079 if self.modelbuilder.toolcontext.opt_tables_metrics.value then
1080 display_sizes
1081 end
1082 if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1083 display_isset_checks
1084 end
1085 var tc = self.modelbuilder.toolcontext
1086 tc.info("# implementation of method invocation",2)
1087 var nb_invok_total = modelbuilder.nb_invok_by_tables + modelbuilder.nb_invok_by_direct + modelbuilder.nb_invok_by_inline
1088 tc.info("total number of invocations: {nb_invok_total}",2)
1089 tc.info("invocations by VFT send: {modelbuilder.nb_invok_by_tables} ({div(modelbuilder.nb_invok_by_tables,nb_invok_total)}%)",2)
1090 tc.info("invocations by direct call: {modelbuilder.nb_invok_by_direct} ({div(modelbuilder.nb_invok_by_direct,nb_invok_total)}%)",2)
1091 tc.info("invocations by inlining: {modelbuilder.nb_invok_by_inline} ({div(modelbuilder.nb_invok_by_inline,nb_invok_total)}%)",2)
1092 end
1093
1094 fun display_sizes
1095 do
1096 print "# size of subtyping tables"
1097 print "\ttotal \tholes"
1098 var total = 0
1099 var holes = 0
1100 for t, table in type_tables do
1101 total += table.length
1102 for e in table do if e == null then holes += 1
1103 end
1104 print "\t{total}\t{holes}"
1105
1106 print "# size of resolution tables"
1107 print "\ttotal \tholes"
1108 total = 0
1109 holes = 0
1110 for t, table in resolution_tables do
1111 total += table.length
1112 for e in table do if e == null then holes += 1
1113 end
1114 print "\t{total}\t{holes}"
1115
1116 print "# size of methods tables"
1117 print "\ttotal \tholes"
1118 total = 0
1119 holes = 0
1120 for t, table in method_tables do
1121 total += table.length
1122 for e in table do if e == null then holes += 1
1123 end
1124 print "\t{total}\t{holes}"
1125
1126 print "# size of attributes tables"
1127 print "\ttotal \tholes"
1128 total = 0
1129 holes = 0
1130 for t, table in attr_tables do
1131 total += table.length
1132 for e in table do if e == null then holes += 1
1133 end
1134 print "\t{total}\t{holes}"
1135 end
1136
1137 protected var isset_checks_count = 0
1138 protected var attr_read_count = 0
1139
1140 fun display_isset_checks do
1141 print "# total number of compiled attribute reads"
1142 print "\t{attr_read_count}"
1143 print "# total number of compiled isset-checks"
1144 print "\t{isset_checks_count}"
1145 end
1146
1147 redef fun compile_nitni_structs
1148 do
1149 self.header.add_decl """
1150 struct nitni_instance \{
1151 struct nitni_instance *next,
1152 *prev; /* adjacent global references in global list */
1153 int count; /* number of time this global reference has been marked */
1154 struct instance *value;
1155 \};
1156 """
1157 super
1158 end
1159
1160 redef fun finalize_ffi_for_module(mmodule)
1161 do
1162 var old_module = self.mainmodule
1163 self.mainmodule = mmodule
1164 super
1165 self.mainmodule = old_module
1166 end
1167 end
1168
1169 # A visitor on the AST of property definition that generate the C code of a separate compilation process.
1170 class SeparateCompilerVisitor
1171 super AbstractCompilerVisitor
1172
1173 redef type COMPILER: SeparateCompiler
1174
1175 redef fun adapt_signature(m, args)
1176 do
1177 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1178 var recv = args.first
1179 if recv.mtype.ctype != m.mclassdef.mclass.mclass_type.ctype then
1180 args.first = self.autobox(args.first, m.mclassdef.mclass.mclass_type)
1181 end
1182 for i in [0..msignature.arity[ do
1183 var mp = msignature.mparameters[i]
1184 var t = mp.mtype
1185 if mp.is_vararg then
1186 t = args[i+1].mtype
1187 end
1188 args[i+1] = self.autobox(args[i+1], t)
1189 end
1190 end
1191
1192 redef fun unbox_signature_extern(m, args)
1193 do
1194 var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
1195 if not m.mproperty.is_init and m.is_extern then
1196 args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
1197 end
1198 for i in [0..msignature.arity[ do
1199 var mp = msignature.mparameters[i]
1200 var t = mp.mtype
1201 if mp.is_vararg then
1202 t = args[i+1].mtype
1203 end
1204 if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
1205 end
1206 end
1207
1208 redef fun autobox(value, mtype)
1209 do
1210 if value.mtype == mtype then
1211 return value
1212 else if not value.mtype.is_c_primitive and not mtype.is_c_primitive then
1213 return value
1214 else if not value.mtype.is_c_primitive then
1215 if mtype.is_tagged then
1216 if mtype.name == "Int" then
1217 return self.new_expr("(long)({value})>>2", mtype)
1218 else if mtype.name == "Char" then
1219 return self.new_expr("(uint32_t)((long)({value})>>2)", mtype)
1220 else if mtype.name == "Bool" then
1221 return self.new_expr("(short int)((long)({value})>>2)", mtype)
1222 else
1223 abort
1224 end
1225 end
1226 return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
1227 else if not mtype.is_c_primitive then
1228 assert value.mtype == value.mcasttype
1229 if value.mtype.is_tagged then
1230 var res
1231 if value.mtype.name == "Int" then
1232 res = self.new_expr("(val*)({value}<<2|1)", mtype)
1233 else if value.mtype.name == "Char" then
1234 res = self.new_expr("(val*)((long)({value})<<2|2)", mtype)
1235 else if value.mtype.name == "Bool" then
1236 res = self.new_expr("(val*)((long)({value})<<2|3)", mtype)
1237 else
1238 abort
1239 end
1240 # Do not loose type info
1241 res.mcasttype = value.mcasttype
1242 return res
1243 end
1244 var valtype = value.mtype.as(MClassType)
1245 if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
1246 valtype = compiler.mainmodule.pointer_type
1247 end
1248 var res = self.new_var(mtype)
1249 # Do not loose type info
1250 res.mcasttype = value.mcasttype
1251 self.require_declaration("BOX_{valtype.c_name}")
1252 self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
1253 return res
1254 else if (value.mtype.ctype == "void*" and mtype.ctype == "void*") or
1255 (value.mtype.ctype == "char*" and mtype.ctype == "void*") or
1256 (value.mtype.ctype == "void*" and mtype.ctype == "char*") then
1257 return value
1258 else
1259 # Bad things will appen!
1260 var res = self.new_var(mtype)
1261 self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */")
1262 self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); fatal_exit(1);")
1263 return res
1264 end
1265 end
1266
1267 redef fun unbox_extern(value, mtype)
1268 do
1269 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1270 mtype.mclass.name != "CString" then
1271 var pointer_type = compiler.mainmodule.pointer_type
1272 var res = self.new_var_extern(mtype)
1273 self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
1274 return res
1275 else
1276 return value
1277 end
1278 end
1279
1280 redef fun box_extern(value, mtype)
1281 do
1282 if mtype isa MClassType and mtype.mclass.kind == extern_kind and
1283 mtype.mclass.name != "CString" then
1284 var valtype = compiler.mainmodule.pointer_type
1285 var res = self.new_var(mtype)
1286 compiler.undead_types.add(mtype)
1287 self.require_declaration("BOX_{valtype.c_name}")
1288 self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
1289 self.require_declaration("type_{mtype.c_name}")
1290 self.add("{res}->type = &type_{mtype.c_name};")
1291 self.require_declaration("class_{mtype.c_name}")
1292 self.add("{res}->class = &class_{mtype.c_name};")
1293 return res
1294 else
1295 return value
1296 end
1297 end
1298
1299 # Returns a C expression containing the tag of the value as a long.
1300 #
1301 # If the C expression is evaluated to 0, it means there is no tag.
1302 # Thus the expression can be used as a condition.
1303 fun extract_tag(value: RuntimeVariable): String
1304 do
1305 assert not value.mtype.is_c_primitive
1306 return "((long){value}&3)" # Get the two low bits
1307 end
1308
1309 # Returns a C expression of the runtime class structure of the value.
1310 # The point of the method is to work also with primitive types.
1311 fun class_info(value: RuntimeVariable): String
1312 do
1313 if not value.mtype.is_c_primitive then
1314 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1315 var tag = extract_tag(value)
1316 return "({tag}?class_info[{tag}]:{value}->class)"
1317 end
1318 return "{value}->class"
1319 else
1320 compiler.undead_types.add(value.mtype)
1321 self.require_declaration("class_{value.mtype.c_name}")
1322 return "(&class_{value.mtype.c_name})"
1323 end
1324 end
1325
1326 # Returns a C expression of the runtime type structure of the value.
1327 # The point of the method is to work also with primitive types.
1328 fun type_info(value: RuntimeVariable): String
1329 do
1330 if not value.mtype.is_c_primitive then
1331 if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1332 var tag = extract_tag(value)
1333 return "({tag}?type_info[{tag}]:{value}->type)"
1334 end
1335 return "{value}->type"
1336 else
1337 compiler.undead_types.add(value.mtype)
1338 self.require_declaration("type_{value.mtype.c_name}")
1339 return "(&type_{value.mtype.c_name})"
1340 end
1341 end
1342
1343 redef fun compile_callsite(callsite, args)
1344 do
1345 var rta = compiler.runtime_type_analysis
1346 # TODO: Inlining of new-style constructors with initializers
1347 if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then
1348 var tgs = rta.live_targets(callsite)
1349 if tgs.length == 1 then
1350 return direct_call(tgs.first, args)
1351 end
1352 end
1353 # Shortcut intern methods as they are not usually redefinable
1354 if callsite.mpropdef.is_intern and callsite.mproperty.name != "object_id" then
1355 # `object_id` is the only redefined intern method, so it can not be directly called.
1356 # TODO find a less ugly approach?
1357 return direct_call(callsite.mpropdef, args)
1358 end
1359 return super
1360 end
1361
1362 # Fully and directly call a mpropdef
1363 #
1364 # This method is used by `compile_callsite`
1365 private fun direct_call(mpropdef: MMethodDef, args: Array[RuntimeVariable]): nullable RuntimeVariable
1366 do
1367 var res0 = before_send(mpropdef.mproperty, args)
1368 var res = call(mpropdef, mpropdef.mclassdef.bound_mtype, args)
1369 if res0 != null then
1370 assert res != null
1371 self.assign(res0, res)
1372 res = res0
1373 end
1374 add("\}") # close the before_send
1375 return res
1376 end
1377 redef fun send(mmethod, arguments)
1378 do
1379 if arguments.first.mcasttype.is_c_primitive then
1380 # In order to shortcut the primitive, we need to find the most specific method
1381 # Howverr, because of performance (no flattening), we always work on the realmainmodule
1382 var m = self.compiler.mainmodule
1383 self.compiler.mainmodule = self.compiler.realmainmodule
1384 var res = self.monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
1385 self.compiler.mainmodule = m
1386 return res
1387 end
1388
1389 return table_send(mmethod, arguments, mmethod)
1390 end
1391
1392 # Handle common special cases before doing the effective method invocation
1393 # This methods handle the `==` and `!=` methods and the case of the null receiver.
1394 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
1395 # Client must not forget to close the } after them.
1396 #
1397 # The value returned is the result of the common special cases.
1398 # If not null, client must compile it with the result of their own effective method invocation.
1399 #
1400 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
1401 # is generated to cancel the effective method invocation that will follow
1402 # TODO: find a better approach
1403 private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1404 do
1405 var res: nullable RuntimeVariable = null
1406 var recv = arguments.first
1407 var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!="
1408 if maybenull(recv) and consider_null then
1409 self.add("if ({recv} == NULL) \{")
1410 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
1411 res = self.new_var(bool_type)
1412 var arg = arguments[1]
1413 if arg.mcasttype isa MNullableType then
1414 self.add("{res} = ({arg} == NULL);")
1415 else if arg.mcasttype isa MNullType then
1416 self.add("{res} = 1; /* is null */")
1417 else
1418 self.add("{res} = 0; /* {arg.inspect} cannot be null */")
1419 end
1420 else if mmethod.name == "!=" then
1421 res = self.new_var(bool_type)
1422 var arg = arguments[1]
1423 if arg.mcasttype isa MNullableType then
1424 self.add("{res} = ({arg} != NULL);")
1425 else if arg.mcasttype isa MNullType then
1426 self.add("{res} = 0; /* is null */")
1427 else
1428 self.add("{res} = 1; /* {arg.inspect} cannot be null */")
1429 end
1430 else
1431 self.add_abort("Receiver is null")
1432 end
1433 self.add("\} else \{")
1434 else
1435 self.add("\{")
1436 end
1437 if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then
1438 # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants)
1439 var arg = arguments[1]
1440 if arg.mcasttype isa MNullType then
1441 if res == null then res = self.new_var(bool_type)
1442 if mmethod.name == "!=" then
1443 self.add("{res} = 1; /* arg is null and recv is not */")
1444 else # `==` and `is_same_instance`
1445 self.add("{res} = 0; /* arg is null but recv is not */")
1446 end
1447 self.add("\}") # closes the null case
1448 self.add("if (0) \{") # what follow is useless, CC will drop it
1449 end
1450 end
1451 return res
1452 end
1453
1454 private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], mentity: MEntity): nullable RuntimeVariable
1455 do
1456 compiler.modelbuilder.nb_invok_by_tables += 1
1457 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
1458
1459 assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
1460
1461 var res0 = before_send(mmethod, arguments)
1462
1463 var runtime_function = mmethod.intro.virtual_runtime_function
1464 var msignature = runtime_function.called_signature
1465
1466 adapt_signature(mmethod.intro, arguments)
1467
1468 var res: nullable RuntimeVariable
1469 var ret = msignature.return_mtype
1470 if ret == null then
1471 res = null
1472 else
1473 res = self.new_var(ret)
1474 end
1475
1476 var ss = arguments.join(", ")
1477
1478 var const_color = mentity.const_color
1479 var ress
1480 if res != null then
1481 ress = "{res} = "
1482 else
1483 ress = ""
1484 end
1485 if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_direct_call_monomorph0.value then
1486 # opt_direct_call_monomorph0 is used to compare the efficiency of the alternative lookup implementation, ceteris paribus.
1487 # The difference with the non-zero option is that the monomorphism is looked-at on the mmethod level and not at the callsite level.
1488 # TODO: remove this mess and use per callsite service to detect monomorphism in a single place.
1489 var md = compiler.is_monomorphic(mentity)
1490 if md != null then
1491 var callsym = md.virtual_runtime_function.c_name
1492 self.require_declaration(callsym)
1493 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1494 else
1495 self.require_declaration(const_color)
1496 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1497 end
1498 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_guard_call.value then
1499 var callsym = "CALL_" + const_color
1500 self.require_declaration(callsym)
1501 self.add "if (!{callsym}) \{"
1502 self.require_declaration(const_color)
1503 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1504 self.add "\} else \{"
1505 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1506 self.add "\}"
1507 else if mentity isa MMethod and compiler.modelbuilder.toolcontext.opt_trampoline_call.value then
1508 var callsym = "CALL_" + const_color
1509 self.require_declaration(callsym)
1510 self.add "{ress}{callsym}({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1511 else
1512 self.require_declaration(const_color)
1513 self.add "{ress}(({runtime_function.c_funptrtype})({class_info(arguments.first)}->vft[{const_color}]))({ss}); /* {mmethod} on {arguments.first.inspect}*/"
1514 end
1515
1516 if res0 != null then
1517 assert res != null
1518 assign(res0,res)
1519 res = res0
1520 end
1521
1522 self.add("\}") # closes the null case
1523
1524 return res
1525 end
1526
1527 redef fun call(mmethoddef, recvtype, arguments)
1528 do
1529 assert arguments.length == mmethoddef.msignature.arity + 1 else debug("Invalid arity for {mmethoddef}. {arguments.length} arguments given.")
1530
1531 var res: nullable RuntimeVariable
1532 var ret = mmethoddef.msignature.return_mtype
1533 if ret == null then
1534 res = null
1535 else
1536 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
1537 res = self.new_var(ret)
1538 end
1539
1540 if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
1541 (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
1542 compiler.modelbuilder.nb_invok_by_inline += 1
1543 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
1544 var frame = new StaticFrame(self, mmethoddef, recvtype, arguments)
1545 frame.returnlabel = self.get_name("RET_LABEL")
1546 frame.returnvar = res
1547 var old_frame = self.frame
1548 self.frame = frame
1549 self.add("\{ /* Inline {mmethoddef} ({arguments.join(",")}) on {arguments.first.inspect} */")
1550 mmethoddef.compile_inside_to_c(self, arguments)
1551 self.add("{frame.returnlabel.as(not null)}:(void)0;")
1552 self.add("\}")
1553 self.frame = old_frame
1554 return res
1555 end
1556 compiler.modelbuilder.nb_invok_by_direct += 1
1557 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
1558
1559 # Autobox arguments
1560 self.adapt_signature(mmethoddef, arguments)
1561
1562 self.require_declaration(mmethoddef.c_name)
1563 if res == null then
1564 self.add("{mmethoddef.c_name}({arguments.join(", ")}); /* Direct call {mmethoddef} on {arguments.first.inspect}*/")
1565 return null
1566 else
1567 self.add("{res} = {mmethoddef.c_name}({arguments.join(", ")});")
1568 end
1569
1570 return res
1571 end
1572
1573 redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
1574 do
1575 if arguments.first.mcasttype.is_c_primitive then
1576 # In order to shortcut the primitive, we need to find the most specific method
1577 # However, because of performance (no flattening), we always work on the realmainmodule
1578 var main = self.compiler.mainmodule
1579 self.compiler.mainmodule = self.compiler.realmainmodule
1580 var res = self.monomorphic_super_send(m, recvtype, arguments)
1581 self.compiler.mainmodule = main
1582 return res
1583 end
1584 return table_send(m.mproperty, arguments, m)
1585 end
1586
1587 redef fun vararg_instance(mpropdef, recv, varargs, elttype)
1588 do
1589 # A vararg must be stored into an new array
1590 # The trick is that the dymaic type of the array may depends on the receiver
1591 # of the method (ie recv) if the static type is unresolved
1592 # This is more complex than usual because the unresolved type must not be resolved
1593 # with the current receiver (ie self).
1594 # Therefore to isolate the resolution from self, a local StaticFrame is created.
1595 # One can see this implementation as an inlined method of the receiver whose only
1596 # job is to allocate the array
1597 var old_frame = self.frame
1598 var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
1599 self.frame = frame
1600 #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
1601 var res = self.array_instance(varargs, elttype)
1602 self.frame = old_frame
1603 return res
1604 end
1605
1606 redef fun isset_attribute(a, recv)
1607 do
1608 self.check_recv_notnull(recv)
1609 var res = self.new_var(bool_type)
1610
1611 # What is the declared type of the attribute?
1612 var mtype = a.intro.static_mtype.as(not null)
1613 var intromclassdef = a.intro.mclassdef
1614 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1615
1616 if mtype isa MNullableType then
1617 self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
1618 return res
1619 end
1620
1621 self.require_declaration(a.const_color)
1622 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1623 self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
1624 else
1625
1626 if not mtype.is_c_primitive and not mtype.is_tagged then
1627 self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
1628 else
1629 self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
1630 end
1631 end
1632 return res
1633 end
1634
1635 redef fun read_attribute(a, recv)
1636 do
1637 self.check_recv_notnull(recv)
1638
1639 # What is the declared type of the attribute?
1640 var ret = a.intro.static_mtype.as(not null)
1641 var intromclassdef = a.intro.mclassdef
1642 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1643
1644 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1645 self.compiler.attr_read_count += 1
1646 self.add("count_attr_reads++;")
1647 end
1648
1649 self.require_declaration(a.const_color)
1650 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1651 # Get the attribute or a box (ie. always a val*)
1652 var cret = self.object_type.as_nullable
1653 var res = self.new_var(cret)
1654 res.mcasttype = ret
1655
1656 self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
1657
1658 # Check for Uninitialized attribute
1659 if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1660 self.add("if (unlikely({res} == NULL)) \{")
1661 self.add_abort("Uninitialized attribute {a.name}")
1662 self.add("\}")
1663
1664 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1665 self.compiler.isset_checks_count += 1
1666 self.add("count_isset_checks++;")
1667 end
1668 end
1669
1670 # Return the attribute or its unboxed version
1671 # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
1672 return self.autobox(res, ret)
1673 else
1674 var res = self.new_var(ret)
1675 self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
1676
1677 # Check for Uninitialized attribute
1678 if not ret.is_c_primitive and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
1679 self.add("if (unlikely({res} == NULL)) \{")
1680 self.add_abort("Uninitialized attribute {a.name}")
1681 self.add("\}")
1682 if self.compiler.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
1683 self.compiler.isset_checks_count += 1
1684 self.add("count_isset_checks++;")
1685 end
1686 end
1687
1688 return res
1689 end
1690 end
1691
1692 redef fun write_attribute(a, recv, value)
1693 do
1694 self.check_recv_notnull(recv)
1695
1696 # What is the declared type of the attribute?
1697 var mtype = a.intro.static_mtype.as(not null)
1698 var intromclassdef = a.intro.mclassdef
1699 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
1700
1701 # Adapt the value to the declared type
1702 value = self.autobox(value, mtype)
1703
1704 self.require_declaration(a.const_color)
1705 if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
1706 var attr = "{recv}->attrs[{a.const_color}]"
1707 if mtype.is_tagged then
1708 # The attribute is not primitive, thus store it as tagged
1709 var tv = autobox(value, compiler.mainmodule.object_type)
1710 self.add("{attr} = {tv}; /* {a} on {recv.inspect} */")
1711 else if mtype.is_c_primitive then
1712 assert mtype isa MClassType
1713 # The attribute is primitive, thus we store it in a box
1714 # The trick is to create the box the first time then resuse the box
1715 self.add("if ({attr} != NULL) \{")
1716 self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
1717 self.add("\} else \{")
1718 value = self.autobox(value, self.object_type.as_nullable)
1719 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1720 self.add("\}")
1721 else
1722 # The attribute is not primitive, thus store it direclty
1723 self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
1724 end
1725 else
1726 self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
1727 end
1728 end
1729
1730 # Check that mtype is a live open type
1731 fun hardening_live_open_type(mtype: MType)
1732 do
1733 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1734 self.require_declaration(mtype.const_color)
1735 var col = mtype.const_color
1736 self.add("if({col} == -1) \{")
1737 self.add("PRINT_ERROR(\"Resolution of a dead open type: %s\\n\", \"{mtype.to_s.escape_to_c}\");")
1738 self.add_abort("open type dead")
1739 self.add("\}")
1740 end
1741
1742 # Check that mtype it a pointer to a live cast type
1743 fun hardening_cast_type(t: String)
1744 do
1745 if not compiler.modelbuilder.toolcontext.opt_hardening.value then return
1746 add("if({t} == NULL) \{")
1747 add_abort("cast type null")
1748 add("\}")
1749 add("if({t}->id == -1 || {t}->color == -1) \{")
1750 add("PRINT_ERROR(\"Try to cast on a dead cast type: %s\\n\", {t}->name);")
1751 add_abort("cast type dead")
1752 add("\}")
1753 end
1754
1755 redef fun init_instance(mtype)
1756 do
1757 self.require_declaration("NEW_{mtype.mclass.c_name}")
1758 var compiler = self.compiler
1759 if mtype isa MGenericType and mtype.need_anchor then
1760 hardening_live_open_type(mtype)
1761 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1762 var recv = self.frame.arguments.first
1763 var recv_type_info = self.type_info(recv)
1764 self.require_declaration(mtype.const_color)
1765 return self.new_expr("NEW_{mtype.mclass.c_name}({recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
1766 end
1767 compiler.undead_types.add(mtype)
1768 self.require_declaration("type_{mtype.c_name}")
1769 return self.new_expr("NEW_{mtype.mclass.c_name}(&type_{mtype.c_name})", mtype)
1770 end
1771
1772 redef fun type_test(value, mtype, tag)
1773 do
1774 self.add("/* {value.inspect} isa {mtype} */")
1775 var compiler = self.compiler
1776
1777 var recv = self.frame.arguments.first
1778 var recv_type_info = self.type_info(recv)
1779
1780 var res = self.new_var(bool_type)
1781
1782 var cltype = self.get_name("cltype")
1783 self.add_decl("int {cltype};")
1784 var idtype = self.get_name("idtype")
1785 self.add_decl("int {idtype};")
1786
1787 var maybe_null = self.maybe_null(value)
1788 var accept_null = "0"
1789 var ntype = mtype
1790 if ntype isa MNullableType then
1791 ntype = ntype.mtype
1792 accept_null = "1"
1793 end
1794
1795 if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
1796 self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
1797 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1798 self.compiler.count_type_test_skipped[tag] += 1
1799 self.add("count_type_test_skipped_{tag}++;")
1800 end
1801 return res
1802 end
1803
1804 if ntype.need_anchor then
1805 var type_struct = self.get_name("type_struct")
1806 self.add_decl("const struct type* {type_struct};")
1807
1808 # Either with resolution_table with a direct resolution
1809 hardening_live_open_type(mtype)
1810 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
1811 self.require_declaration(mtype.const_color)
1812 self.add("{type_struct} = {recv_type_info}->resolution_table->types[{mtype.const_color}];")
1813 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1814 self.compiler.count_type_test_unresolved[tag] += 1
1815 self.add("count_type_test_unresolved_{tag}++;")
1816 end
1817 hardening_cast_type(type_struct)
1818 self.add("{cltype} = {type_struct}->color;")
1819 self.add("{idtype} = {type_struct}->id;")
1820 if maybe_null and accept_null == "0" then
1821 var is_nullable = self.get_name("is_nullable")
1822 self.add_decl("short int {is_nullable};")
1823 self.add("{is_nullable} = {type_struct}->is_nullable;")
1824 accept_null = is_nullable.to_s
1825 end
1826 else if ntype isa MClassType then
1827 compiler.undead_types.add(mtype)
1828 self.require_declaration("type_{mtype.c_name}")
1829 hardening_cast_type("(&type_{mtype.c_name})")
1830 self.add("{cltype} = type_{mtype.c_name}.color;")
1831 self.add("{idtype} = type_{mtype.c_name}.id;")
1832 if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
1833 self.compiler.count_type_test_resolved[tag] += 1
1834 self.add("count_type_test_resolved_{tag}++;")
1835 end
1836 else
1837 self.add("PRINT_ERROR(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); fatal_exit(1);")
1838 end
1839
1840 # check color is in table
1841 if maybe_null then
1842 self.add("if({value} == NULL) \{")
1843 self.add("{res} = {accept_null};")
1844 self.add("\} else \{")
1845 end
1846 var value_type_info = self.type_info(value)
1847 self.add("if({cltype} >= {value_type_info}->table_size) \{")
1848 self.add("{res} = 0;")
1849 self.add("\} else \{")
1850 self.add("{res} = {value_type_info}->type_table[{cltype}] == {idtype};")
1851 self.add("\}")
1852 if maybe_null then
1853 self.add("\}")
1854 end
1855
1856 return res
1857 end
1858
1859 redef fun is_same_type_test(value1, value2)
1860 do
1861 var res = self.new_var(bool_type)
1862 # Swap values to be symetric
1863 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1864 var tmp = value1
1865 value1 = value2
1866 value2 = tmp
1867 end
1868 if value1.mtype.is_c_primitive then
1869 if value2.mtype == value1.mtype then
1870 self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
1871 else if value2.mtype.is_c_primitive then
1872 self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
1873 else
1874 var mtype1 = value1.mtype.as(MClassType)
1875 self.require_declaration("class_{mtype1.c_name}")
1876 self.add("{res} = ({value2} != NULL) && ({class_info(value2)} == &class_{mtype1.c_name}); /* is_same_type_test */")
1877 end
1878 else
1879 self.add("{res} = ({value1} == {value2}) || ({value1} != NULL && {value2} != NULL && {class_info(value1)} == {class_info(value2)}); /* is_same_type_test */")
1880 end
1881 return res
1882 end
1883
1884 redef fun class_name_string(value)
1885 do
1886 var res = self.get_name("var_class_name")
1887 self.add_decl("const char* {res};")
1888 if not value.mtype.is_c_primitive then
1889 self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
1890 else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
1891 value.mtype.as(MClassType).name != "CString" then
1892 self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
1893 else
1894 self.require_declaration("type_{value.mtype.c_name}")
1895 self.add "{res} = type_{value.mtype.c_name}.name;"
1896 end
1897 return res
1898 end
1899
1900 redef fun equal_test(value1, value2)
1901 do
1902 var res = self.new_var(bool_type)
1903 if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
1904 var tmp = value1
1905 value1 = value2
1906 value2 = tmp
1907 end
1908 if value1.mtype.is_c_primitive then
1909 var t1 = value1.mtype
1910 assert t1 == value1.mcasttype
1911
1912 # Fast case: same C type.
1913 if value2.mtype == t1 then
1914 # Same exact C primitive representation.
1915 self.add("{res} = {value1} == {value2};")
1916 return res
1917 end
1918
1919 # Complex case: value2 has a different representation
1920 # Thus, it should be checked if `value2` is type-compatible with `value1`
1921 # This compatibility is done statically if possible and dynamically else
1922
1923 # Conjunction (ands) of dynamic tests according to the static knowledge
1924 var tests = new Array[String]
1925
1926 var t2 = value2.mcasttype
1927 if t2 isa MNullableType then
1928 # The destination type cannot be null
1929 tests.add("({value2} != NULL)")
1930 t2 = t2.mtype
1931 else if t2 isa MNullType then
1932 # `value2` is known to be null, thus incompatible with a primitive
1933 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
1934 return res
1935 end
1936
1937 if t2 == t1 then
1938 # Same type but different representation.
1939 else if t2.is_c_primitive then
1940 # Type of `value2` is a different primitive type, thus incompatible
1941 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}*/")
1942 return res
1943 else if t1.is_tagged then
1944 # To be equal, `value2` should also be correctly tagged
1945 tests.add("({extract_tag(value2)} == {t1.tag_value})")
1946 else
1947 # To be equal, `value2` should also be boxed with the same class
1948 self.require_declaration("class_{t1.c_name}")
1949 tests.add "({class_info(value2)} == &class_{t1.c_name})"
1950 end
1951
1952 # Compare the unboxed `value2` with `value1`
1953 if tests.not_empty then
1954 self.add "if ({tests.join(" && ")}) \{"
1955 end
1956 self.add "{res} = {self.autobox(value2, t1)} == {value1};"
1957 if tests.not_empty then
1958 self.add "\} else {res} = 0;"
1959 end
1960
1961 return res
1962 end
1963 var maybe_null = true
1964 var test = new Array[String]
1965 var t1 = value1.mcasttype
1966 if t1 isa MNullableType then
1967 test.add("{value1} != NULL")
1968 t1 = t1.mtype
1969 else
1970 maybe_null = false
1971 end
1972 var t2 = value2.mcasttype
1973 if t2 isa MNullableType then
1974 test.add("{value2} != NULL")
1975 t2 = t2.mtype
1976 else
1977 maybe_null = false
1978 end
1979
1980 var incompatible = false
1981 var primitive
1982 if t1.is_c_primitive then
1983 primitive = t1
1984 if t1 == t2 then
1985 # No need to compare class
1986 else if t2.is_c_primitive then
1987 incompatible = true
1988 else if can_be_primitive(value2) then
1989 if t1.is_tagged then
1990 self.add("{res} = {value1} == {value2};")
1991 return res
1992 end
1993 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
1994 test.add("(!{extract_tag(value2)})")
1995 end
1996 test.add("{value1}->class == {value2}->class")
1997 else
1998 incompatible = true
1999 end
2000 else if t2.is_c_primitive then
2001 primitive = t2
2002 if can_be_primitive(value1) then
2003 if t2.is_tagged then
2004 self.add("{res} = {value1} == {value2};")
2005 return res
2006 end
2007 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
2008 test.add("(!{extract_tag(value1)})")
2009 end
2010 test.add("{value1}->class == {value2}->class")
2011 else
2012 incompatible = true
2013 end
2014 else
2015 primitive = null
2016 end
2017
2018 if incompatible then
2019 if maybe_null then
2020 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
2021 return res
2022 else
2023 self.add("{res} = 0; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
2024 return res
2025 end
2026 end
2027 if primitive != null then
2028 if primitive.is_tagged then
2029 self.add("{res} = {value1} == {value2};")
2030 return res
2031 end
2032 test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
2033 else if can_be_primitive(value1) and can_be_primitive(value2) then
2034 if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
2035 test.add("(!{extract_tag(value1)}) && (!{extract_tag(value2)})")
2036 end
2037 test.add("{value1}->class == {value2}->class")
2038 var s = new Array[String]
2039 for t, v in self.compiler.box_kinds do
2040 if t.mclass_type.is_tagged then continue
2041 s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
2042 end
2043 if s.is_empty then
2044 self.add("{res} = {value1} == {value2};")
2045 return res
2046 end
2047 test.add("({s.join(" || ")})")
2048 else
2049 self.add("{res} = {value1} == {value2};")
2050 return res
2051 end
2052 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
2053 return res
2054 end
2055
2056 fun can_be_primitive(value: RuntimeVariable): Bool
2057 do
2058 var t = value.mcasttype.undecorate
2059 if not t isa MClassType then return false
2060 var k = t.mclass.kind
2061 return k == interface_kind or t.is_c_primitive
2062 end
2063
2064 fun maybe_null(value: RuntimeVariable): Bool
2065 do
2066 var t = value.mcasttype
2067 return t isa MNullableType or t isa MNullType
2068 end
2069
2070 redef fun array_instance(array, elttype)
2071 do
2072 var nclass = mmodule.native_array_class
2073 var arrayclass = mmodule.array_class
2074 var arraytype = arrayclass.get_mtype([elttype])
2075 var res = self.init_instance(arraytype)
2076 self.add("\{ /* {res} = array_instance Array[{elttype}] */")
2077 var length = self.int_instance(array.length)
2078 var nat = native_array_instance(elttype, length)
2079 for i in [0..array.length[ do
2080 var r = self.autobox(array[i], self.object_type)
2081 self.add("((struct instance_{nclass.c_name}*){nat})->values[{i}] = (val*) {r};")
2082 end
2083 self.send(self.get_property("with_native", arrayclass.intro.bound_mtype), [res, nat, length])
2084 self.add("\}")
2085 return res
2086 end
2087
2088 redef fun native_array_instance(elttype, length)
2089 do
2090 var mtype = mmodule.native_array_type(elttype)
2091 self.require_declaration("NEW_{mtype.mclass.c_name}")
2092 assert mtype isa MGenericType
2093 var compiler = self.compiler
2094 length = autobox(length, compiler.mainmodule.int_type)
2095 if mtype.need_anchor then
2096 hardening_live_open_type(mtype)
2097 link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
2098 var recv = self.frame.arguments.first
2099 var recv_type_info = self.type_info(recv)
2100 self.require_declaration(mtype.const_color)
2101 return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, {recv_type_info}->resolution_table->types[{mtype.const_color}])", mtype)
2102 end
2103 compiler.undead_types.add(mtype)
2104 self.require_declaration("type_{mtype.c_name}")
2105 return self.new_expr("NEW_{mtype.mclass.c_name}((int){length}, &type_{mtype.c_name})", mtype)
2106 end
2107
2108 redef fun native_array_def(pname, ret_type, arguments)
2109 do
2110 var elttype = arguments.first.mtype
2111 var nclass = mmodule.native_array_class
2112 var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
2113 if pname == "[]" then
2114 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2115 var res = self.new_expr("{recv}[{arguments[1]}]", compiler.mainmodule.object_type)
2116 res.mcasttype = ret_type.as(not null)
2117 self.ret(res)
2118 return true
2119 else if pname == "[]=" then
2120 self.add("{recv}[{arguments[1]}]={arguments[2]};")
2121 return true
2122 else if pname == "length" then
2123 self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
2124 return true
2125 else if pname == "copy_to" then
2126 var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
2127 self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
2128 return true
2129 else if pname == "memmove" then
2130 # fun memmove(start: Int, length: Int, dest: NativeArray[E], dest_start: Int) is intern do
2131 var recv1 = "((struct instance_{nclass.c_name}*){arguments[3]})->values"
2132 self.add("memmove({recv1}+{arguments[4]}, {recv}+{arguments[1]}, {arguments[2]}*sizeof({elttype.ctype}));")
2133 return true
2134 end
2135 return false
2136 end
2137
2138 redef fun native_array_get(nat, i)
2139 do
2140 var nclass = mmodule.native_array_class
2141 var recv = "((struct instance_{nclass.c_name}*){nat})->values"
2142 # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
2143 var res = self.new_expr("{recv}[{i}]", compiler.mainmodule.object_type)
2144 return res
2145 end
2146
2147 redef fun native_array_set(nat, i, val)
2148 do
2149 var nclass = mmodule.native_array_class
2150 var recv = "((struct instance_{nclass.c_name}*){nat})->values"
2151 self.add("{recv}[{i}]={val};")
2152 end
2153
2154 fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
2155 assert mtype.need_anchor
2156 var compiler = self.compiler
2157 if not compiler.live_unresolved_types.has_key(self.frame.mpropdef.mclassdef) then
2158 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef] = new HashSet[MType]
2159 end
2160 compiler.live_unresolved_types[self.frame.mpropdef.mclassdef].add(mtype)
2161 end
2162 end
2163
2164 redef class MMethodDef
2165 # The C function associated to a mmethoddef
2166 fun separate_runtime_function: SeparateRuntimeFunction
2167 do
2168 var res = self.separate_runtime_function_cache
2169 if res == null then
2170 var recv = mclassdef.bound_mtype
2171 var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
2172 res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
2173 self.separate_runtime_function_cache = res
2174 end
2175 return res
2176 end
2177 private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
2178
2179 # The C function associated to a mmethoddef, that can be stored into a VFT of a class
2180 # The first parameter (the reciever) is always typed by val* in order to accept an object value
2181 # The C-signature is always compatible with the intro
2182 fun virtual_runtime_function: SeparateRuntimeFunction
2183 do
2184 var res = self.virtual_runtime_function_cache
2185 if res == null then
2186 # Because the function is virtual, the signature must match the one of the original class
2187 var intromclassdef = mproperty.intro.mclassdef
2188 var recv = intromclassdef.bound_mtype
2189
2190 res = separate_runtime_function
2191 if res.called_recv == recv then
2192 self.virtual_runtime_function_cache = res
2193 return res
2194 end
2195
2196 var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
2197
2198 if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
2199 self.virtual_runtime_function_cache = res
2200 return res
2201 end
2202
2203 res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
2204 self.virtual_runtime_function_cache = res
2205 res.is_thunk = true
2206 end
2207 return res
2208 end
2209 private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
2210 end
2211
2212 redef class MSignature
2213 # Does the C-version of `self` the same than the C-version of `other`?
2214 fun c_equiv(other: MSignature): Bool
2215 do
2216 if self == other then return true
2217 if arity != other.arity then return false
2218 for i in [0..arity[ do
2219 if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
2220 end
2221 if return_mtype != other.return_mtype then
2222 if return_mtype == null or other.return_mtype == null then return false
2223 if return_mtype.ctype != other.return_mtype.ctype then return false
2224 end
2225 return true
2226 end
2227 end
2228
2229 # The C function associated to a methoddef separately compiled
2230 class SeparateRuntimeFunction
2231 super AbstractRuntimeFunction
2232
2233 # The call-side static receiver
2234 var called_recv: MType
2235
2236 # The call-side static signature
2237 var called_signature: MSignature
2238
2239 # The name on the compiled method
2240 redef var build_c_name: String
2241
2242 # Statically call the original body instead
2243 var is_thunk = false
2244
2245 redef fun to_s do return self.mmethoddef.to_s
2246
2247 # The C return type (something or `void`)
2248 var c_ret: String is lazy do
2249 var ret = called_signature.return_mtype
2250 if ret != null then
2251 return ret.ctype
2252 else
2253 return "void"
2254 end
2255 end
2256
2257 # The C signature (only the parmeter part)
2258 var c_sig: String is lazy do
2259 var sig = new FlatBuffer
2260 sig.append("({called_recv.ctype} self")
2261 for i in [0..called_signature.arity[ do
2262 var mp = called_signature.mparameters[i]
2263 var mtype = mp.mtype
2264 if mp.is_vararg then
2265 mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
2266 end
2267 sig.append(", {mtype.ctype} p{i}")
2268 end
2269 sig.append(")")
2270 return sig.to_s
2271 end
2272
2273 # The C type for the function pointer.
2274 var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
2275
2276 redef fun compile_to_c(compiler)
2277 do
2278 var mmethoddef = self.mmethoddef
2279
2280 var sig = "{c_ret} {c_name}{c_sig}"
2281 compiler.provide_declaration(self.c_name, "{sig};")
2282
2283 var rta = compiler.as(SeparateCompiler).runtime_type_analysis
2284
2285 var recv = self.mmethoddef.mclassdef.bound_mtype
2286 var v = compiler.new_visitor
2287 var selfvar = new RuntimeVariable("self", called_recv, recv)
2288 var arguments = new Array[RuntimeVariable]
2289 var frame = new StaticFrame(v, mmethoddef, recv, arguments)
2290 v.frame = frame
2291
2292 var msignature = called_signature
2293 var ret = called_signature.return_mtype
2294
2295 var comment = new FlatBuffer
2296 comment.append("({selfvar}: {selfvar.mtype}")
2297 arguments.add(selfvar)
2298 for i in [0..msignature.arity[ do
2299 var mp = msignature.mparameters[i]
2300 var mtype = mp.mtype
2301 if mp.is_vararg then
2302 mtype = v.mmodule.array_type(mtype)
2303 end
2304 comment.append(", {mtype}")
2305 var argvar = new RuntimeVariable("p{i}", mtype, mtype)
2306 arguments.add(argvar)
2307 end
2308 comment.append(")")
2309 if ret != null then
2310 comment.append(": {ret}")
2311 end
2312
2313 v.add_decl("/* method {self} for {comment} */")
2314 v.add_decl("{sig} \{")
2315 if ret != null then
2316 frame.returnvar = v.new_var(ret)
2317 end
2318 frame.returnlabel = v.get_name("RET_LABEL")
2319
2320 if is_thunk then
2321 var subret = v.call(mmethoddef, recv, arguments)
2322 if ret != null then
2323 assert subret != null
2324 v.assign(frame.returnvar.as(not null), subret)
2325 end
2326 else if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
2327 v.add_abort("FATAL: Dead method executed.")
2328 else
2329 mmethoddef.compile_inside_to_c(v, arguments)
2330 end
2331
2332 v.add("{frame.returnlabel.as(not null)}:;")
2333 if ret != null then
2334 v.add("return {frame.returnvar.as(not null)};")
2335 end
2336 v.add("\}")
2337 compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
2338 end
2339
2340 # Compile the trampolines used to implement late-binding.
2341 #
2342 # See `opt_trampoline_call`.
2343 fun compile_trampolines(compiler: SeparateCompiler)
2344 do
2345 var recv = self.mmethoddef.mclassdef.bound_mtype
2346 var selfvar = new RuntimeVariable("self", called_recv, recv)
2347 var ret = called_signature.return_mtype
2348 var arguments = ["self"]
2349 for i in [0..called_signature.arity[ do arguments.add "p{i}"
2350
2351 if mmethoddef.is_intro and not recv.is_c_primitive then
2352 var m = mmethoddef.mproperty
2353 var n2 = "CALL_" + m.const_color
2354 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2355 var v2 = compiler.new_visitor
2356 v2.add "{c_ret} {n2}{c_sig} \{"
2357 v2.require_declaration(m.const_color)
2358 var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
2359 if ret != null then
2360 v2.add "return {call}"
2361 else
2362 v2.add call
2363 end
2364
2365 v2.add "\}"
2366
2367 end
2368 if mmethoddef.has_supercall and not recv.is_c_primitive then
2369 var m = mmethoddef
2370 var n2 = "CALL_" + m.const_color
2371 compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
2372 var v2 = compiler.new_visitor
2373 v2.add "{c_ret} {n2}{c_sig} \{"
2374 v2.require_declaration(m.const_color)
2375 var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
2376 if ret != null then
2377 v2.add "return {call}"
2378 else
2379 v2.add call
2380 end
2381
2382 v2.add "\}"
2383 end
2384 end
2385 end
2386
2387 redef class MType
2388 # Are values of `self` tagged?
2389 # If false, it means that the type is not primitive, or is boxed.
2390 var is_tagged = false
2391
2392 # The tag value of the type
2393 #
2394 # ENSURE `is_tagged == (tag_value > 0)`
2395 # ENSURE `not is_tagged == (tag_value == 0)`
2396 var tag_value = 0
2397 end
2398
2399 redef class MEntity
2400 var const_color: String is lazy do return "COLOR_{c_name}"
2401 end
2402
2403 interface PropertyLayoutElement end
2404
2405 redef class MProperty
2406 super PropertyLayoutElement
2407 end
2408
2409 redef class MPropDef
2410 super PropertyLayoutElement
2411 end
2412
2413 redef class AMethPropdef
2414 # The semi-global compilation does not support inlining calls to extern news
2415 redef fun can_inline
2416 do
2417 var m = mpropdef
2418 if m != null and m.mproperty.is_init and m.is_extern then return false
2419 return super
2420 end
2421 end
2422
2423 redef class AAttrPropdef
2424 redef fun init_expr(v, recv)
2425 do
2426 super
2427 if is_lazy and v.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
2428 var guard = self.mlazypropdef.mproperty
2429 v.write_attribute(guard, recv, v.bool_instance(false))
2430 end
2431 end
2432 end