5374ac83637276ea1d70b3740d044d00855cf595
1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2009 Jean Privat <jean@pryen.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Generate C code from intermediate code representation
18 package compiling_icode
21 private import analysis
24 # Compiler context from ICode to C
25 class I2CCompilerVisitor
27 var _ids
: HashMap[Object, String] = new HashMap[Object, String]
28 # Associate other things
29 var _ids2
: HashMap[Object, String] = new HashMap[Object, String]
31 # Return the string associated with a register
32 fun register
(e
: IRegister): String
34 if e
.stype
isa MMTypeNone then return "NIT_NULL"
36 if closure
and not e
.is_local
then ids
= _ids2
37 if ids
.has_key
(e
) then
42 # The register is dead
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->REG[{i}]"
56 strs
= once
new HashMap[Int, String]
57 if not strs
.has_key
(i
) then strs
[i
] = "fra.me.REG[{i}]"
66 # Return the strings associated with registers
67 fun registers
(a
: Collection[IRegister]): Array[String]
69 var r
= new Array[String].with_capacity
(a
.length
)
76 var _last_number
: Int = 0
77 # Give a new unique number (unique for the visitor)
84 # Return the string associated with a escape label
85 fun lab
(e
: ISeq): String
87 if _ids
.has_key
(e
) then
90 var s
= "label{new_number}"
96 # The rank (number) of each closure
97 readable var _closures
: HashMap[IClosureDecl, Int] = new HashMap[IClosureDecl, Int]
99 # The functionnal type of each closure
100 readable var _clostypes
: HashMap[IClosureDecl, String] = new HashMap[IClosureDecl, String]
102 # label locally accessibles
103 readable writable var _local_labels
: HashSet[ISeq] = new HashSet[ISeq]
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]
109 # Register a escape to a non local label and return an index identifying the label
110 fun register_escape_label
(e
: ISeq): Int
112 if _escaped_labels
.has_key
(e
) then
113 return _escaped_labels
[e
]
115 var res
= _escaped_labels
.length
+ 1
116 _escaped_labels
[e
] = res
121 # Add a C label mark (if needed)
122 fun add_label
(e
: ISeq)
124 if _ids
.has_key
(e
) then
125 add_instr
("{_ids[e]}: while(0);")
129 # Add a goto to a label (even outside a closure)
130 fun add_goto
(seq
: ISeq)
132 if local_labels
.has
(seq
) then
133 add_instr
("goto {lab(seq)};")
136 var ind
= register_escape_label
(seq
)
137 add_instr
("closctx->has_broke = {ind};")
138 add_instr
("goto {lab(return_label.as(not null))};")
142 # Association between IEscapeMarks and visited ISeq
143 readable var _marks_to_seq
: Map[IEscapeMark, ISeq] = new HashMap[IEscapeMark, ISeq]
145 # Are we in a closure ?
146 readable writable var _closure
: Bool = false
148 # The current compiler visitor
149 readable var _visitor
: CompilerVisitor
151 # The current compiled iroutine
152 readable var _iroutine
: IRoutine
154 # The return label of the current compiling C function
155 readable writable var _return_label
: nullable ISeq = null
157 fun add_decl
(s
: String)
162 fun add_instr
(s
: String)
164 var l
= _next_location
166 visitor
.add_instr
("/* ", l
.file
, ":", l
.line_start
.to_s
, " */")
167 _next_location
= null
182 fun add_assignment
(to
, from
: String)
184 visitor
.add_assignment
(to
, from
)
187 var _last_location
: nullable Location = null
188 var _next_location
: nullable Location = null
190 # Add location information in a comment
191 # Do nothing if the last location added is the same
192 fun add_location
(l
: nullable Location)
194 var last
= _last_location
195 if last
== l
or l
== null then return
197 if last
!= null and last
.file
== l
.file
and last
.line_start
== l
.line_start
then
204 # The C fonction name of the iroutine
205 readable var _basecname
: String
207 init(v
: CompilerVisitor, ir
: IRoutine, cname
: String)
216 # Declare and start a C function that match the routine
217 # Return what must be given to compile_inside_to_c or to compile_to_c
218 # After the method, an openinig { and and indent is added.
219 # So, do not forget to add a sub_context, to unintent and to add a closing }
220 fun compile_signature_to_c
(v
: CompilerVisitor, cname
: String, human_name
: nullable String, before_params
, after_params
: nullable String): Array[String]
222 var cargs
= new Array[String]
223 var cparams
= new Array[String]
224 if before_params
!= null then cparams
.add
(before_params
)
225 for i
in [0..params
.length
[ do
227 cparams
.add
("val_t p{i}")
229 if closure_decls
!= null then
230 cparams
.add
("struct stack_frame_t *closctx_param")
231 for i
in [0..closure_decls
.length
[ do
232 var closcn
= "CLOS_{cname}_{i}"
233 var cs
= closure_decls
[i
].closure
.signature
234 var subparams
= new Array[String] # Parameters of the closure
235 subparams
.add
("struct stack_frame_t *")
236 for j
in [0..cs
.arity
[ do
237 subparams
.add
("val_t")
240 if cs
.return_type
!= null then rr
= "val_t"
241 v
.add_decl
("typedef {rr} (*{closcn})({subparams.join(", ")});")
242 cargs
.add
("clos_fun{i}")
243 cparams
.add
("fun_t clos_fun{i}")
246 if after_params
!= null then cparams
.add
(after_params
)
248 if result
!= null then r
= "val_t"
250 if cparams
.is_empty
then
253 p
= cparams
.join
(", ")
255 if human_name
!= null then v
.add_decl
("#define LOCATE_", cname
, " \"", human_name, "\
"")
256 v
.add_decl
(r
, " ", cname
, "(", p
, ");")
257 v
.add_decl
("typedef ", r
, " (*", cname
, "_t)(", p
, ");")
258 v
.add_instr
(r
, " ", cname
, "(", p
, ")\{")
263 # Compile the body of the routine, return the result value is any
264 fun compile_inside_to_c
(v
: I2CCompilerVisitor, args
: Array[String]): nullable String
266 # Create and push the stack frame
268 if location
!= null then
269 ll
= location
.line_start
271 # Encapsulate the frame ('me') in a larger structure ('fra') that has enough space to store the local variables (REG)
272 if std_slots_nb
> 1 then
273 v
.add_decl
("struct \{struct stack_frame_t me; val_t MORE_REG[{std_slots_nb-1}];\} fra;")
275 v
.add_decl
("struct \{struct stack_frame_t me;\} fra;")
277 v
.add_instr
("fra.me.prev = stack_frame_head; stack_frame_head = &fra.me;")
278 v
.add_instr
("fra.me.file = LOCATE_{v.visitor.module.name};")
279 v
.add_instr
("fra.me.line = {ll};")
280 v
.add_instr
("fra.me.meth = LOCATE_{v.basecname};")
281 v
.add_instr
("fra.me.has_broke = 0;")
282 v
.add_instr
("fra.me.REG_size = {std_slots_nb};")
284 # Declare/initialize local variables
285 for i
in [0..std_slots_nb
[ do
286 v
.add_instr
("fra.me.REG[{i}] = NIT_NULL;")
288 for i
in [0..tag_slots_nb
[ do
289 v
.add_decl
("val_t REGB{i};")
291 var iclosdecls
= closure_decls
292 if iclosdecls
!= null then
293 v
.add_decl
("fun_t CREG[{iclosdecls.length}];")
294 v
.add_instr
("fra.me.closure_ctx = closctx_param;")
295 v
.add_instr
("fra.me.closure_funs = CREG;")
299 if r
.slot_index
!= null then v
.add_assignment
(v
.register
(r
), args
[k
])
302 if iclosdecls
!= null then
303 for i
in [0..iclosdecls
.length
[ do
304 var iclosdecl
= iclosdecls
[i
]
305 v
.add_instr
("CREG[{i}] = {args[params.length+i]};")
306 v
.closures
[iclosdecl
] = i
307 var cs
= iclosdecl
.closure
.signature
# Closure signature
308 var subparams
= new Array[String] # Parameters of the closure
309 subparams
.add
("struct stack_frame_t *")
310 for j
in [0..cs
.arity
[ do
315 if cs
.return_type
!= null then r
= "val_t"
316 v
.clostypes
[iclosdecl
] = "{r} (*)({subparams.join(", ")})"
319 v
.add_decl
("val_t tmp;")
322 var old_rl
= v
.return_label
323 v
.return_label
= body
328 v
.add_instr
("stack_frame_head = fra.me.prev;")
329 v
.return_label
= old_rl
338 # Full compilation of the routine
339 # cv must be in the correct function
340 fun compile_to_c
(cv
: CompilerVisitor, cname
: String, args
: Array[String]): nullable String
342 var v
= new I2CCompilerVisitor(cv
, self, cname
)
343 return compile_inside_to_c
(v
, args
)
348 # Full compilation of the icode
349 fun compile_to_c
(v
: I2CCompilerVisitor)
351 v
.add_location
(location
)
352 store_result
(v
, inner_compile_to_c
(v
))
355 # Is a result really needed
356 private fun need_result
: Bool
359 return r
!= null and r
.slot_index
!= null
362 # Store s in the result value of self
363 private fun store_result
(v
: I2CCompilerVisitor, s
: nullable String)
366 if r
!= null and r
.slot_index
!= null then
368 v
.add_assignment
(v
.register
(r
), s
)
369 else if s
!= null and not is_pure
then
370 # ICode with side effects must be evaluated
371 # even if the result is not wanted
376 # Compilation of without the result assigment
377 # Return the right value is case of expression
378 # Return the full expression (witout ;) in case of statement
379 private fun inner_compile_to_c
(v
: I2CCompilerVisitor): nullable String is abstract
383 redef fun inner_compile_to_c
(v
)
385 v
.local_labels
.add
(self)
386 var mark
= iescape_mark
387 if mark
!= null then v
.marks_to_seq
[mark
] = self
397 redef fun inner_compile_to_c
(v
)
399 v
.add_instr
("if (UNTAG_Bool({v.register(expr)})) \{")
400 if not then_seq
.icodes
.is_empty
then
402 then_seq
.inner_compile_to_c
(v
)
405 if not else_seq
.icodes
.is_empty
then
406 v
.add_instr
("} else \{")
408 else_seq
.inner_compile_to_c
(v
)
417 redef fun inner_compile_to_c
(v
)
419 v
.local_labels
.add
(self)
420 var mark
= iescape_mark
421 if mark
!= null then v
.marks_to_seq
[mark
] = self
422 v
.add_instr
("while(1) \{")
435 redef fun inner_compile_to_c
(v
)
437 v
.add_goto
(v
.marks_to_seq
[iescape_mark
])
443 redef fun compile_to_c
(v
)
445 v
.add_location
(location
)
446 var args
= v
.registers
(exprs
)
448 # Compile closure definitions
449 var old_el
= v
.escaped_labels
450 var closdefs
= closure_defs
451 var closctx
: nullable String = null # The closure context of closdefs
452 if closdefs
!= null then
453 # Get the closure context
457 closctx
= "(&(fra.me))"
460 # First aditionnal arguments is the closure context
463 # We are in a new escape boundary
464 v
.escaped_labels
= new HashMap[ISeq, Int]
466 # Compile each closures and add each sub-function as an other additionnal parameter
467 for cd
in closdefs
do
469 var cn
= cd
.compile_closure
(v
)
477 # Compile the real call
478 var s
= compile_call_to_c
(v
, args
)
479 var r
: nullable String = s
482 if closctx
!= null then
483 var els
= v
.escaped_labels
484 v
.escaped_labels
= old_el
485 # Is there possible escapes?
486 if not els
.is_empty
then
487 # Call in a tmp variable to avoid 'break' overwrite
490 v
.add_assignment
(r
, s
)
495 # What are the expected escape indexes
496 v
.add_instr
("switch ({closctx}->has_broke) \{")
498 # No escape occured, continue as usual
499 v
.add_instr
("case 0: break;")
500 var lls
= v
.local_labels
501 var iels
= els
.iterator
502 var forward_escape
= false
506 # Local escape occured
507 # Clear the has_broke information and go to the target
508 v
.add_instr
("case {iels.item}: {closctx}->has_broke = 0; goto {v.lab(seq)};")
510 # Forward escape occured: register the escape label
512 v
.register_escape_label
(seq
)
513 forward_escape
= true
517 # If forward escape occured, just pass to the next one
518 if forward_escape
then
519 # Do not need to copy 'has_broke' value since it is shared by the next one.
520 # So just exit the C function.
521 v
.add_instr
("default: goto {v.lab(v.return_label.as(not null))};")
531 redef fun inner_compile_to_c
(v
) do abort
533 # The single invocation witout fancy stuffs
534 private fun compile_call_to_c
(v
: I2CCompilerVisitor, args
: Array[String]): String is abstract
538 redef fun compile_call_to_c
(v
, args
)
541 if prop
.global
.is_init
then args
.add
("init_table")
542 if prop
.name
== (once
("add".to_symbol
)) and prop
.local_class
.name
== (once
("Array".to_symbol
)) then
543 return "{prop.cname}({args.join(", ")})"
545 return "{prop.global.meth_call}({args[0]})({args.join(", ")})"
551 redef fun compile_call_to_c
(v
, args
)
554 if prop
.global
.is_init
then args
.add
("init_table")
555 return "{prop.super_meth_call}({args[0]})({args.join(", ")})"
560 redef fun compile_call_to_c
(v
, args
)
562 return "NEW_{stype.local_class}_{property.global.intro.cname}({args.join(", ")})"
566 redef class IAllocateInstance
567 redef fun inner_compile_to_c
(v
)
569 return "NEW_{stype.local_class.name}()"
573 redef class ICheckInstance
574 redef fun inner_compile_to_c
(v
)
576 return "CHECKNEW_{stype.local_class.name}({v.register(expr)})"
580 redef class IInitAttributes
581 redef fun inner_compile_to_c
(v
)
583 return "INIT_ATTRIBUTES__{stype.local_class.name}({v.register(expr)})"
587 redef class IStaticCall
588 redef fun compile_call_to_c
(v
, args
)
591 if prop
.global
.is_init
then args
.add
("init_table")
592 return "{property.cname}({args.join(", ")})"
597 redef fun inner_compile_to_c
(v
)
599 if exprs
.is_empty
then
604 var c
= code
.split_with
("@@@")
607 if i
< exprs
.length
and i
< c
.length-1
then
608 res
.append
(v
.register
(exprs
[i
]))
618 redef fun inner_compile_to_c
(v
)
620 var s
= new Buffer.from
("fprintf(stderr")
622 s
.append
(", \"{t}\
"")
628 s
= new Buffer.from
("fprintf(stderr, \" (%s
")
632 s.append(")\\n\
", LOCATE_{module_location.name}")
634 s
.append
(", {ll.line_start}")
639 v
.add_instr
("nit_exit(1);")
645 redef fun inner_compile_to_c
(v
)
647 return v
.register
(expr
)
651 redef class IAttrRead
652 redef fun inner_compile_to_c
(v
)
654 return "{property.global.attr_access}({v.register(expr)})"
658 redef class IAttrIsset
659 redef fun inner_compile_to_c
(v
)
661 return "TAG_Bool({property.global.attr_access}({v.register(expr)})!=NIT_NULL)"
665 redef class IAttrWrite
666 redef fun inner_compile_to_c
(v
)
668 v
.add_instr
("{property.global.attr_access}({v.register(expr1)}) = {v.register(expr2)};")
673 redef class ITypeCheck
674 redef fun inner_compile_to_c
(v
)
676 # FIXME handle formaltypes
677 var g
= stype
.local_class
.global
678 var recv
= v
.register
(expr
)
680 if expr
.stype
.is_nullable
then
681 if stype
.is_nullable
then
682 s
= "({recv}==NIT_NULL) || "
683 else if stype
.as_nullable
== expr
.stype
then
684 return "TAG_Bool({recv}!=NIT_NULL)"
686 s
= "({recv}!=NIT_NULL) && "
689 return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {stype}*/"
694 redef fun inner_compile_to_c
(v
)
698 if t1
isa MMTypeNone then
699 if t2
isa MMTypeNone then
701 else if t2
.is_nullable
then
702 return "TAG_Bool({v.register(expr2)}==NIT_NULL)"
706 else if t1
.is_nullable
then
707 if t2
isa MMTypeNone then
708 return "TAG_Bool({v.register(expr1)}==NIT_NULL)"
709 else if t2
.is_nullable
then
710 return "TAG_Bool(IS_EQUAL_NN({v.register(expr1)},{v.register(expr2)}))"
712 return "TAG_Bool(IS_EQUAL_ON({v.register(expr2)},{v.register(expr1)}))"
715 if t2
isa MMTypeNone then
717 else if t2
.is_nullable
then
718 return "TAG_Bool(IS_EQUAL_ON({v.register(expr1)},{v.register(expr2)}))"
720 return "TAG_Bool(IS_EQUAL_OO({v.register(expr1)},{v.register(expr2)}))"
727 redef fun inner_compile_to_c
(v
)
729 return "TAG_Bool(!UNTAG_Bool({v.register(expr)}))"
734 redef fun inner_compile_to_c
(v
)
737 var res
= result
.as(not null)
738 if res
.stype
.is_nullable
then
739 v
.add_decl
("static val_t once_value_{i}; static int once_bool_{i}; /* Once value */")
740 v
.add_instr
("if (!once_bool_{i}) \{")
742 # Since the value is not nullable, we use the null value to represent the boolean
743 v
.add_decl
("static val_t once_value_{i}; /* Once value */")
744 v
.add_instr
("if (!once_value_{i}) \{")
748 var e
= v
.register
(result
.as(not null))
749 v
.add_instr
("once_value_{i} = {e};")
750 v
.add_instr
("register_static_object(&once_value_{i});")
751 if res
.stype
.is_nullable
then v
.add_instr
("once_bool_{i} = true;")
753 v
.add_instr
("} else {e} = once_value_{i};")
758 redef class IClosCall
759 redef fun compile_to_c
(v
: I2CCompilerVisitor)
761 v
.add_location
(location
)
763 var args
: Array[String]
765 ivar
= "closctx->closure_funs[{v.closures[closure_decl]}]"
766 args
= ["closctx->closure_ctx"]
768 ivar
= "CREG[{v.closures[closure_decl]}]"
769 args
= ["closctx_param"]
771 args
.append
(v
.registers
(exprs
))
773 var s
= "(({v.clostypes[closure_decl]})({ivar}))({args.join(", ")})"
777 v
.add_instr
("if ({args.first}->has_broke) \{")
783 v
.add_goto
(v
.iroutine
.body
)
788 redef fun inner_compile_to_c
(v
) do abort
792 redef fun inner_compile_to_c
(v
)
796 ivar
= "closctx->closure_funs[{v.closures[closure_decl]}]"
798 ivar
= "CREG[{v.closures[closure_decl]}]"
800 return "TAG_Bool({ivar} != NULL)"
804 redef class IClosureDef
805 # Compile the closure as a separate C function in the visitor out_contexts.
806 # Return a fun_t pointer to the function.
807 fun compile_closure
(v
: I2CCompilerVisitor): String
811 # We are now in a closure
812 var cfc_old
= v
.closure
815 # We are now in a escape boundary
816 var lls_old
= v
.local_labels
817 v
.local_labels
= new HashSet[ISeq]
819 # We are now in a new C context
821 cv
.ctx
= new CContext
822 cv
.out_contexts
.add
(cv
.ctx
)
824 # Generate the C function
825 var cname
= "OC_{v.basecname}_{v.new_number}"
826 var args
= compile_signature_to_c
(v
.visitor
, cname
, null, "struct stack_frame_t *closctx", null)
827 var ctx_old2
= cv
.ctx
828 cv
.ctx
= new CContext
829 var s
= compile_inside_to_c
(v
, args
)
831 v
.add_instr
("return;")
833 v
.add_instr
("return {s};")
835 ctx_old2
.append
(cv
.ctx
)
843 v
.local_labels
= lls_old
844 return "((fun_t){cname})"