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