75ceb7c1ee7859b1b40b8f16f27c3aef13f13aa5
[nit.git] / src / compiling / compiling_icode.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2009 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Generate C code from intermediate code representation
18 package compiling_icode
19
20 import icode
21 private import analysis
22 import compiling_base
23
24 # Compiler context from ICode to C
25 class I2CCompilerVisitor
26 # Associate things
27 var _ids: HashMap[Object, String] = new HashMap[Object, String]
28 # Associate other things
29 var _ids2: HashMap[Object, String] = new HashMap[Object, String]
30
31 # Return the string associated with a register
32 fun register(e: IRegister): String
33 do
34 if e.stype isa MMTypeNone then return "NIT_NULL"
35 var ids = _ids
36 if closure and not e.is_local then ids = _ids2
37 if ids.has_key(e) then
38 return ids[e]
39 else
40 var i = e.slot_index
41 if i == null then
42 # The register is dead
43 var s = "NIT_NULL"
44 ids[e] = s
45 return s
46 else
47 var s: String
48 var strs: HashMap[Int, String]
49 if e.in_tag_slots then
50 strs = once new HashMap[Int, String]
51 if not strs.has_key(i) then strs[i] = "REGB{i}"
52 else if closure and not e.is_local then
53 strs = once new HashMap[Int, String]
54 if not strs.has_key(i) then strs[i] = "closctx->variable[{i}]"
55 else
56 strs = once new HashMap[Int, String]
57 if not strs.has_key(i) then strs[i] = "fra.me.REG[{i}]"
58 end
59 s = strs[i]
60 ids[e] = s
61 return s
62 end
63 end
64 end
65
66 # Return the strings associated with registers
67 fun registers(a: Collection[IRegister]): Array[String]
68 do
69 var r = new Array[String].with_capacity(a.length)
70 for e in a do
71 r.add(register(e))
72 end
73 return r
74 end
75
76 var _last_number: Int = 0
77 # Give a new unique number (unique for the visitor)
78 fun new_number: Int
79 do
80 _last_number += 1
81 return _last_number
82 end
83
84 # Return the string associated with a escape label
85 fun lab(e: ISeq): String
86 do
87 if _ids.has_key(e) then
88 return _ids[e]
89 else
90 var s = "label{new_number}"
91 _ids[e] = s
92 return s
93 end
94 end
95
96 # The rank (number) of each closure
97 readable var _closures: HashMap[IClosureDecl, Int] = new HashMap[IClosureDecl, Int]
98
99 # The functionnal type of each closure
100 readable var _clostypes: HashMap[IClosureDecl, String] = new HashMap[IClosureDecl, String]
101
102 # label locally accessibles
103 readable writable var _local_labels: HashSet[ISeq] = new HashSet[ISeq]
104
105 # Not local escaped labels
106 # The integer value is an index identifying the label
107 readable writable var _escaped_labels: HashMap[ISeq, Int] = new HashMap[ISeq, Int]
108
109 # Register a escape to a non local label and return an index identifying the label
110 fun register_escape_label(e: ISeq): Int
111 do
112 if _escaped_labels.has_key(e) then
113 return _escaped_labels[e]
114 else
115 var res = _escaped_labels.length + 1
116 _escaped_labels[e] = res
117 return res
118 end
119 end
120
121 # Add a C label mark (if needed)
122 fun add_label(e: ISeq)
123 do
124 if _ids.has_key(e) then
125 add_instr("{_ids[e]}: while(0);")
126 end
127 end
128
129 # Add a goto to a label (even outside a closure)
130 fun add_goto(seq: ISeq)
131 do
132 if local_labels.has(seq) then
133 add_instr("goto {lab(seq)};")
134 else
135 assert closure
136 var ind = register_escape_label(seq)
137 add_instr("closctx->has_broke = (val_t*){ind};")
138 add_instr("goto {lab(return_label.as(not null))};")
139 end
140 end
141
142 # Are we in a closure ?
143 readable writable var _closure: Bool = false
144
145 # The current compiler visitor
146 readable var _visitor: CompilerVisitor
147
148 # The current compiled iroutine
149 readable var _iroutine: IRoutine
150
151 # The return label of the current compiling C function
152 readable writable var _return_label: nullable ISeq = null
153
154 fun add_decl(s: String)
155 do
156 visitor.add_decl(s)
157 end
158
159 fun add_instr(s: String)
160 do
161 var l = _next_location
162 if l != null then
163 visitor.add_instr("/* ", l.file, ":", l.line_start.to_s, " */")
164 _next_location = null
165 end
166 visitor.add_instr(s)
167 end
168
169 fun indent
170 do
171 visitor.indent
172 end
173
174 fun unindent
175 do
176 visitor.unindent
177 end
178
179 fun add_assignment(to, from: String)
180 do
181 visitor.add_assignment(to, from)
182 end
183
184 var _last_location: nullable Location = null
185 var _next_location: nullable Location = null
186
187 # Add location information in a comment
188 # Do nothing if the last location added is the same
189 fun add_location(l: nullable Location)
190 do
191 var last = _last_location
192 if last == l or l == null then return
193 _last_location = l
194 if last != null and last.file == l.file and last.line_start == l.line_start then
195 return
196 else
197 _next_location = l
198 end
199 end
200
201 # The C fonction name of the iroutine
202 readable var _basecname: String
203
204 init(v: CompilerVisitor, ir: IRoutine, cname: String)
205 do
206 _visitor = v
207 _iroutine = ir
208 _basecname = cname
209 end
210 end
211
212 redef class IRoutine
213 # Declare and start a C function that match the routine
214 # Return what must be given to compile_inside_to_c or to compile_to_c
215 # After the method, an openinig { and and indent is added.
216 # So, do not forget to add a sub_context, to unintent and to add a closing }
217 fun compile_signature_to_c(v: CompilerVisitor, cname: String, human_name: nullable String, before_params, after_params: nullable String): Array[String]
218 do
219 var cargs = new Array[String]
220 var cparams = new Array[String]
221 if before_params != null then cparams.add(before_params)
222 for i in [0..params.length[ do
223 cargs.add("p{i}")
224 cparams.add("val_t p{i}")
225 end
226 if closure_decls != null then
227 for i in [0..closure_decls.length[ do
228 var closcn = "CLOS_{cname}_{i}"
229 var cs = closure_decls[i].closure.signature
230 var subparams = new Array[String] # Parameters of the closure
231 subparams.add("struct WBT_ *")
232 for j in [0..cs.arity[ do
233 subparams.add("val_t")
234 end
235 var rr = "void"
236 if cs.return_type != null then rr = "val_t"
237 v.add_decl("typedef {rr} (*{closcn})({subparams.join(", ")});")
238 cargs.add("wd{i}")
239 cparams.add("struct WBT_ *wd{i}")
240 end
241 end
242 if after_params != null then cparams.add(after_params)
243 var r = "void"
244 if result != null then r = "val_t"
245 var p: String
246 if cparams.is_empty then
247 p = "void"
248 else
249 p = cparams.join(", ")
250 end
251 if human_name != null then v.add_decl("#define LOCATE_", cname, " \"", human_name, "\"")
252 v.add_decl(r, " ", cname, "(", p, ");")
253 v.add_decl("typedef ", r, " (*", cname, "_t)(", p, ");")
254 v.add_instr(r, " ", cname, "(", p, ")\{")
255 v.indent
256 return cargs
257 end
258
259 # Compile the body of the routine, return the result value is any
260 fun compile_inside_to_c(v: I2CCompilerVisitor, args: Array[String]): nullable String
261 do
262 # Create and push the stack frame
263 var ll = 0
264 if location != null then
265 ll = location.line_start
266 end
267 # Encapsulate the frame ('me') in a larger structure ('fra') that has enough space to store the local variables (REG)
268 if std_slots_nb > 1 then
269 v.add_decl("struct \{struct stack_frame_t me; val_t MORE_REG[{std_slots_nb-1}];\} fra;")
270 else
271 v.add_decl("struct \{struct stack_frame_t me;\} fra;")
272 end
273 v.add_instr("fra.me.prev = stack_frame_head; stack_frame_head = &fra.me;")
274 v.add_instr("fra.me.file = LOCATE_{v.visitor.module.name};")
275 v.add_instr("fra.me.line = {ll};")
276 v.add_instr("fra.me.meth = LOCATE_{v.basecname};")
277 v.add_instr("fra.me.REG_size = {std_slots_nb};")
278
279 # Declare/initialize local variables
280 for i in [0..std_slots_nb[ do
281 v.add_instr("fra.me.REG[{i}] = NIT_NULL;")
282 end
283 for i in [0..tag_slots_nb[ do
284 v.add_decl("val_t REGB{i};")
285 end
286 var iclosdecls = closure_decls
287 if iclosdecls != null then
288 v.add_decl("struct WBT_ *CREG[{iclosdecls.length}];")
289 else
290 v.add_decl("struct WBT_ **CREG = NULL;")
291 end
292 var k = 0
293 for r in params do
294 if r.slot_index != null then v.add_assignment(v.register(r), args[k])
295 k += 1
296 end
297 if iclosdecls != null then
298 for i in [0..iclosdecls.length[ do
299 var iclosdecl = iclosdecls[i]
300 v.add_instr("CREG[{i}] = {args[params.length+i]};")
301 v.closures[iclosdecl] = i
302 var cs = iclosdecl.closure.signature # Closure signature
303 var subparams = new Array[String] # Parameters of the closure
304 subparams.add("struct WBT_ *")
305 for j in [0..cs.arity[ do
306 var p = "val_t"
307 subparams.add(p)
308 end
309 var r = "void"
310 if cs.return_type != null then r = "val_t"
311 v.clostypes[iclosdecl] = "{r} (*)({subparams.join(", ")})"
312 end
313 end
314 v.add_decl("val_t tmp;")
315
316 # Prepare return
317 var old_rl = v.return_label
318 v.return_label = body
319
320 # Compile body
321 body.compile_to_c(v)
322
323 v.add_instr("stack_frame_head = fra.me.prev;")
324 v.return_label = old_rl
325 var r = result
326 if r != null then
327 return v.register(r)
328 else
329 return null
330 end
331 end
332
333 # Full compilation of the routine
334 # Including optimization and other stuff.
335 # cv must be in the correct function
336 fun compile_to_c(cv: CompilerVisitor, cname: String, args: Array[String]): nullable String
337 do
338 optimize(cv.module)
339 var v = new I2CCompilerVisitor(cv, self, cname)
340 return compile_inside_to_c(v, args)
341 end
342 end
343
344 redef class ICode
345 # Full compilation of the icode
346 fun compile_to_c(v: I2CCompilerVisitor)
347 do
348 v.add_location(location)
349 store_result(v, inner_compile_to_c(v))
350 end
351
352 # Is a result really needed
353 private fun need_result: Bool
354 do
355 var r = result
356 return r != null and r.slot_index != null
357 end
358
359 # Store s in the result value of self
360 private fun store_result(v: I2CCompilerVisitor, s: nullable String)
361 do
362 var r = result
363 if r != null and r.slot_index != null then
364 assert s != null
365 v.add_assignment(v.register(r), s)
366 else if s != null and not is_pure then
367 # ICode with side effects must be evaluated
368 # even if the result is not wanted
369 v.add_instr(s + ";")
370 end
371 end
372
373 # Compilation of without the result assigment
374 # Return the right value is case of expression
375 # Return the full expression (witout ;) in case of statement
376 private fun inner_compile_to_c(v: I2CCompilerVisitor): nullable String is abstract
377 end
378
379 redef class ISeq
380 redef fun inner_compile_to_c(v)
381 do
382 v.local_labels.add(self)
383 for ic in icodes do
384 ic.compile_to_c(v)
385 end
386 v.add_label(self)
387 return null
388 end
389 end
390
391 redef class IIf
392 redef fun inner_compile_to_c(v)
393 do
394 v.add_instr("if (UNTAG_Bool({v.register(expr)})) \{")
395 if not then_seq.icodes.is_empty then
396 v.indent
397 then_seq.inner_compile_to_c(v)
398 v.unindent
399 end
400 if not else_seq.icodes.is_empty then
401 v.add_instr("} else \{")
402 v.indent
403 else_seq.inner_compile_to_c(v)
404 v.unindent
405 end
406 v.add_instr("}")
407 return null
408 end
409 end
410
411 redef class ILoop
412 redef fun inner_compile_to_c(v)
413 do
414 v.local_labels.add(self)
415 v.add_instr("while(1) \{")
416 v.indent
417 for ic in icodes do
418 ic.compile_to_c(v)
419 end
420 v.unindent
421 v.add_instr("}")
422 v.add_label(self)
423 return null
424 end
425 end
426
427 redef class IEscape
428 redef fun inner_compile_to_c(v)
429 do
430 v.add_goto(seq)
431 return null
432 end
433 end
434
435 redef class IAbsCall
436 redef fun compile_to_c(v)
437 do
438 v.add_location(location)
439 var args = v.registers(exprs)
440
441 # Compile closure definitions
442 var old_el = v.escaped_labels
443 var closdefs = closure_defs
444 var closcns: nullable Array[String] = null
445 if closdefs != null then
446 v.escaped_labels = new HashMap[ISeq, Int]
447 closcns = new Array[String]
448 for cd in closdefs do
449 if cd != null then
450 var cn = cd.compile_closure(v)
451 args.add(cn)
452 closcns.add(cn)
453 else
454 args.add("NULL")
455 end
456 end
457 end
458
459 var s = compile_call_to_c(v, args)
460 var r: nullable String = s
461
462 # Intercept escapes
463 if closcns != null then
464 var els = v.escaped_labels
465 v.escaped_labels = old_el
466 # Is there possible escapes?
467 if not els.is_empty then
468 # Call in a tmp variable to avoid 'break' overwrite
469 if need_result then
470 r = "tmp"
471 v.add_assignment(r, s)
472 else
473 r = null
474 v.add_instr(s + ";")
475 end
476 # What is the escape index (if any?)
477 # It's computed as the union of has_broke
478 var switcha = new Array[String]
479 for cn in closcns do
480 switcha.add("((int)({cn}->has_broke))")
481 end
482 var switch = switcha.join(" | ")
483 # What are the expected escape indexes
484 v.add_instr("switch ({switch}) \{")
485 v.indent
486 # No escape occured, continue as usual
487 v.add_instr("case 0: break;")
488 var lls = v.local_labels
489 var iels = els.iterator
490 var forward_escape = false
491 while iels.is_ok do
492 var seq = iels.key
493 if lls.has(seq) then
494 # Local escape occured
495 v.add_instr("case {iels.item}: goto {v.lab(seq)};")
496 else
497 # Forward escape occured: register the escape label
498 assert v.closure
499 v.register_escape_label(seq)
500 forward_escape = true
501 end
502 iels.next
503 end
504 # Forward escape occured, just pass to the next one
505 if forward_escape then
506 v.add_instr("default: closctx->has_broke = (val_t*)({switch}); goto {v.lab(v.return_label.as(not null))};")
507 end
508 v.unindent
509 v.add_instr("\}")
510 end
511 end
512
513 store_result(v, r)
514 end
515
516 redef fun inner_compile_to_c(v) do abort
517
518 # The single invocation witout fancy stuffs
519 private fun compile_call_to_c(v: I2CCompilerVisitor, args: Array[String]): String is abstract
520 end
521
522 redef class ICall
523 redef fun compile_call_to_c(v, args)
524 do
525 var prop = property
526 if prop.global.is_init then args.add("init_table")
527 if prop.name == (once ("add".to_symbol)) and prop.local_class.name == (once ("Array".to_symbol)) then
528 return "{prop.cname}({args.join(", ")})"
529 else
530 return "{prop.global.meth_call}({args[0]})({args.join(", ")})"
531 end
532 end
533 end
534
535 redef class ISuper
536 redef fun compile_call_to_c(v, args)
537 do
538 var prop = property
539 if prop.global.is_init then args.add("init_table")
540 return "{prop.super_meth_call}({args[0]})({args.join(", ")})"
541 end
542 end
543
544 redef class INew
545 redef fun compile_call_to_c(v, args)
546 do
547 return "NEW_{stype.local_class}_{property.global.intro.cname}({args.join(", ")})"
548 end
549 end
550
551 redef class INative
552 redef fun inner_compile_to_c(v)
553 do
554 if exprs.is_empty then
555 return code
556 else
557 var res = new Buffer
558 var i = 0
559 var c = code.split_with("@@@")
560 for s in c do
561 res.append(s)
562 if i < exprs.length and i < c.length-1 then
563 res.append(v.register(exprs[i]))
564 end
565 i += 1
566 end
567 return res.to_s
568 end
569 end
570 end
571
572 redef class IAbort
573 redef fun inner_compile_to_c(v)
574 do
575 var s = new Buffer.from("fprintf(stderr")
576 for t in texts do
577 s.append(", \"{t}\"")
578 end
579 s.append(");")
580 v.add_instr(s.to_s)
581
582 var ll = location
583 s = new Buffer.from("fprintf(stderr, \" (%s")
584 if ll != null then
585 s.append(":%d")
586 end
587 s.append(")\\n\", LOCATE_{module_location.name}")
588 if ll != null then
589 s.append(", {ll.line_start}")
590 end
591 s.append(");")
592 v.add_instr(s.to_s)
593
594 v.add_instr("nit_exit(1);")
595 return null
596 end
597 end
598
599 redef class IMove
600 redef fun inner_compile_to_c(v)
601 do
602 return v.register(expr)
603 end
604 end
605
606 redef class IAttrRead
607 redef fun inner_compile_to_c(v)
608 do
609 return "{property.global.attr_access}({v.register(expr)})"
610 end
611 end
612
613 redef class IAttrIsset
614 redef fun inner_compile_to_c(v)
615 do
616 return "TAG_Bool({property.global.attr_access}({v.register(expr)})!=NIT_NULL)"
617 end
618 end
619
620 redef class IAttrWrite
621 redef fun inner_compile_to_c(v)
622 do
623 v.add_instr("{property.global.attr_access}({v.register(expr1)}) = {v.register(expr2)};")
624 return null
625 end
626 end
627
628 redef class ITypeCheck
629 redef fun inner_compile_to_c(v)
630 do
631 # FIXME handle formaltypes
632 var g = stype.local_class.global
633 var recv = v.register(expr)
634 var s = ""
635 if expr.stype.is_nullable then
636 if stype.is_nullable then
637 s = "({recv}==NIT_NULL) || "
638 else if stype.as_nullable == expr.stype then
639 return "TAG_Bool({recv}!=NIT_NULL)"
640 else
641 s = "({recv}!=NIT_NULL) && "
642 end
643 end
644 return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {stype}*/"
645 end
646 end
647
648 redef class IIs
649 redef fun inner_compile_to_c(v)
650 do
651 var t1 = expr1.stype
652 var t2 = expr2.stype
653 if t1 isa MMTypeNone then
654 if t2 isa MMTypeNone then
655 return "TAG_Bool(1)"
656 else if t2.is_nullable then
657 return "TAG_Bool({v.register(expr2)}==NIT_NULL)"
658 else
659 return "TAG_Bool(0)"
660 end
661 else if t1.is_nullable then
662 if t2 isa MMTypeNone then
663 return "TAG_Bool({v.register(expr1)}==NIT_NULL)"
664 else if t2.is_nullable then
665 return "TAG_Bool(IS_EQUAL_NN({v.register(expr1)},{v.register(expr2)}))"
666 else
667 return "TAG_Bool(IS_EQUAL_ON({v.register(expr2)},{v.register(expr1)}))"
668 end
669 else
670 if t2 isa MMTypeNone then
671 return "TAG_Bool(0)"
672 else if t2.is_nullable then
673 return "TAG_Bool(IS_EQUAL_ON({v.register(expr1)},{v.register(expr2)}))"
674 else
675 return "TAG_Bool(IS_EQUAL_OO({v.register(expr1)},{v.register(expr2)}))"
676 end
677 end
678 end
679 end
680
681 redef class INot
682 redef fun inner_compile_to_c(v)
683 do
684 return "TAG_Bool(!UNTAG_Bool({v.register(expr)}))"
685 end
686 end
687
688 redef class IOnce
689 redef fun inner_compile_to_c(v)
690 do
691 var i = v.new_number
692 var res = result.as(not null)
693 if res.stype.is_nullable then
694 v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value */")
695 v.add_instr("if (!once_bool_{i}) \{")
696 else
697 # Since the value is not nullable, we use the null value to represent the boolean
698 v.add_decl("static val_t once_value_{i}; /* Once value */")
699 v.add_instr("if (!once_value_{i}) \{")
700 end
701 v.indent
702 body.compile_to_c(v)
703 var e = v.register(result.as(not null))
704 v.add_instr("once_value_{i} = {e};")
705 v.add_instr("register_static_object(&once_value_{i});")
706 if res.stype.is_nullable then v.add_instr("once_bool_{i} = true;")
707 v.unindent
708 v.add_instr("} else {e} = once_value_{i};")
709 return e
710 end
711 end
712
713 redef class IClosCall
714 redef fun compile_to_c(v: I2CCompilerVisitor)
715 do
716 v.add_location(location)
717 var ivar: String
718 if v.closure then
719 ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
720 else
721 ivar = "CREG[{v.closures[closure_decl]}]"
722 end
723 var args = [ivar]
724 args.append(v.registers(exprs))
725
726 var s = "(({v.clostypes[closure_decl]})({ivar}->fun))({args.join(", ")})"
727 store_result(v, s)
728
729 v.add_instr("if ({ivar}->has_broke) \{")
730 v.indent
731 var bs = break_seq
732 if bs != null then
733 bs.compile_to_c(v)
734 end
735 v.add_goto(v.iroutine.body)
736 v.unindent
737 v.add_instr("\}")
738 end
739
740 redef fun inner_compile_to_c(v) do abort
741 end
742
743 redef class IHasClos
744 redef fun inner_compile_to_c(v)
745 do
746 var ivar: String
747 if v.closure then
748 ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
749 else
750 ivar = "CREG[{v.closures[closure_decl]}]"
751 end
752 return "TAG_Bool({ivar} != NULL)"
753 end
754 end
755
756
757 redef class IClosureDef
758 fun compile_closure(v: I2CCompilerVisitor): String
759 do
760 var cfc_old = v.closure
761 v.closure = true
762 var lls_old = v.local_labels
763 v.local_labels = new HashSet[ISeq]
764
765 var cv = v.visitor
766 var ctx_old = cv.ctx
767 cv.ctx = new CContext
768 cv.out_contexts.add(cv.ctx)
769
770 var cname = "OC_{v.basecname}_{v.new_number}"
771 var args = compile_signature_to_c(v.visitor, cname, null, "struct WBT_ *closctx", null)
772 var ctx_old2 = cv.ctx
773 cv.ctx = new CContext
774
775 var s = compile_inside_to_c(v, args)
776 if s == null then
777 v.add_instr("return;")
778 else
779 v.add_instr("return {s};")
780 end
781
782 ctx_old2.append(cv.ctx)
783 cv.ctx = ctx_old2
784 v.unindent
785 v.add_instr("}")
786 cv.ctx = ctx_old
787
788 v.closure = cfc_old
789
790 # Build closure
791 var closcnv = "wbclos{v.new_number}"
792 v.add_decl("struct WBT_ {closcnv};")
793 v.add_instr("{closcnv}.fun = (fun_t){cname};")
794 v.add_instr("{closcnv}.has_broke = NULL;")
795 if cfc_old then
796 v.add_instr("{closcnv}.variable = closctx->variable;")
797 v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;")
798 else
799 v.add_instr("{closcnv}.variable = fra.me.REG;")
800 v.add_instr("{closcnv}.closurevariable = CREG;")
801 end
802
803 v.local_labels = lls_old
804 return "(&{closcnv})"
805 end
806 end