82cf93c581a828ca560695eca7efb80f902e64f7
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 var w
= visitor
.writer
167 visitor
.add_indent
(w
)
171 w
.add
(l
.line_start
.to_s
)
173 _next_location
= null
188 fun add_assignment
(to
, from
: String)
190 visitor
.add_assignment
(to
, from
)
193 var _last_location
: nullable Location = null
194 var _next_location
: nullable Location = null
196 # Add location information in a comment
197 # Do nothing if the last location added is the same
198 fun add_location
(l
: nullable Location)
200 var last
= _last_location
201 if last
== l
or l
== null then return
203 if last
!= null and last
.file
== l
.file
and last
.line_start
== l
.line_start
then
210 # The C fonction name of the iroutine
211 readable var _basecname
: String
213 init(v
: CompilerVisitor, ir
: IRoutine, cname
: String)
222 # Declare and start a C function that match the routine
223 # Return what must be given to compile_inside_to_c or to compile_to_c
224 # After the method, an openinig { and and indent is added.
225 # So, do not forget to add a sub_context, to unintent and to add a closing }
226 fun compile_signature_to_c
(v
: CompilerVisitor, cname
: String, human_name
: nullable String, before_params
, after_params
: nullable String): Array[String]
228 var cargs
= new Array[String]
229 var cparams
= new Array[String]
230 if before_params
!= null then cparams
.add
(before_params
)
231 for i
in [0..params
.length
[ do
233 cparams
.add
("val_t p{i}")
235 if closure_decls
!= null then
236 cparams
.add
("struct stack_frame_t *closctx_param")
237 for i
in [0..closure_decls
.length
[ do
238 var closcn
= "CLOS_{cname}_{i}"
239 var cs
= closure_decls
[i
].closure
.signature
240 var subparams
= new Array[String] # Parameters of the closure
241 subparams
.add
("struct stack_frame_t *")
242 for j
in [0..cs
.arity
[ do
243 subparams
.add
("val_t")
246 if cs
.return_type
!= null then rr
= "val_t"
247 v
.add_decl
("typedef {rr} (*{closcn})({subparams.join(", ")});")
248 cargs
.add
("clos_fun{i}")
249 cparams
.add
("fun_t clos_fun{i}")
252 if after_params
!= null then cparams
.add
(after_params
)
254 if result
!= null then r
= "val_t"
256 if cparams
.is_empty
then
259 p
= cparams
.join
(", ")
261 if human_name
!= null then v
.add_decl
("#define LOCATE_{cname} \"{human_name}\
"")
262 v
.add_decl
("{r} {cname}({p});")
263 v
.add_decl
("typedef {r} (*{cname}_t)({p});")
264 v
.add_instr
("{r} {cname}({p})\{")
269 # Compile the body of the routine, return the result value is any
270 fun compile_inside_to_c
(v
: I2CCompilerVisitor, args
: Array[String]): nullable String
272 # Create and push the stack frame
274 if location
!= null then
275 ll
= location
.line_start
277 # Encapsulate the frame ('me') in a larger structure ('fra') that has enough space to store the local variables (REG)
278 if std_slots_nb
> 1 then
279 v
.add_decl
("struct \{struct stack_frame_t me; val_t MORE_REG[{std_slots_nb-1}];\} fra;")
281 v
.add_decl
("struct \{struct stack_frame_t me;\} fra;")
283 v
.add_instr
("fra.me.prev = stack_frame_head; stack_frame_head = &fra.me;")
284 v
.add_instr
("fra.me.file = LOCATE_{v.visitor.module.name};")
285 v
.add_instr
("fra.me.line = {ll};")
286 v
.add_instr
("fra.me.meth = LOCATE_{v.basecname};")
287 v
.add_instr
("fra.me.has_broke = 0;")
288 v
.add_instr
("fra.me.REG_size = {std_slots_nb};")
290 # Declare/initialize local variables
291 for i
in [0..std_slots_nb
[ do
292 v
.add_instr
("fra.me.REG[{i}] = NIT_NULL;")
294 for i
in [0..tag_slots_nb
[ do
295 v
.add_decl
("val_t REGB{i};")
297 var iclosdecls
= closure_decls
298 if iclosdecls
!= null then
299 v
.add_decl
("fun_t CREG[{iclosdecls.length}];")
300 v
.add_instr
("fra.me.closure_ctx = closctx_param;")
301 v
.add_instr
("fra.me.closure_funs = CREG;")
305 if r
.slot_index
!= null then v
.add_assignment
(v
.register
(r
), args
[k
])
308 if iclosdecls
!= null then
309 for i
in [0..iclosdecls
.length
[ do
310 var iclosdecl
= iclosdecls
[i
]
311 v
.add_instr
("CREG[{i}] = {args[params.length+i]};")
312 v
.closures
[iclosdecl
] = i
313 var cs
= iclosdecl
.closure
.signature
# Closure signature
314 var subparams
= new Array[String] # Parameters of the closure
315 subparams
.add
("struct stack_frame_t *")
316 for j
in [0..cs
.arity
[ do
321 if cs
.return_type
!= null then r
= "val_t"
322 v
.clostypes
[iclosdecl
] = "{r} (*)({subparams.join(", ")})"
325 v
.add_decl
("val_t tmp;")
328 var old_rl
= v
.return_label
329 v
.return_label
= body
334 v
.add_instr
("stack_frame_head = fra.me.prev;")
335 v
.return_label
= old_rl
344 # Full compilation of the routine
345 # cv must be in the correct function
346 fun compile_to_c
(cv
: CompilerVisitor, cname
: String, args
: Array[String]): nullable String
348 var v
= new I2CCompilerVisitor(cv
, self, cname
)
349 return compile_inside_to_c
(v
, args
)
354 # Full compilation of the icode
355 fun compile_to_c
(v
: I2CCompilerVisitor)
357 v
.add_location
(location
)
358 store_result
(v
, inner_compile_to_c
(v
))
361 # Is a result really needed
362 private fun need_result
: Bool
365 return r
!= null and r
.slot_index
!= null
368 # Store s in the result value of self
369 private fun store_result
(v
: I2CCompilerVisitor, s
: nullable String)
372 if r
!= null and r
.slot_index
!= null then
374 v
.add_assignment
(v
.register
(r
), s
)
375 else if s
!= null and not is_pure
then
376 # ICode with side effects must be evaluated
377 # even if the result is not wanted
382 # Compilation of without the result assigment
383 # Return the right value is case of expression
384 # Return the full expression (witout ;) in case of statement
385 private fun inner_compile_to_c
(v
: I2CCompilerVisitor): nullable String is abstract
389 redef fun inner_compile_to_c
(v
)
391 v
.local_labels
.add
(self)
392 var mark
= iescape_mark
393 if mark
!= null then v
.marks_to_seq
[mark
] = self
403 redef fun inner_compile_to_c
(v
)
405 v
.add_instr
("if (UNTAG_Bool({v.register(expr)})) \{")
406 if not then_seq
.icodes
.is_empty
then
408 then_seq
.inner_compile_to_c
(v
)
411 if not else_seq
.icodes
.is_empty
then
412 v
.add_instr
("} else \{")
414 else_seq
.inner_compile_to_c
(v
)
423 redef fun inner_compile_to_c
(v
)
425 v
.local_labels
.add
(self)
426 var mark
= iescape_mark
427 if mark
!= null then v
.marks_to_seq
[mark
] = self
428 v
.add_instr
("while(1) \{")
441 redef fun inner_compile_to_c
(v
)
443 v
.add_goto
(v
.marks_to_seq
[iescape_mark
])
449 redef fun compile_to_c
(v
)
451 v
.add_location
(location
)
452 var args
= v
.registers
(exprs
)
454 # Compile closure definitions
455 var old_el
= v
.escaped_labels
456 var closdefs
= closure_defs
457 var closctx
: nullable String = null # The closure context of closdefs
458 if closdefs
!= null then
459 # Get the closure context
463 closctx
= "(&(fra.me))"
466 # First aditionnal arguments is the closure context
469 # We are in a new escape boundary
470 v
.escaped_labels
= new HashMap[ISeq, Int]
472 # Compile each closures and add each sub-function as an other additionnal parameter
473 for cd
in closdefs
do
475 var cn
= cd
.compile_closure
(v
)
483 # Compile the real call
484 var s
= compile_call_to_c
(v
, args
)
485 var r
: nullable String = s
488 if closctx
!= null then
489 var els
= v
.escaped_labels
490 v
.escaped_labels
= old_el
491 # Is there possible escapes?
492 if not els
.is_empty
then
493 # Call in a tmp variable to avoid 'break' overwrite
496 v
.add_assignment
(r
, s
)
501 # What are the expected escape indexes
502 v
.add_instr
("switch ({closctx}->has_broke) \{")
504 # No escape occured, continue as usual
505 v
.add_instr
("case 0: break;")
506 var lls
= v
.local_labels
507 var iels
= els
.iterator
508 var forward_escape
= false
512 # Local escape occured
513 # Clear the has_broke information and go to the target
514 v
.add_instr
("case {iels.item}: {closctx}->has_broke = 0; goto {v.lab(seq)};")
516 # Forward escape occured: register the escape label
518 v
.register_escape_label
(seq
)
519 forward_escape
= true
523 # If forward escape occured, just pass to the next one
524 if forward_escape
then
525 # Do not need to copy 'has_broke' value since it is shared by the next one.
526 # So just exit the C function.
527 v
.add_instr
("default: goto {v.lab(v.return_label.as(not null))};")
537 redef fun inner_compile_to_c
(v
) do abort
539 # The single invocation witout fancy stuffs
540 private fun compile_call_to_c
(v
: I2CCompilerVisitor, args
: Array[String]): String is abstract
544 redef fun compile_call_to_c
(v
, args
)
547 if prop
.global
.is_init
then args
.add
("init_table")
548 if prop
.name
== (once
("add".to_symbol
)) and prop
.local_class
.name
== (once
("Array".to_symbol
)) then
549 return "{prop.cname}({args.join(", ")})"
551 return "{prop.global.meth_call}({args[0]})({args.join(", ")})"
557 redef fun compile_call_to_c
(v
, args
)
560 if prop
.global
.is_init
then args
.add
("init_table")
561 return "{prop.super_meth_call}({args[0]})({args.join(", ")})"
566 redef fun compile_call_to_c
(v
, args
)
568 return "NEW_{stype.local_class}_{property.global.intro.cname}({args.join(", ")})"
572 redef class IAllocateInstance
573 redef fun inner_compile_to_c
(v
)
575 return "NEW_{stype.local_class.name}()"
579 redef class ICheckInstance
580 redef fun inner_compile_to_c
(v
)
582 return "CHECKNEW_{stype.local_class.name}({v.register(expr)})"
586 redef class IInitAttributes
587 redef fun inner_compile_to_c
(v
)
589 return "INIT_ATTRIBUTES__{stype.local_class.name}({v.register(expr)})"
593 redef class IStaticCall
594 redef fun compile_call_to_c
(v
, args
)
597 if prop
.global
.is_init
then args
.add
("init_table")
598 return "{property.cname}({args.join(", ")})"
603 redef fun inner_compile_to_c
(v
)
605 if exprs
.is_empty
then
610 var c
= code
.split_with
("@@@")
613 if i
< exprs
.length
and i
< c
.length-1
then
614 res
.append
(v
.register
(exprs
[i
]))
624 redef fun inner_compile_to_c
(v
)
626 var s
= new Buffer.from
("fprintf(stderr")
628 s
.append
(", \"{t}\
"")
634 s
= new Buffer.from
("fprintf(stderr, \" (%s
")
638 s.append(")\\n\
", LOCATE_{module_location.name}")
640 s
.append
(", {ll.line_start}")
645 v
.add_instr
("nit_exit(1);")
651 redef fun inner_compile_to_c
(v
)
653 return v
.register
(expr
)
657 redef class IAttrRead
658 redef fun inner_compile_to_c
(v
)
660 return "{property.global.attr_access}({v.register(expr)})"
664 redef class IAttrIsset
665 redef fun inner_compile_to_c
(v
)
667 return "TAG_Bool({property.global.attr_access}({v.register(expr)})!=NIT_NULL)"
671 redef class IAttrWrite
672 redef fun inner_compile_to_c
(v
)
674 v
.add_instr
("{property.global.attr_access}({v.register(expr1)}) = {v.register(expr2)};")
679 redef class ITypeCheck
680 redef fun inner_compile_to_c
(v
)
682 # FIXME handle formaltypes
683 var g
= stype
.local_class
.global
684 var recv
= v
.register
(expr
)
686 if expr
.stype
.is_nullable
then
687 if stype
.is_nullable
then
688 s
= "({recv}==NIT_NULL) || "
689 else if stype
.as_nullable
== expr
.stype
then
690 return "TAG_Bool({recv}!=NIT_NULL)"
692 s
= "({recv}!=NIT_NULL) && "
695 return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {stype}*/"
700 redef fun inner_compile_to_c
(v
)
704 if t1
isa MMTypeNone then
705 if t2
isa MMTypeNone then
707 else if t2
.is_nullable
then
708 return "TAG_Bool({v.register(expr2)}==NIT_NULL)"
712 else if t1
.is_nullable
then
713 if t2
isa MMTypeNone then
714 return "TAG_Bool({v.register(expr1)}==NIT_NULL)"
715 else if t2
.is_nullable
then
716 return "TAG_Bool(IS_EQUAL_NN({v.register(expr1)},{v.register(expr2)}))"
718 return "TAG_Bool(IS_EQUAL_ON({v.register(expr2)},{v.register(expr1)}))"
721 if t2
isa MMTypeNone then
723 else if t2
.is_nullable
then
724 return "TAG_Bool(IS_EQUAL_ON({v.register(expr1)},{v.register(expr2)}))"
726 return "TAG_Bool(IS_EQUAL_OO({v.register(expr1)},{v.register(expr2)}))"
733 redef fun inner_compile_to_c
(v
)
735 return "TAG_Bool(!UNTAG_Bool({v.register(expr)}))"
740 redef fun inner_compile_to_c
(v
)
743 var res
= result
.as(not null)
744 if res
.stype
.is_nullable
then
745 v
.add_decl
("static val_t once_value_{i}; static int once_bool_{i}; /* Once value */")
746 v
.add_instr
("if (!once_bool_{i}) \{")
748 # Since the value is not nullable, we use the null value to represent the boolean
749 v
.add_decl
("static val_t once_value_{i}; /* Once value */")
750 v
.add_instr
("if (!once_value_{i}) \{")
754 var e
= v
.register
(result
.as(not null))
755 v
.add_instr
("once_value_{i} = {e};")
756 v
.add_instr
("register_static_object(&once_value_{i});")
757 if res
.stype
.is_nullable
then v
.add_instr
("once_bool_{i} = true;")
759 v
.add_instr
("} else {e} = once_value_{i};")
764 redef class IClosCall
765 redef fun compile_to_c
(v
: I2CCompilerVisitor)
767 v
.add_location
(location
)
769 var args
: Array[String]
771 ivar
= "closctx->closure_funs[{v.closures[closure_decl]}]"
772 args
= ["closctx->closure_ctx"]
774 ivar
= "CREG[{v.closures[closure_decl]}]"
775 args
= ["closctx_param"]
777 args
.append
(v
.registers
(exprs
))
779 var s
= "(({v.clostypes[closure_decl]})({ivar}))({args.join(", ")})"
783 v
.add_instr
("if ({args.first}->has_broke) \{")
789 v
.add_goto
(v
.iroutine
.body
)
794 redef fun inner_compile_to_c
(v
) do abort
798 redef fun inner_compile_to_c
(v
)
802 ivar
= "closctx->closure_funs[{v.closures[closure_decl]}]"
804 ivar
= "CREG[{v.closures[closure_decl]}]"
806 return "TAG_Bool({ivar} != NULL)"
810 redef class IClosureDef
811 # Compile the closure as a separate C function in the visitor out_contexts.
812 # Return a fun_t pointer to the function.
813 fun compile_closure
(v
: I2CCompilerVisitor): String
817 # We are now in a closure
818 var cfc_old
= v
.closure
821 # We are now in a escape boundary
822 var lls_old
= v
.local_labels
823 v
.local_labels
= new HashSet[ISeq]
825 # We are now in a new C context
826 var decl_writer_old
= cv
.decl_writer
827 var writer_old
= cv
.writer
828 cv
.writer
= cv
.top_writer
.sub
829 cv
.decl_writer
= cv
.header_writer
.sub
831 # Generate the C function
832 var cname
= "OC_{v.basecname}_{v.new_number}"
833 var args
= compile_signature_to_c
(v
.visitor
, cname
, null, "struct stack_frame_t *closctx", null)
834 cv
.decl_writer
= cv
.writer
.sub
836 var s
= compile_inside_to_c
(v
, args
)
838 v
.add_instr
("return;")
840 v
.add_instr
("return {s};")
846 cv
.writer
= writer_old
847 cv
.decl_writer
= decl_writer_old
849 v
.local_labels
= lls_old
850 return "((fun_t){cname})"