1 (********************************************************************)
3 (* The Why3 Verification Platform / The Why3 Development Team *)
4 (* Copyright 2010-2023 -- Inria - CNRS - Paris-Saclay University *)
6 (* This software is distributed under the terms of the GNU Lesser *)
7 (* General Public License version 2.1, with the special exception *)
8 (* on linking described in file LICENSE. *)
10 (********************************************************************)
14 exception Unsupported
= Printer.Unsupported
16 let current_decl_name = ref ""
20 (* struct name, fields) *)
21 type struct_def
= string * (string * ty
) list
25 | Tsyntax
of string * ty list
28 | Tstruct
of struct_def
29 | Tunion
of ident
* (ident
* ty
) list
30 | Tnamed
of ident
(* alias from typedef *)
31 | Tmutable
of ty
(* struct with a single mutable field *)
32 | Tnosyntax
(* types that do not have a syntax, can't be printed *)
33 (* enum, const could be added *)
37 and names
= ty
* (ident
* expr
) list
39 (* return type, parameter list. Variadic functions not handled. *)
40 and proto
= ty
* (ty
* ident
) list
42 and binop
= Band
| Bor
| Beq
| Bne
| Bassign
| Blt
| Ble
| Bgt
| Bge
43 (* += and co. maybe to add *)
46 and unop
= Unot
| Ustar
| Uaddr
| Upreincr
| Upostincr
| Upredecr
| Upostdecr
50 | Eunop
of unop
* expr
51 | Ebinop
of binop
* expr
* expr
52 | Equestion
of expr
* expr
* expr
(* c ? t : e *)
54 | Ecall
of expr
* expr list
57 | Elikely
of expr
(* __builtin_expect(e,1) *)
58 | Eunlikely
of expr
(* __builtin_expect(e,0) *)
61 | Eindex
of expr
* expr
(* Array access *)
62 | Edot
of expr
* string (* Field access with dot *)
63 | Earrow
of expr
* string (* Pointer access with arrow *)
64 | Esyntaxrename
of string * expr list
(* syntax val f "g" w/o params *)
65 | Esyntax
of string * ty
* (ty array
) * (expr
*ty
) list
* int list
66 (* template, type and type arguments of result, typed arguments, precedence level *)
69 | Cint
of string * Number.int_constant
80 | Sif
of expr
* stmt
* stmt
81 | Swhile
of expr
* stmt
82 | Sfor
of expr
* expr
* expr
* stmt
86 and include_kind
= Sys
| Proj
(* include <...> vs. include "..." *)
89 | Dfun
of ident
* proto
* body
90 | Dinclude
of ident
* include_kind
91 | Dproto
of ident
* proto
93 | Dextern
of ty
* ident
94 | Dstruct
of struct_def
95 | Dstruct_decl
of string
96 | Dtypedef
of ty
* ident
99 and body
= definition list
* stmt
103 let rec is_nop = function
104 | Snop
| Sexpr Enothing
-> true
105 | Sblock
([], s
) -> is_nop s
106 | Sseq
(s1
,s2
) -> is_nop s1
&& is_nop s2
109 let is_one ic
= BigInt.eq ic
.Number.il_int
BigInt.one
110 let is_zero ic
= BigInt.eq ic
.Number.il_int
BigInt.zero
112 let is_true = function
113 | Sexpr
(Econst
(Cint
(_
,ic
))) -> is_one ic
116 let is_false = function
117 | Sexpr
(Econst
(Cint
(_
,ic
))) -> is_zero ic
120 (** [assignify v] transforms a statement that computes a value into
121 a statement that assigns that value to v *)
122 let rec assignify v
= function
123 | Snop
-> raise NotAValue
124 | Sexpr e
-> Sexpr
(Ebinop
(Bassign
, v
, e
))
125 | Sblock
(ds
, s
) -> Sblock
(ds
, assignify v s
)
126 | Sseq
(s1
, s2
) when not
(is_nop s2
) -> Sseq
(s1
, assignify v s2
)
127 | Sseq
(s1
,_
) -> assignify v s1
128 | Sif
(c
,t
,e
) -> Sif
(c
, assignify v t
, assignify v e
)
129 | Swhile
(c
,s
) -> Swhile
(c
, assignify v s
) (* can this be a value ?*)
130 | Sfor
(e0
,e1
,e2
,s
) -> Sfor
(e0
,e1
,e2
, assignify v s
)
131 | Sbreak
-> raise NotAValue
132 | Sreturn _
-> raise NotAValue
135 (** [get_last_expr] extracts the expression computed by the given statement.
136 This is needed when loop conditions are more complex than a
137 simple expression. *)
138 let rec get_last_expr = function
139 | Snop
-> raise NotAValue
142 let s'
,e
= get_last_expr s in
144 | Sseq
(s1
,s2
) when not
(is_nop s2
) ->
145 let s'
, e
= get_last_expr s2
in
147 | Sseq
(s1
,_
) -> get_last_expr s1
148 | Sif
(c
,Sexpr t
,Sexpr e
) -> Snop
, Equestion
(c
,t
,e
)
149 | Sif _
-> raise
(Unsupported
"for/while (complex if)")
150 | Swhile
(_c
,_s
) -> raise
(Unsupported
"for/while (while) {}")
151 | Sfor _
-> raise
(Unsupported
"for/while (for)")
152 | Sbreak
-> raise NotAValue
153 | Sreturn _
-> raise NotAValue
155 (** [flatten_defs d s] appends all definitions in the statement [s] to d. *)
156 (* TODO check ident unicity ? *)
157 let rec flatten_defs d
= function
159 let d, s1'
= flatten_defs d s1
in
160 let d, s2'
= flatten_defs d s2
in
163 let d'
,s'
= flatten_defs d'
s in
166 let d, t'
= flatten_defs d t
in
167 let d, e'
= flatten_defs d e
in
170 let d, s'
= flatten_defs d s in
172 | Sfor
(e1
,e2
,e3
,s) ->
173 let d, s'
= flatten_defs d s in
175 | Sbreak
| Snop
| Sexpr _
| Sreturn _
as s -> d, s
177 let rec group_defs_by_type l
=
178 (* heuristic to reduce the number of lines of defs*)
179 let rec group_types t1 t2
=
181 | Tvoid
, Tvoid
-> true
182 | Tsyntax
(s1
, l1
), Tsyntax
(s2
, l2
) ->
183 List.length l1
= List.length l2
184 && List.for_all2
group_types l1 l2
186 && (not
(String.contains s1 '
*'
))
187 | Tptr t
, Tptr t'
-> group_types t t'
188 | Tarray _
, _
-> false
189 | Tstruct
(n1
, _
), Tstruct
(n2
, _
) -> n1
= n2
190 | Tunion
(id1
, _
), Tunion
(id2
, _
) -> id_equal id1 id2
191 | Tnamed id1
, Tnamed id2
-> id_equal id1 id2
192 | Tmutable t
, Tmutable t'
-> group_types t t'
197 | Ddecl
(ty1
, el1
) :: Ddecl
(ty2
, el2
) :: l'
198 when group_types ty1 ty2
199 -> group_defs_by_type (Ddecl
(ty1
, el1
@el2
)::l'
)
200 | Ddecl
(ty1
, el1
) :: Ddecl
(ty2
, el2
) :: Ddecl
(ty3
, el3
) :: l'
201 when group_types ty1 ty3
202 -> group_defs_by_type (Ddecl
(ty1
, el1
@el3
) :: Ddecl
(ty2
, el2
) :: l'
)
203 | Ddecl
(ty1
, el1
) :: Ddecl
(ty2
, el2
) :: Ddecl
(ty3
, el3
) :: l'
204 when group_types ty2 ty3
205 -> group_defs_by_type (Ddecl
(ty1
, el1
) :: Ddecl
(ty2
, el2
@el3
) :: l'
)
206 | Ddecl
(ty1
, el1
) :: l'
->
207 Ddecl
(ty1
, el1
) :: group_defs_by_type l'
210 let rec elim_empty_blocks = function
211 | Sblock
([], s) -> elim_empty_blocks s
212 | Sblock
(d,s) -> Sblock
(d, elim_empty_blocks s)
213 | Sseq
(s1
,s2
) -> Sseq
(elim_empty_blocks s1
, elim_empty_blocks s2
)
214 | Sif
(c
,t
,e
) -> Sif
(c
, elim_empty_blocks t
, elim_empty_blocks e
)
215 | Swhile
(c
,s) -> Swhile
(c
, elim_empty_blocks s)
216 | Sfor
(e1
,e2
,e3
,s) -> Sfor
(e1
,e2
,e3
,elim_empty_blocks s)
217 | Snop
| Sbreak
| Sexpr _
| Sreturn _
as s -> s
219 let rec elim_nop = function
221 let s1 = elim_nop s1 in
222 let s2 = elim_nop s2 in
223 begin match s1,s2 with
225 | Snop
, s | s, Snop
-> s
229 let s = elim_nop s in
230 begin match d, s with
234 | Sif
(c
,t
,e
) -> Sif
(c
, elim_nop t
, elim_nop e
)
235 | Swhile
(c
,s) -> Swhile
(c
, elim_nop s)
236 | Sfor
(e1
,e2
,e3
,s) -> Sfor
(e1
,e2
,e3
,elim_nop s)
237 | Snop
| Sbreak
| Sexpr _
| Sreturn _
as s -> s
239 let is_expr = function
243 let rec simplify_expr (d,s) : expr
=
244 match (d,elim_empty_blocks(elim_nop s)) with
245 | [], Sblock
([],s) -> simplify_expr ([],s)
248 Equestion
(c
, simplify_expr([],t
), simplify_expr([],e
))
250 (* FIXME : use a dedicated exception here.
251 See also the capture of [Invalid_argument] below *)
252 raise
(Invalid_argument
"simplify_expr")
254 let rec simplify_cond (cd
, cs
) =
255 match cd
,elim_empty_blocks(elim_nop cs
) with
256 | [], Sexpr c
-> ([], Sexpr c
)
257 | ([], Sif
(c'
, t'
, e'
) as b
) ->
258 let _,t'
= simplify_cond ([], t'
) in
259 let _,e'
= simplify_cond ([], e'
) in
260 if is_false e'
&& is_expr t'
(* c' && t' *)
262 let t'
= simplify_expr ([],t'
) in
263 [], Sexpr
(Ebinop
(Band
, c'
, t'
))
264 else if is_true e'
&& is_expr t'
(* !c' || t' *)
266 let t'
= simplify_expr ([],t'
) in
267 [], Sexpr
(Ebinop
(Bor
,Eunop
(Unot
,c'
),t'
))
268 else if is_true t'
&& is_expr e'
(* c' || e' *)
270 let e'
= simplify_expr ([],e'
) in
271 [], Sexpr
(Ebinop
(Bor
,c'
,e'
))
272 else if is_false t'
&& is_expr e'
(* !c' && e' *)
274 let e'
= simplify_expr ([],e'
) in
275 [], Sexpr
(Ebinop
(Band
,Eunop
(Unot
,c'
),e'
))
279 (* Operator precedence, needed to compute which parentheses can be removed *)
281 let prec_unop = function
282 | Unot
| Ustar
| Uaddr
| Upreincr
| Upredecr
-> 2
283 | Upostincr
| Upostdecr
-> 1
285 let prec_binop = function
287 | Bor
-> 11 (* really 12, but this avoids Wparentheses *)
290 | Blt
| Ble
| Bgt
| Bge
-> 6
292 let is_const_unop (u
:unop
) = match u
with
294 | Ustar
| Uaddr
| Upreincr
| Upostincr
| Upredecr
| Upostdecr
-> false
296 let is_const_binop (b
:binop
) = match b
with
297 | Band
| Bor
| Beq
| Bne
| Blt
| Ble
| Bgt
| Bge
-> true
300 let rec is_const_expr = function
302 | Eunop
(u
,e) -> is_const_unop u
&& is_const_expr e
303 | Ebinop
(b
,e1
,e2
) ->
304 is_const_binop b
&& is_const_expr e1
&& is_const_expr e2
305 | Equestion
(c
,_,_) -> is_const_expr c
306 | Ecast
(_,_) -> true
307 | Ecall
(_,_) -> false
310 | Elikely
e | Eunlikely
e -> is_const_expr e
311 | Esize_expr
_ -> false
312 | Esize_type
_ -> true
313 | Eindex
(_,_) | Edot
(_,_) | Earrow
(_,_) -> false
314 | Esyntax
(_,_,_,_,_) -> false
315 | Esyntaxrename
_ -> false
317 let rec get_const_expr (d,s) =
318 let fail () = raise
(Unsupported
"non-constant array size") in
320 then match elim_empty_blocks (elim_nop s) with
321 | Sexpr
e -> if is_const_expr e then e
323 | Sblock
(d, s) -> get_const_expr (d,s)
327 let rec has_unboxed_struct = function
330 | Tunion
(_, lidty
) -> List.exists
has_unboxed_struct (List.map snd lidty
)
331 | Tsyntax
(_, lty
) -> List.exists
has_unboxed_struct lty
334 let rec has_array = function
336 | Tmutable
t -> has_array t
337 | Tsyntax
(_,lty
) -> List.exists
has_array lty
338 | Tstruct
(_, lsty
) -> List.exists
has_array (List.map snd lsty
)
339 | Tunion
(_,lidty
) -> List.exists
has_array (List.map snd lidty
)
342 let rec should_not_escape = function
346 | Tunion
(_, lidty
) -> List.exists
should_not_escape (List.map snd lidty
)
347 | Tsyntax
(_, lty
) -> List.exists
should_not_escape lty
350 let left_assoc = function
351 | Beq
| Bne
| Blt
| Ble
| Bgt
| Bge
-> true
353 | Band
| Bor
-> false (* avoids Wparentheses *)
355 let right_assoc = function
359 let e_map fe
(e:expr
) =
361 | Enothing
-> Enothing
362 | Eunop
(u
,e) -> Eunop
(u
, fe
e)
363 | Ebinop
(b
, e1
, e2
) -> Ebinop
(b
, fe e1
, fe e2
)
364 | Equestion
(c
, t, e) -> Equestion
(fe c
, fe
t, fe
e)
365 | Ecast
(t,e) -> Ecast
(t, fe
e)
366 | Ecall
(f
,args
) -> Ecall
(fe f
, List.map fe args
)
367 | Econst c
-> Econst c
369 | Elikely
e -> Elikely
(fe
e)
370 | Eunlikely
e -> Eunlikely
(fe
e)
371 | Esize_expr
e -> Esize_expr
(fe
e)
372 | Esize_type
t -> Esize_type
t
373 | Eindex
(a
,i
) -> Eindex
(fe a
, fe i
)
374 | Edot
(e,f
) -> Edot
(fe
e, f
)
375 | Earrow
(e, f
) -> Earrow
(fe
e, f
)
376 | Esyntaxrename
(s,args
) -> Esyntaxrename
(s, List.map fe args
)
377 | Esyntax
(s,rt
,ta
,args
,pl
) ->
378 Esyntax
(s, rt
, ta
, List.map
(fun (e, t) -> (fe
e, t)) args
, pl
)
380 let s_map fd fs fe
(s:stmt
) = match s with
382 | Sexpr
e -> Sexpr
(fe
e)
383 | Sblock
(d,s) -> Sblock
(List.map fd
d, fs
s)
384 | Sseq
(s1,s2) -> Sseq
(fs
s1, fs
s2)
385 | Sif
(ce
,ts
,es
) -> Sif
(fe ce
, fs ts
, fs es
)
386 | Swhile
(c
,b
) -> Swhile
(fe c
, fs b
)
387 | Sfor
(e1
,e2
,e3
,b
) -> Sfor
(fe e1
, fe e2
, fe e3
, fs b
)
389 | Sreturn
e -> Sreturn
(fe
e)
394 syntax
: Printer.syntax_map
;
395 literal
: Printer.syntax_map
; (*TODO handle literals*)
396 kn
: Pdecl.known_map
;
397 prec
: (int list
) Mid.t;
400 let debug_c_extraction = Debug.register_info_flag
403 let debug_c_no_error_msgs =
405 ~desc
:"Disable the printing of the error messages in the C extraction"
408 module Print
= struct
415 exception Unprinted
of string
417 let c_keywords = ["auto"; "break"; "case"; "char"; "const"; "continue"; "default"; "do"; "double"; "else"; "enum"; "extern"; "float"; "for"; "goto"; "if"; "int"; "long"; "register"; "return"; "short"; "signed"; "sizeof"; "static"; "struct"; "switch"; "typedef"; "union"; "unsigned"; "void"; "volatile"; "while" ]
419 let () = assert (List.length
c_keywords = 32)
421 let sanitizer = sanitizer char_to_lalpha char_to_alnumus
422 let sanitizer s = Strings.lowercase
(sanitizer s)
423 let local_printer = ref None
424 let global_printer = ref None
425 let printers_initialized = ref false
427 let init_printers blacklist
=
428 if not
!printers_initialized
430 let bl = c_keywords@blacklist
in
431 local_printer := Some
(create_ident_printer
bl ~
sanitizer);
432 global_printer := Some
(create_ident_printer
bl ~
sanitizer);
433 printers_initialized := true
436 let c_static_inline = create_attribute
"extraction:c_static_inline"
437 (* prints the c inline keyword *)
439 let print_local_ident fmt id
=
440 pp_print_string fmt
(id_unique
(Option.get
!local_printer) id
)
441 let print_global_ident fmt id
=
442 pp_print_string fmt
(id_unique
(Option.get
!global_printer) id
)
444 let clear_local_printer () = Ident.forget_all
(Option.get
!local_printer)
446 let space_nolinebreak fmt
() = pp_print_string fmt
" "
448 let protect_on ?
(boxed
=false) x
s =
449 if x
then "@[<1>(" ^^
s ^^
")@]"
450 else if not boxed
then "@[" ^^
s ^^
"@]"
453 let extract_stars ty
=
454 let rec aux acc
= function
455 | Tptr
t -> aux (acc
+1) t
460 let char_escape c
= match c
with
462 | _ -> Constant.default_escape c
464 let rec print_ty ?
(paren
=false) fmt
= function
465 | Tvoid
-> pp_print_string fmt
"void"
467 syntax_arguments
s (print_ty ~paren
:false) fmt tl
468 | Tptr ty
-> fprintf fmt
"%a *" (print_ty ~paren
:true) ty
469 (* should be handled in extract_stars *)
470 | Tarray
(ty
, Enothing
) ->
471 fprintf fmt
(protect_on paren
"%a *")
472 (print_ty ~paren
:true) ty
473 (* raise (Unprinted "printing array type with unknown size")*)
474 | Tarray
(ty
, expr
) ->
475 fprintf fmt
(protect_on paren
"%a[%a]")
476 (print_ty ~paren
:true) ty
(print_expr ~prec
:1) expr
477 | Tstruct
(s,_) -> fprintf fmt
"struct %s" s
478 | Tunion
_ -> raise
(Unprinted
"unions")
479 | Tnamed id
-> print_global_ident fmt id
480 | Tmutable ty
-> print_ty ~paren fmt ty
481 | Tnosyntax
-> raise
(Unprinted
"type without syntax")
483 and print_unop fmt
= function
484 | Unot
-> pp_print_string fmt
"!"
485 | Ustar
-> pp_print_string fmt
"*"
486 | Uaddr
-> pp_print_string fmt
"&"
487 | Upreincr
| Upostincr
-> pp_print_string fmt
"++"
488 | Upredecr
| Upostdecr
-> pp_print_string fmt
"--"
490 and unop_postfix
= function
491 | Upostincr
| Upostdecr
-> true
494 and print_binop fmt
= function
495 | Band
-> pp_print_string fmt
"&&"
496 | Bor
-> pp_print_string fmt
"||"
497 | Beq
-> pp_print_string fmt
"=="
498 | Bne
-> pp_print_string fmt
"!="
499 | Bassign
-> pp_print_string fmt
"="
500 | Blt
-> pp_print_string fmt
"<"
501 | Ble
-> pp_print_string fmt
"<="
502 | Bgt
-> pp_print_string fmt
">"
503 | Bge
-> pp_print_string fmt
">="
505 and print_expr ~prec fmt
= function
506 (* invariant: 0 <= prec <= 15 *)
509 let p = prec_unop u
in
511 then fprintf fmt
(protect_on (prec
< p) "%a%a")
512 (print_expr ~prec
:(p-1)) e print_unop u
513 else fprintf fmt
(protect_on (prec
< p) "%a%a")
514 print_unop u
(print_expr ~prec
:(p-1)) e
516 let p = prec_binop b
in
517 let pleft = if left_assoc b
then p else p-1 in
518 let pright = if right_assoc b
then p else p-1 in
519 fprintf fmt
(protect_on (prec
< p) "%a %a %a")
520 (print_expr ~prec
:pleft) e1 print_binop b
(print_expr ~prec
:pright) e2
521 | Equestion
(c
,t,e) ->
522 fprintf fmt
(protect_on (prec
< 13) "%a ? %a : %a")
523 (print_expr ~prec
:12) c
524 (print_expr ~prec
:15) t
525 (print_expr ~prec
:13) e
527 fprintf fmt
(protect_on (prec
< 2) "(%a)%a")
528 (print_ty ~paren
:false) ty
(print_expr ~prec
:2) e
529 | Esyntaxrename
(s, l
) ->
530 (* call to function defined in the prelude *)
531 fprintf fmt
(protect_on (prec
< 1) "%s(%a)")
532 s (print_list comma
(print_expr ~prec
:15)) l
533 | Ecall
(Evar id
, l
) ->
534 fprintf fmt
(protect_on (prec
< 1) "%a(%a)")
535 print_global_ident id
(print_list comma
(print_expr ~prec
:15)) l
536 | Ecall
((Esyntax
_ as e),l
) ->
537 fprintf fmt
(protect_on (prec
< 1) "%a(%a)")
538 (print_expr ~prec
:1) e (print_list comma
(print_expr ~prec
:15)) l
539 | Ecall
_ -> raise
(Unsupported
"complex function expression")
540 | Econst c
-> print_const fmt c
541 | Evar id
-> print_local_ident fmt id
542 | Elikely
e -> fprintf fmt
543 (protect_on (prec
< 1) "__builtin_expect(%a,1)")
544 (print_expr ~prec
:15) e
545 | Eunlikely
e -> fprintf fmt
546 (protect_on (prec
< 1) "__builtin_expect(%a,0)")
547 (print_expr ~prec
:15) e
549 fprintf fmt
(protect_on (prec
< 2) "sizeof(%a)") (print_expr ~prec
:15) e
551 fprintf fmt
(protect_on (prec
< 2) "sizeof(%a)")
552 (print_ty ~paren
:false) ty
554 fprintf fmt
(protect_on (prec
< 1) "%a.%s")
555 (print_expr ~prec
:1) e s
557 fprintf fmt
(protect_on (prec
<= 1) "%a[%a]")
558 (print_expr ~prec
:1) e1
559 (print_expr ~prec
:15) e2
561 fprintf fmt
(protect_on (prec
< 1) "%a->%s")
562 (print_expr ~prec
:1) e s
563 | Esyntax
("%1", _, _, [e,_], _) ->
564 print_expr ~prec fmt
e
565 | Esyntax
(s, t, args
, lte
, pl
) ->
567 if pl
= [] || prec
< List.hd pl
568 then Format.sprintf
"(%s)" s
570 let lte = Array.of_list
lte in
573 | None
-> print_expr ~prec
:p fmt
(fst
lte.(i
- 1))
575 let v = if i
= 0 then t else snd
lte.(i
- 1) in
576 print_ty ~paren
:false fmt
v
578 print_ty ~paren
:false fmt args
.(i
)
579 | Some '
d'
-> (* dereference the value *)
580 begin match fst
lte.(i
- 1) with
581 | Eunop
(Uaddr
, e) -> print_expr ~prec
:p fmt
e
582 | e -> print_expr ~prec
:p fmt
(Eunop
(Ustar
, e))
584 | Some c
-> raise
(BadSyntaxKind c
) in
585 gen_syntax_arguments_prec fmt
s pr pl
587 and print_const fmt
= function
588 | Cint
(s,_) | Cfloat
s -> pp_print_string fmt
s
589 | Cchar
s -> fprintf fmt
"'%s'" Constant.(escape
char_escape s)
590 | Cstring
s -> fprintf fmt
"\"%s\"" Constant.(escape default_escape
s)
591 let print_id_init ?
(size
=None
) ~stars fmt ie
=
593 then fprintf fmt
"%s " (String.make stars '
*'
)
596 | None
, (id
, Enothing
) -> print_local_ident fmt id
598 fprintf fmt
"%a = %a"
599 print_local_ident id
(print_expr ~prec
:(prec_binop Bassign
)) e
600 | Some Enothing
, (id
, Enothing
) ->
601 (* size unknown, treat as pointer *)
602 fprintf fmt
"* %a" print_local_ident id
603 | Some
e, (id
, Enothing
) ->
604 fprintf fmt
"%a[%a]" print_local_ident id
(print_expr ~prec
:15) e
605 | Some _es
, (_id
, _ei
) -> raise
(Unsupported
"array initializer")
607 let print_expr_no_paren fmt expr
= print_expr ~prec
:max_int fmt expr
609 let rec print_stmt ~braces fmt
= function
610 | Snop
| Sexpr Enothing
-> Debug.dprintf
debug_c_extraction "snop"; ()
611 | Sexpr
e -> fprintf fmt
"%a;" print_expr_no_paren e;
612 | Sblock
([] ,s) when not braces
->
613 (print_stmt ~braces
:false) fmt
s
615 fprintf fmt
"@[<hov>{@\n @[<hov>%a@]@\n}@]" print_body b
616 | Sseq
(s1,s2) -> fprintf fmt
"%a@\n%a"
617 (print_stmt ~braces
:false) s1
618 (print_stmt ~braces
:false) s2
619 | Sif
(c
,t,e) when is_nop e ->
620 fprintf fmt
"@[<hov 2>if (%a) {@\n@[<hov>%a@]@]@\n}"
621 print_expr_no_paren c
(print_stmt ~braces
:false) t
624 "@[<hov 2>if (%a) {@\n@[<hov>%a@]@]@\n@[<hov 2>} else {@\n@[<hov>%a@]@]@\n}"
625 print_expr_no_paren c
626 (print_stmt ~braces
:false) t
627 (print_stmt ~braces
:false) e
628 | Swhile
(e,b
) -> fprintf fmt
"@[<hov 2>while (%a) {@\n@[<hov>%a@]@]@\n}"
629 print_expr_no_paren e (print_stmt ~braces
:false) b
630 | Sfor
(einit
, etest
, eincr
, s) ->
631 fprintf fmt
"@[<hov 2>for (%a; %a; %a) {@\n@[<hov>%a@]@]@\n}"
632 print_expr_no_paren einit
633 print_expr_no_paren etest
634 print_expr_no_paren eincr
635 (print_stmt ~braces
:false) s
636 | Sbreak
-> pp_print_string fmt
"break;"
637 | Sreturn Enothing
-> pp_print_string fmt
"return;"
638 | Sreturn
e -> fprintf fmt
"return %a;" print_expr_no_paren e
640 and print_def ~global fmt def
=
641 let print_inline fmt id
=
642 if Sattr.mem
c_static_inline id
.id_attrs
643 then pp_print_string fmt
"static inline " in
645 | Dfun
(id
,(rt
,args
),body
) ->
646 fprintf fmt
"@[@\n@[<hv 2>%a%a %a(@[%a@]) {@\n@[%a@]@]\n}@]"
648 (print_ty ~paren
:false) rt
649 print_global_ident id
651 (print_pair_delim nothing
space_nolinebreak nothing
652 (print_ty ~paren
:false) print_local_ident))
655 | Dproto
(id
, (rt
, args
)) ->
656 fprintf fmt
"@\n%a %a(@[%a@]);"
657 (print_ty ~paren
:false) rt
658 print_global_ident id
660 (print_pair_delim nothing
space_nolinebreak nothing
661 (print_ty ~paren
:false) print_local_ident))
663 | Ddecl
(Tarray
(ty
, e), lie
) ->
664 fprintf fmt
"%a @[<hov>%a@];"
665 (print_ty ~paren
:false) ty
666 (print_list comma
(print_id_init ~stars
:0 ~size
:(Some
e)))
669 if global
then pp_force_newline fmt
();
670 let nb, ty
= extract_stars ty
in
672 fprintf fmt
"%a @[<hov>%a@];"
673 (print_ty ~paren
:false) ty
674 (print_list comma
(print_id_init ~stars
:nb ~size
:None
))
676 | Dextern
(ty
, id
) ->
677 fprintf fmt
"@\nextern %a %a;" (print_ty ~paren
:false) ty
print_local_ident id
679 fprintf fmt
"@\nstruct %s {@\n%a};" s
680 (print_list_suf newline
681 (fun fmt
(s,ty
) -> fprintf fmt
" %a %s;"
682 (print_ty ~paren
:false) ty
s))
685 fprintf fmt
"struct %s;@;" s
686 | Dinclude
(id
, Sys
) ->
687 fprintf fmt
"#include <%s.h>" (sanitizer id
.id_string
)
688 | Dinclude
(id
, Proj
) ->
689 fprintf fmt
"#include \"%s.h\"" (sanitizer id
.id_string
)
690 | Dtypedef
(ty
,id
) ->
691 fprintf fmt
"@[<hov>typedef@ %a@;%a;@]"
692 (print_ty ~paren
:false) ty
print_global_ident id
694 and print_body fmt
(def
, s) =
696 then print_stmt ~braces
:true fmt
s
698 print_pair_delim nothing newline nothing
699 (print_list newline
(print_def ~global
:false))
700 (print_stmt ~braces
:true)
703 let print_global_def fmt def
=
705 clear_local_printer ();
706 let buf = Buffer.create
1024 in
707 let f = formatter_of_buffer
buf in
708 print_def ~global
:true f def
;
710 pp_print_string fmt
(Buffer.contents
buf)
713 if Debug.test_noflag
debug_c_no_error_msgs
716 "Could not print declaration of %s. Unsupported: %s@."
721 module MLToC
= struct
731 let field i
= "__field_"^
(string_of_int i
)
733 let structs : struct_def
Hid.t = Hid.create
16
734 let aliases : C.ty
Hid.t = Hid.create
16
735 let globals : unit Hid.t = Hid.create
16
737 let array = create_attribute
"extraction:array"
738 let array_mk = create_attribute
"extraction:array_make"
739 let likely = create_attribute
"extraction:likely"
740 let unlikely = create_attribute
"extraction:unlikely"
742 let decl_attribute = create_attribute
"extraction:c_declaration"
743 let rec ty_of_mlty info
= function
744 | Tvar
{ tv_name
= id
} ->
745 begin match query_syntax info
.syntax id
747 | Some
s -> C.Tsyntax
(s, [])
748 | None
-> C.Tnamed id
750 | Tapp
(id
, [t]) when Sattr.mem
array id
.id_attrs
->
751 Tarray
(ty_of_mlty info
t, Enothing
)
752 | Tapp
(id
, [t]) when id_equal id
Pmodule.ts_ref
.ts_name
->
753 Tmutable
(ty_of_mlty info
t)
755 begin match query_syntax info
.syntax id
757 | Some
s -> C.Tsyntax
(s, List.map
(ty_of_mlty info
) tl
)
760 then if Hid.mem
aliases id
761 then Hid.find
aliases id
762 else try Tstruct
(Hid.find
structs id
)
763 with Not_found
-> Tnosyntax
766 | Ttuple
[] -> C.Tvoid
767 | Ttuple
[t] -> ty_of_mlty info
t
768 | Ttuple
_ -> raise
(Unsupported
"tuple parameters")
769 | Tarrow
_ -> raise
(Unsupported
"arrow type")
771 let struct_of_rs info mlty rs
: struct_def
=
772 let s = match query_syntax info
.syntax rs
.rs_name
with
774 | None
-> rs
.rs_name
.id_string
in
775 let name = Pp.sprintf
"__%s_result" s in
779 let rec fields fr tys
= match tys
with
781 | ty
::l
-> (field fr
, ty_of_mlty info ty
)::(fields (fr
+1) l
) in
787 let struct_of_rs info mlty
=
788 Wrs.memoize
17 (fun rs
-> struct_of_rs info mlty rs
)
790 let ity_of_expr e = match e.e_ity
with
794 let pv_name pv
= pv
.pv_vs
.vs_name
796 let is_constructor rs its
=
797 List.exists
(rs_equal rs
) its
.Pdecl.itd_constructors
799 let is_struct_constructor info rs
=
801 if is_rs_tuple rs
then false
802 else match Mid.find_opt rs
.rs_name info
.kn
with
803 | Some
{ pd_node
= PDtype its
} ->
804 List.exists
(is_constructor rs
) its
807 let struct_of_constructor info rs
=
809 match Mid.find rs
.rs_name info
.kn
with
810 | { pd_node
= PDtype its
} ->
811 let its = List.find
(is_constructor rs
) its in
812 let id = its.itd_its
.its_ts
.ts_name
in
813 (try Hid.find
structs id
814 with Not_found
-> raise
(Unsupported
"bad struct"))
817 type syntax_env
= { in_unguarded_loop
: bool;
818 computes_return_value
: bool;
819 current_function
: rsymbol
;
823 array_sizes
: C.expr
Mid.t;
825 (* is this struct boxed or passed by value? *)
828 let is_unit = function
832 let handle_likely attrs
(e:C.expr
) =
833 let lkl = Sattr.mem
likely attrs
in
834 let ulk = Sattr.mem
unlikely attrs
in
836 then (if ulk then e else Elikely
e)
837 else (if ulk then Eunlikely
e else e)
839 let reverse_likely = function
840 | Elikely
e -> Eunlikely
e
841 | Eunlikely
e -> Elikely
e
844 let lit_one = Number.(int_literal ILitDec ~neg
:false "1")
845 let lit_zero = Number.(int_literal ILitDec ~neg
:false "0")
847 (* /!\ Do not use if e may have type unit and not be Enothing *)
848 let expr_or_return env
e =
849 if env
.computes_return_value
853 let ity_escapes_from_expr env ity
e =
854 let aregs = ity_exp_fold
Sreg.add_left
Sreg.empty ity
in
855 let reset_regs = e.e_effect
.eff_resets
in
856 let locked_regs = Sreg.union env
.ret_regs
reset_regs in
857 not
(Sreg.is_empty
(Sreg.inter
aregs locked_regs))
859 let var_escapes_from_expr env
v e =
860 ity_escapes_from_expr env
v.pv_ity
e
862 let rec expr info env
(e:Mltree.expr) : C.body
=
863 let do_let id ity cty le
e =
864 if should_not_escape cty
&& ity_escapes_from_expr env ity
e
865 then raise
(Unsupported
"array or struct escaping function");
866 let (d,s) = expr info
{env
with computes_return_value
= false} le
in
867 let initblock = d, C.assignify (Evar
id) s in
868 [ C.Ddecl
(cty
, [id, C.Enothing
]) ],
869 C.Sseq
(C.Sblock
initblock, C.Sblock
(expr info env
e)) in
870 let do_for (eb
: pvsymbol
) (ee
: Mltree.expr option)
871 (sb
: pvsymbol
) (se
: Mltree.expr option) i imlty dir body
=
873 match i
.pv_vs
.vs_ty
.ty_node
with
874 | Tyapp
({ ts_def
= Range
{ ir_lower
= lb
; ir_upper
= ub
}},_) ->
875 let init_test_ok, end_test_ok
=
877 | _, Some
{ e_node
= Mltree.Econst
(Constant.ConstInt ec
) } ->
880 then BigInt.lt ec
.il_int ub
881 else BigInt.lt lb ec
.il_int
882 | Some
{ e_node
= Mltree.Econst
(Constant.ConstInt sc
) }, _ ->
884 then BigInt.eq sc
.il_int lb
885 else BigInt.eq sc
.il_int ub
),
887 | _, _ -> false, false
889 let ty = ty_of_mlty info imlty
in
890 let di = C.Ddecl
(ty, [i
.pv_vs
.vs_name
, Enothing
]) in
891 let ei = C.Evar
(i
.pv_vs
.vs_name
) in
892 let env_f = { env
with computes_return_value
= false } in
893 let ds, is
, ie
= match se
with
895 let ds, is
= expr info
env_f se
in
897 | Sexpr
(Econst
_ | Evar
_ as e) ->
900 let iv = Evar
(pv_name sb
) in
901 let is = assignify iv is in
902 C.Ddecl
(ty, [pv_name sb
, Enothing
]) :: ds, is, iv
904 | None
-> [], Snop
, Evar
(pv_name sb
)
906 let init_e = C.Ebinop
(Bassign
, ei, ie
) in
910 let de, es
= expr info
env_f ee
in
912 | Sexpr
(Econst
_ | Evar
_ as e) ->
915 let ev = Evar
(pv_name eb
) in
916 let es = assignify ev es in
917 C.Ddecl
(ty, [pv_name eb
, Enothing
]) :: de, es, ev
919 | None
-> [], Snop
, Evar
(pv_name eb
)
921 let d = di :: ds @ de in
922 let incr_op = match dir
with To
-> Upreincr
| DownTo
-> Upredecr
in
923 let incr_e = C.Eunop
(incr_op, ei) in
924 let env'
= { env with computes_return_value
= false;
925 in_unguarded_loop
= true;
927 if env.in_unguarded_loop
928 then Sid.empty
else env.breaks
} in
929 let bd, bs
= expr info
env' body
in
930 let test_op = match dir
with | To
-> C.Ble
| DownTo
-> C.Bge
in
933 then C.Sfor
(init_e, C.Ebinop
(test_op, ei, ee
),
934 incr_e, C.Sblock
(bd, bs
))
936 let end_test = C.Sif
(C.Ebinop
(C.Beq
, ei, ee
),
938 let bs = C.Sseq
(bs, end_test) in
939 C.Sfor
(init_e, Enothing
, incr_e, C.Sblock
(bd,bs)) in
940 let ise = C.Sseq
(is, es) in
941 let s = if init_test_ok
943 else C.(Sif
(Ebinop
(test_op, ie
, ee
), sloop, Snop
)) in
946 raise
(Unsupported
"for loops where loop index is not a range type")
949 | Eblock
[] -> ([], expr_or_return env Enothing
)
950 | Eblock
[e] -> [], C.Sblock
(expr info
env e)
952 let env_f = { env with computes_return_value
= false } in
953 let rec aux = function
954 | [] -> ([], expr_or_return env Enothing
)
955 | [s] -> ([], C.Sblock
(expr info
env s))
956 | h
::t -> ([], C.Sseq
(C.Sblock
(expr info
env_f h
),
960 | Eapp
(_, _, true) -> raise
(Unsupported
"partial application")
961 | Eapp
(rs
, [], false) when rs_equal rs rs_true
->
962 Debug.dprintf
debug_c_extraction "true@.";
963 ([],expr_or_return env (C.Econst
(Cint
("1",lit_one))))
964 | Eapp
(rs
, [], false) when rs_equal rs rs_false
->
965 Debug.dprintf
debug_c_extraction "false@.";
966 ([],expr_or_return env (C.Econst
(Cint
("0", lit_zero))))
968 let id = pv_name pv
in
970 ([], expr_or_return env e)
971 | Mltree.Econst
(Constant.ConstStr
s) ->
972 C.([], expr_or_return env (Econst
(Cstring
s)))
973 | Mltree.Econst
(Constant.ConstReal
_) ->
974 raise
(Unsupported
"real constant")
975 | Mltree.Econst
(Constant.ConstInt ic
) ->
979 if BigInt.lt
n BigInt.zero
981 (* default to base 10 for negative literals *)
982 Format.fprintf fmt
"-%a" (print_in_base
10 None
) (BigInt.abs
n)
984 match ic
.il_kind
with
986 -> Format.fprintf fmt
"0x%a" (print_in_base
16 None
) n
987 | ILitOct
-> Format.fprintf fmt
"0%a" (print_in_base
8 None
) n
988 | ILitDec
| ILitUnk
->
989 (* default to base 10 *)
990 print_in_base
10 None fmt
n in
992 let tname = match e.e_mlty
with
994 | _ -> assert false in
995 begin match query_syntax info
.literal
tname with
997 Format.asprintf
"%a" (syntax_range_literal ~cb
:(Some
print) st
) ic
999 let s = tname.id_string
in
1000 raise
(Unsupported
("unspecified number format for type "^
s)) end
1002 let e = C.(Econst
(Cint
(s, ic
))) in
1003 ([], expr_or_return env e)
1004 | Eapp
(rs
, [], _) when Hid.mem
globals rs
.rs_name
->
1005 Debug.dprintf
debug_c_extraction "global variable %s@."
1006 rs
.rs_name
.id_string
;
1007 [], expr_or_return env (Evar rs
.rs_name
)
1008 | Eapp
(rs
, _, _) when Sattr.mem
decl_attribute rs
.rs_name
.id_attrs
->
1009 raise
(Unsupported
"local variable declaration call outside let-in")
1010 | Eapp
(rs
, [e], _) when rs_equal rs
Pmodule.rs_ref
->
1011 Debug.dprintf
debug_c_extraction "ref constructor@.";
1012 let env_f = { env with computes_return_value
= false } in
1013 let arg = simplify_expr (expr info
env_f e) in
1014 ([], expr_or_return env arg)
1016 when is_struct_constructor info rs
1017 && query_syntax info
.syntax rs
.rs_name
= None
->
1018 Debug.dprintf
debug_c_extraction "constructor %s@." rs
.rs_name
.id_string
;
1022 assert (not
e.e_effect
.eff_ghost
);
1023 (not
(Sattr.mem dummy_expr_attr
e.e_attrs
)) &&
1024 (not
(is_unit e.e_mlty
)))
1026 let env_f = { env with computes_return_value
= false } in
1027 let args = List.map
(fun e -> simplify_expr (expr info
env_f e)) args in
1028 let ((sname
, sfields
) as sd
) = struct_of_constructor info rs
in
1029 let id = id_register
(id_fresh sname
) in
1030 let defs = [ Ddecl
(Tstruct sd
, [id, Enothing
]) ] in
1032 let assign_expr f arg = Sexpr
(Ebinop
(Bassign
, Edot
(st, f), arg)) in
1035 (fun acc
(f, _ty
) arg -> Sseq
(acc
,assign_expr f arg))
1036 Snop sfields
args in
1037 (defs, Sseq
(inits, expr_or_return env st))
1038 | Eapp
(rs
, el
, _) ->
1039 Debug.dprintf
debug_c_extraction "call to %s@." rs
.rs_name
.id_string
;
1040 let args = List.filter
(fun e -> not
(is_unit e.e_mlty
)) el
1041 in (*FIXME still needed with masks? *)
1042 let env_f = { env with computes_return_value
= false } in
1046 | [] -> C.([], expr_or_return env Enothing
);
1047 | [e] -> expr info
env e
1049 let id_struct = id_register
(result_id
()) in
1050 let e_struct = C.Evar
id_struct in
1053 (struct_of_rs info
e.e_mlty
env.current_function
),
1054 [id_struct, Enothing
])) in
1055 let assign i
(d,s) =
1056 C.Sblock
(d,assignify C.(Edot
(e_struct, field i
)) s) in
1057 let rec assigns args i
=
1061 let b = expr info
env_f e in
1062 C.Sseq
(assign i
b, assigns t (i
+1)) in
1063 C.([d_struct], Sseq
(assigns args 0, expr_or_return env e_struct))
1066 let (prdefs
, prstmt
), e'
=
1067 let prelude, unboxed_params
=
1069 (fun ((accd
, accs
) as acc
) e ->
1070 let pty = ty_of_mlty info
e.e_mlty
in
1071 let d, s = expr info
env_f e in
1074 (simplify_expr (d,s), pty)
1076 (* FIXME: use a dedicated exception instead.
1077 see also [simplify_expr]. *)
1078 Invalid_argument
"simplify_expr" ->
1079 let s'
, e'
= get_last_expr s in
1080 (accd
@d, Sseq
(accs
, s'
)), (e'
, pty))
1087 | (ce
, Tstruct
s), { e_ity
= I
{ ity_node
= Ityreg r
}}
1088 when not
(Hreg.mem
env.boxed r
) ->
1089 C.(Eunop
(Uaddr
, ce
), Tptr
(Tstruct
s))
1090 | (ce
, Tmutable
t), { e_ity
= I
{ ity_node
= Ityreg r
}}
1091 when not
(Hreg.mem
env.boxed r
) ->
1092 (Eunop
(Uaddr
, ce
), Tptr
t)
1094 unboxed_params
args in
1096 match query_syntax info
.syntax rs
.rs_name
with
1099 String.contains
s '
%'
1100 || String.contains
s ' '
1101 || String.contains
s '
('
in
1102 let p = Mid.find rs
.rs_name info
.prec
in
1105 let mlty = e.e_mlty
in
1106 let args = match mlty with
1108 | Tapp
(_, args) | Ttuple
args->
1109 Array.of_list
(List.map
(ty_of_mlty info
) args)
1112 C.Esyntax
(s,ty_of_mlty info
mlty, args, params, p)
1115 then C.(Esyntax
(s, Tnosyntax
, [||], [], p)) (*constant*)
1117 (*function defined in the prelude *)
1118 let cargs = List.map fst
params in
1119 C.(Esyntaxrename
(s, cargs))
1121 match rs
.rs_field
with
1123 C.(Ecall
(Evar
(rs
.rs_name
), List.map fst
params))
1125 assert (List.length el
= 1);
1126 begin match unboxed_params
, args with
1127 | [(ce
, Tstruct
_)], [{ e_ity
= I
{ ity_node
= Ityreg r
}}] ->
1128 if Hreg.mem
env.boxed r
1129 then C.Earrow
(ce
, (pv_name pv
).id_string
)
1130 else C.Edot
(ce
, (pv_name pv
).id_string
)
1131 | [(ce
, Tmutable
_)], [{ e_ity
= I
{ ity_node
= Ityreg r
}}] ->
1132 if Hreg.mem
env.boxed r
then C.Eunop
(C.Ustar
, ce
) else ce
1133 | _ -> C.Edot
(fst
(List.hd
params), (pv_name pv
).id_string
)
1137 if env.computes_return_value
1140 then Sseq
(Sexpr
e'
, Sreturn Enothing
)
1145 else C.(prdefs
, Sseq
(prstmt
, s))
1146 | Eif
(cond
, th
, el
) ->
1147 let cd,cs
= expr info
{env with computes_return_value
= false} cond
in
1148 let t = expr info
env th
in
1149 let e = expr info
env el
in
1150 begin match simplify_cond (cd, cs
) with
1152 let c = handle_likely cond
.e_attrs
c in
1153 if Mltree.is_false th
&& Mltree.is_true el
1154 then C.([], expr_or_return env (Eunop
(Unot
, c)))
1155 else [], C.Sif
(c,C.Sblock
t, C.Sblock
e)
1157 let cid = id_register
(id_fresh
"cond") in (* ? *)
1158 C.Ddecl
(C.Tsyntax
("int",[]), [cid, C.Enothing
])::cdef
,
1159 C.Sseq
(C.assignify (Evar
cid) cs
,
1160 C.Sif
((handle_likely cond
.e_attrs
(C.Evar
cid)),
1161 C.Sblock
t, C.Sblock
e))
1164 Debug.dprintf
debug_c_extraction "while@.";
1165 let cd, cs
= expr info
{env with computes_return_value
= false} c in
1166 (* this is needed so that the extracted expression has all
1167 needed variables in its scope *)
1168 let cd, cs
= C.flatten_defs cd cs
in
1169 let cd = C.group_defs_by_type cd in
1170 let env'
= { env with
1171 computes_return_value
= false;
1172 in_unguarded_loop
= true;
1174 if env.in_unguarded_loop
1175 then Sid.empty
else env.breaks
} in
1176 let b = expr info
env'
b in
1177 begin match (C.simplify_cond (cd,cs
)) with
1178 | cd, C.Sexpr ce
-> cd, C.Swhile
(handle_likely c.e_attrs ce
, C.Sblock
b)
1180 begin match C.get_last_expr cs
with
1181 | C.Snop
, e -> cd, C.(Swhile
(handle_likely c.e_attrs
e, C.Sblock
b))
1183 let brc = reverse_likely (handle_likely c.e_attrs
(Eunop
(Unot
,e))) in
1184 cd, C.(Swhile
(Econst
(Cint
("1", lit_one)),
1186 Sseq
(Sif
(brc, Sbreak
, Snop
),
1190 | Ematch
(b, [], (_::_ as xl
)) ->
1191 Debug.dprintf
debug_c_extraction "TRY@.";
1192 let is_while = match b.e_node
with Ewhile
_ -> true | _-> false in
1193 let breaks, returns
= List.fold_left
1194 (fun (bs,rs
) (xs
, pvsl
, r
) ->
1195 let id = xs
.xs_name
in
1196 match pvsl
, r
.e_node
with
1197 | [], (Eblock
[]) when is_while ->
1198 assert (is_unit r
.e_mlty
);
1200 | [pv
], Mltree.Evar pv'
1201 when pv_equal pv pv'
&& env.computes_return_value
->
1203 | [], Mltree.Eblock
[] when env.computes_return_value
->
1204 assert (is_unit r
.e_mlty
);
1206 | _ -> raise
(Unsupported
"non break/return exception in try"))
1207 (Sid.empty
, env.returns
) xl
1209 let env'
= { env with
1210 in_unguarded_loop
= false;
1216 | Eraise
(xs
,_) when Sid.mem xs
.xs_name
env.breaks ->
1217 Debug.dprintf
debug_c_extraction "BREAK@.";
1219 | Eraise
(xs
, Some r
) when Sid.mem xs
.xs_name
env.returns
->
1220 Debug.dprintf
debug_c_extraction "RETURN@.";
1221 expr info
{env with computes_return_value
= true} r
1223 when Sid.mem xs
.xs_name
env.returns
&&
1224 ity_equal
Ity.ity_unit
env.current_function
.rs_cty
.cty_result
1225 -> ([], C.Sreturn Enothing
)
1226 | Eraise
(xs
, None
) when Sid.mem xs
.xs_name
env.returns
->
1227 assert false (* nothing to pass to return *)
1228 | Eraise
_ -> raise
(Unsupported
"non break/return exception raised")
1229 | Elet
(Lvar
(eb
, ee
),
1230 { e_node
= Elet
(Lvar
(sb
, se
),
1231 { e_node
= Efor
(i
, ty, sb'
, dir
, eb'
, body
) })})
1232 when pv_equal sb sb'
&& pv_equal eb eb'
->
1233 Debug.dprintf
debug_c_extraction "LETLETFOR@.";
1234 do_for eb
(Some ee
) sb
(Some se
) i
ty dir body
1235 | Elet
(Lvar
(eb
, ee
), { e_node
= Efor
(i
, ty, sb
, dir
, eb'
, body
) })
1236 when pv_equal eb eb'
->
1237 Debug.dprintf
debug_c_extraction "ENDLETFOR@.";
1238 do_for eb
(Some ee
) sb None i
ty dir body
1239 | Elet
(Lvar
(sb
, se
), { e_node
= Efor
(i
, ty, sb'
, dir
, eb
, body
) })
1240 when pv_equal sb sb'
->
1241 Debug.dprintf
debug_c_extraction "STARTLETFOR@.";
1242 do_for eb None sb
(Some se
) i
ty dir body
1243 | Efor
(i
, ty, sb
, dir
, eb
, body
) ->
1244 Debug.dprintf
debug_c_extraction "FOR@.";
1245 do_for eb None sb None i
ty dir body
1246 | Ematch
(({e_node
= Eapp
(_rs
,_, _)} as _e1
), [Pwild
, _e2
], []) ->
1247 assert false (* simplified at Compile *)
1248 | Ematch
(_e1
, [Pvar _v
, _e2
], []) ->
1249 assert false (* simplified at Compile *)
1250 | Ematch
(({e_node
= Eapp
(rs
,_, _)} as e1
), [Ptuple rets
,e2
], [])
1251 | Ematch
({e_node
= Eblock
[{e_node
= Eapp
(rs
, _, _)} as e1
]}, [Ptuple rets
, e2
], [])
1253 (function | Pwild
(*ghost*) | Pvar
_ -> true |_-> false)
1256 begin match rets
with
1258 let id_struct = id_register
(id_fresh
"struct_res") in
1259 let e_struct = C.Evar
id_struct in
1260 let d_struct = C.Ddecl
(C.Tstruct
(struct_of_rs info e1
.e_mlty rs
),
1261 [id_struct, C.Enothing
]) in
1263 match e1
.e_mlty
with
1268 | Pvar vs
-> C.Ddecl
(ty_of_mlty info
ty,
1269 [vs
.vs_name
, C.Enothing
])::acc
1271 | _ -> assert false )
1273 | _ -> assert false in
1274 let d,s = expr info
{env with computes_return_value
= false} e1
in
1275 let s = assignify e_struct s in
1277 assignify (C.Evar vs
) C.(Sexpr
(Edot
(e_struct, field i
))) in
1278 let rec assigns rets i
=
1281 | Pvar vs
:: t -> C.Sseq
((assign vs
.vs_name i
), (assigns t (i
+1)))
1282 | Pwild
:: t -> assigns t (i
+1) (* ghost variable, skip *)
1283 | _ -> assert false in
1284 let assigns = assigns rets
0 in
1285 let b = expr info
env e2
in
1286 d@defs, C.(Sseq
(Sseq
(s,assigns), Sblock
b)) end
1287 | Ematch
_ -> raise
(Unsupported
"pattern matching")
1288 | Eabsurd
-> assert false
1289 | Eassign
([e1
, pvmlty
, ({rs_field
= Some
_} as rs
), e2
]) ->
1291 match (ity_of_expr e1
).ity_node
with
1292 | Ityreg r
-> Hreg.mem
env.boxed r
1294 let ce1 = simplify_expr
1295 (expr info
{ env with computes_return_value
= false } e1
) in
1297 match query_syntax info
.syntax rs
.rs_name
with
1301 Re.Str.search_forward
1302 (Re.Str.regexp
"[%]\\([tv]?\\)[0-9]+") s 0
1303 with Not_found
-> raise
(Unsupported
"assign field format") in
1305 then C.(Eunop
(Ustar
, ce1))
1307 let params = [ st, ty_of_mlty info pvmlty
] in
1308 let args = match pvmlty
with
1310 | Tapp
(_,args) | Ttuple
args ->
1311 Array.of_list
(List.map
(ty_of_mlty info
) args)
1314 let p = Mid.find rs
.rs_name info
.prec
in
1315 C.Esyntax
(s,ty_of_mlty info pvmlty
, args, params,p)
1317 let ty = ty_of_mlty info pvmlty
in
1320 if boxed then Eunop
(Ustar
, ce1) else ce1
1323 then Earrow
(ce1, rs
.rs_name
.id_string
)
1324 else Edot
(ce1, rs
.rs_name
.id_string
) in
1325 let d,v = expr info
{ env with computes_return_value
= false } e2
in
1326 d, C.(Sexpr
(Ebinop
(Bassign
, t, simplify_expr ([],v))))
1327 | Eassign
_ -> raise
(Unsupported
"assign")
1328 | Eexn
(_,_,e) -> expr info
env e
1330 Debug.dprintf
debug_c_extraction "Eignore@.";
1332 C.Sseq
(C.Sblock
(expr info
{env with computes_return_value
= false} e),
1333 if env.computes_return_value
1334 then C.Sreturn
(Enothing
)
1336 | Efun
_ -> raise
(Unsupported
"higher order")
1337 | Elet
(Lvar
(v, ({ e_node
= Eapp
(rs
, al
, _) } as ev)), e)
1338 when Sattr.mem
decl_attribute rs
.rs_name
.id_attrs
1339 && query_syntax info
.syntax rs
.rs_name
<> None
1340 && List.for_all
(fun e -> is_unit e.e_mlty
) al
1342 Debug.dprintf
debug_c_extraction "variable declaration call@.";
1343 if var_escapes_from_expr env v e
1344 then raise
(Unsupported
"local variable escaping function");
1345 let t = ty_of_mlty info
ev.e_mlty
in
1346 let scall = Option.get
(query_syntax info
.syntax rs
.rs_name
) in
1347 let p = Mid.find rs
.rs_name info
.prec
in
1348 let ecall = C.(Esyntax
(scall, Tnosyntax
, [||], [], p)) in
1349 let d = C.Ddecl
(t, [pv_name v, ecall]) in
1350 let d'
, s = expr info
env e in
1352 | Elet
(Lvar
(a
, { e_node
= Eapp
(rs
, [n; v], _) }), e)
1353 when Sattr.mem
array_mk rs
.rs_name
.id_attrs
->
1354 (* let a = Array.make n v in e*)
1355 Debug.dprintf
debug_c_extraction "call to an array constructor@.";
1356 if var_escapes_from_expr env a e
1357 then raise
(Unsupported
"array escaping function");
1358 let env_f = { env with computes_return_value
= false } in
1359 let n = expr info
env_f n in
1360 let n = get_const_expr n in
1361 let avar = pv_name a in
1362 let sizes = Mid.add
avar n env.array_sizes
in
1363 let v_ty = ty_of_mlty info
v.e_mlty
in
1364 let loop_i = id_register
(id_fresh
"i") in
1365 let d = C.([Ddecl
(Tarray
(v_ty, n), [avar, Enothing
]);
1366 Ddecl
(Tsyntax
("int", []), [loop_i, Enothing
])]) in
1367 let i = C.Evar
loop_i in
1368 let dv, sv
= expr info
env_f v in
1369 let eai = C.Eindex
(Evar
avar, i) in
1370 let assign_e = assignify eai sv
in
1371 let init_loop = C.(Sfor
(Ebinop
(Bassign
, i,
1372 Econst
(Cint
("0", lit_zero))),
1376 let env = { env with array_sizes
= sizes } in
1377 let d'
, s'
= expr info
env e in
1378 d@dv@d'
, Sseq
(init_loop, s'
)
1381 | Lvar
(pv
,le
) -> (* not a block *)
1382 let ity = pv
.pv_ity
in
1383 let cty = ty_of_mlty info le
.e_mlty
in
1384 do_let pv.pv_vs
.vs_name
ity cty le
e
1385 | Lsym
_ -> raise
(Unsupported
"LDsym")
1386 | Lrec
_ -> raise
(Unsupported
"LDrec") (* TODO for rec at least*)
1387 | Lany
_ -> raise
(Unsupported
"Lany")
1390 let translate_decl (info
:info
) (d:decl
) ~flat ~header
: C.definition list
=
1391 current_decl_name := "";
1392 let translate_fun rs
mlty vl
e =
1393 current_decl_name := rs
.rs_name
.id_string
;
1394 Debug.dprintf
debug_c_extraction "print %s@." rs
.rs_name
.id_string
;
1396 then begin Debug.dprintf
debug_c_extraction "is ghost@."; [] end
1398 let is_dummy id = Sattr.mem
Dexpr.dummy_var_attr
id.id_attrs
in
1399 let boxed = Hreg.create
16 in
1400 let keep_var (id, mlty, gh
) =
1406 else raise
(Unsupported
"non-dummy unit parameter")
1410 not
(ity_equal
pv.pv_ity
Ity.ity_unit
1411 && is_dummy pv.pv_vs
.vs_name
) in
1412 let ngvl = List.filter
keep_var vl
in
1413 let ngargs = List.filter
keep_pv rs
.rs_cty
.cty_args
in
1415 List.map2
(fun (id, mlty, _gh
) pv ->
1416 let cty = ty_of_mlty info
mlty in
1417 match has_unboxed_struct cty, pv.pv_ity
.ity_node
with
1419 Debug.dprintf
debug_c_extraction "boxing parameter %s@."
1421 Hreg.add
boxed r
();
1423 | _ -> (cty, id)) ngvl ngargs in
1424 let rity = rs
.rs_cty
.cty_result
in
1425 let ret_regs = ity_exp_fold
Sreg.add_left
Sreg.empty
rity in
1426 let is_simple_tuple ity =
1427 let arity_zero = function
1428 | Ityapp
(_,a,r
) -> a = [] && r
= []
1429 | Ityreg
{ reg_args
= a; reg_regs
= r
} ->
1433 (match ity.ity_node
with
1434 | Ityapp
({its_ts
= s},_,_)
1435 | Ityreg
{ reg_its
= {its_ts
= s}; }
1440 acc
&& arity_zero ity.ity_node
) true ity)
1442 (* FIXME is it necessary to have arity 0 in regions ?*)
1444 try ty_of_mlty info
mlty, []
1445 with Unsupported
_ when is_simple_tuple rity ->
1446 let st = struct_of_rs info
mlty rs
in
1447 C.Tstruct
st, [C.Dstruct
st] in
1448 (* struct definition goes only in the header *)
1449 let rm_struct_def = function
1450 | C.Dstruct
(s,_) ->
1451 Debug.dprintf
debug_c_extraction "removing def of %s@." s;
1455 if not
(header
|| flat
)
1456 then List.map
rm_struct_def sdecls
1458 if has_array rtype then raise
(Unsupported
"array return");
1461 if Hid.mem
globals rs
.rs_name
1462 then sdecls@[C.Dextern
(rtype, rs
.rs_name
)]
1463 else sdecls@[C.Dproto
(rs
.rs_name
, (rtype, params))]
1465 let env = { computes_return_value
= true;
1466 in_unguarded_loop
= false;
1467 current_function
= rs
;
1468 ret_regs = ret_regs;
1469 returns
= Sid.empty
;
1471 array_sizes
= Mid.empty
;
1474 let d,s = expr info
env e in
1475 let d,s = C.flatten_defs d s in
1476 let d = C.group_defs_by_type d in
1477 let s = C.elim_nop s in
1478 let s = C.elim_empty_blocks s in
1480 | Sreturn r
when rs
.rs_cty
.cty_args
= [] ->
1481 Debug.dprintf
debug_c_extraction
1482 "declaring global %s@." rs
.rs_name
.id_string
;
1483 Hid.add
globals rs
.rs_name
();
1484 sdecls@[C.Ddecl
(rtype, [rs
.rs_name
, r
])]
1485 | _ -> sdecls@[C.Dfun
(rs
.rs_name
, (rtype,params), (d,s))] in
1488 | Dlet
(Lsym
(rs
, _, mlty, vl
, e)) ->
1489 if Sattr.mem
Compile.InlineFunctionCalls.inline_attr
1490 rs
.rs_name
.id_attrs
(* call is inlined, do not extract *)
1492 else translate_fun rs
mlty vl
e
1493 | Dtype
[{its_name
=id; its_def
=idef
}] ->
1494 current_decl_name := id.id_string
;
1495 Debug.dprintf
debug_c_extraction "PDtype %s@." id.id_string
;
1496 begin match query_syntax info
.syntax
id with
1500 | Some
(Dalias
mlty) ->
1501 let ty = ty_of_mlty info
mlty in
1502 Hid.replace
aliases id ty;
1504 (*TODO print actual typedef? *)
1505 (*[C.Dtypedef (ty_of_mlty info ty, id)]*)
1506 | Some
(Drecord pjl
) ->
1511 | Ttuple
[] -> false
1517 let cty = ty_of_mlty info
ty in
1519 | Tstruct
_ -> raise
(Unsupported
"nested structs")
1520 | _ -> (id.id_string
, cty))
1523 (fun (_,t) -> match t with Tarray _ -> true | _ -> false)
1525 then raise (Unsupported "array in struct");*)
1526 let sd = id.id_string
, fields in
1527 Hid.replace
structs id sd;
1530 else [C.Dstruct_decl
id.id_string
]
1531 | Some Ddata
_ -> raise
(Unsupported
"Ddata")
1532 | Some
(Drange
_) | Some
(Dfloat
_) ->
1533 raise
(Unsupported
"Drange/Dfloat")
1534 | None
-> raise
(Unsupported
1535 ("type declaration without syntax or alias: "
1540 let translate_rdef rd
=
1541 translate_fun rd
.rec_sym rd
.rec_res rd
.rec_args rd
.rec_exp
in
1542 let defs = List.flatten
(List.map
translate_rdef rl
) in
1545 let proto_of_fun = function
1546 | C.Dfun
(id, pr, _) -> [C.Dproto
(id, pr)]
1548 let protos = List.flatten
(List.map
proto_of_fun defs) in
1550 | _ -> [] (*TODO exn ? *) end
1553 if Debug.test_noflag
debug_c_no_error_msgs
1556 "Could not translate declaration of %s. Unsupported: %s@."
1557 !current_decl_name s;
1560 let translate_decl (info
:info
) (d:Mltree.decl
) ~header ~flat
1561 : C.definition list
=
1562 let decide_print id =
1563 (not
(header
&& (Sattr.mem
Print.c_static_inline id.id_attrs
)))
1564 && query_syntax info
.syntax
id = None
in
1565 let names = Mltree.get_decl_name
d in
1566 match List.filter
decide_print names with
1568 | _ -> translate_decl info
d ~header ~flat
1572 module Transform
= struct
1576 (* Common simplifications *)
1578 let e = e_map expr e in
1580 | Equestion
(c, Econst
(Cint
(_, ic1
)), Econst
(Cint
(_, ic0
)))
1581 when is_one ic1
&& is_zero ic0
-> c (* c ? 1 : 0 *)
1582 | Equestion
(c, Econst
(Cint
(_, ic0
)), Econst
(Cint
(_, ic1
)))
1583 when is_one ic1
&& is_zero ic0
-> Eunop
(Unot
, c) (* c ? 0 : 1 *)
1584 | Eunop
(Unot
, Ebinop
(Beq
, e1
, e2
)) -> Ebinop
(Bne
, e1
, e2
) (* ! (e1 == e2) *)
1585 | Edot
(Eunop
(Ustar
, e1
), s) -> Earrow
(e1
, s)
1589 let s = s_map def stmt
expr s in
1591 | Sif
(Eunop
(Unot
, Ebinop
(Beq
, c, Econst
(Cint
(_, ic0
)))), t, e)
1592 | Sif
(Ebinop
(Bne
, c, Econst
(Cint
(_, ic0
))), t, e)
1593 when is_zero ic0
-> Sif
(c, t, e) (* if c != 0 *)
1596 and def
(d:definition
) = match d with
1597 | C.Dfun
(id,p,(dl
, s)) -> Dfun
(id, p, (List.map def dl
, stmt
s))
1598 | C.Ddecl
(ty, dl
) -> C.Ddecl
(ty, List.map
(fun (id, e) -> id, expr e) dl
)
1600 | C.Dproto
(_,_) | C.Dstruct
_
1601 | C.Dstruct_decl
_ | C.Dtypedef
(_,_)
1602 | C.Dinclude
(_,_) -> d
1604 let defs dl
= List.map def dl
1608 let name_gen suffix ?fname m
=
1609 let n = m
.Pmodule.mod_theory
.Theory.th_name
.Ident.id_string
in
1610 let n = Print.sanitizer n in
1611 let r = match fname
with
1612 | None
-> n ^ suffix
1613 | Some
f -> f ^
"__" ^
n ^ suffix
in
1616 let header_border_printer header _args ?old
:_ ?fname fmt m
=
1617 let n = Strings.uppercase
(name_gen "_H_INCLUDED" ?fname m
) in
1619 Format.fprintf fmt
"#ifndef %s@\n@." n
1621 Format.fprintf fmt
"@\n#define %s@\n#endif // %s@." n n
1623 let print_header_decl args fmt
d =
1624 let cds = MLToC.translate_decl args d ~header
:true ~flat
:false in
1625 List.iter
(Format.fprintf fmt
"%a@." Print.print_global_def) cds
1627 let mk_info (args:Pdriver.printer_args
) m
= {
1628 syntax
= args.Pdriver.syntax
;
1629 literal
= args.Pdriver.literal
;
1630 prec
= args.Pdriver.prec
;
1631 kn
= m
.Pmodule.mod_known
}
1633 let print_header_decl =
1634 let memo = Hashtbl.create
16 in
1635 fun args ?old ?fname m fmt
d ->
1638 if not
(Hashtbl.mem
memo d)
1640 Hashtbl.add
memo d ();
1641 let info = mk_info args m
in
1642 print_header_decl info fmt
d end
1644 let print_prelude ~header
args ?old ?fname ~flat ~deps
1645 ~global_prelude ~
prelude fmt pm
=
1649 Print.init_printers args.Pdriver.blacklist
;
1651 let id = m
.Pmodule.mod_theory
.Theory.th_name
in
1652 Format.fprintf fmt
"%a@." Print.print_global_def C.(Dinclude
(id,Proj
)) in
1653 (* system includes *)
1654 Printer.print_prelude fmt global_prelude
;
1658 List.iter
add_include (List.rev deps
)
1660 (* C files include only their own header *)
1662 (* module and exported prelude *)
1663 Printer.print_prelude fmt
prelude
1665 let print_decl args fmt
d ~flat
=
1666 let cds = MLToC.translate_decl args d ~header
:false ~flat
in
1667 let cds = Transform.defs cds in
1669 Format.fprintf fmt
"%a@." Print.print_global_def d in
1670 List.iter
print_def cds
1672 let print_decl ~flat
=
1673 let memo = Hashtbl.create
16 in
1674 fun args ?old ?fname m fmt
d ->
1678 Print.init_printers args.Pdriver.blacklist
;
1679 if not
(Hashtbl.mem
memo d)
1681 Hashtbl.add
memo d ();
1682 let info = mk_info args m
in
1683 print_decl info fmt
d ~flat
end
1685 let c_printer = Pdriver.{
1686 desc
= "printer for C code";
1688 filename_generator
= name_gen ".c";
1689 decl_printer
= print_decl ~flat
:false;
1690 prelude_printer
= print_prelude ~flat
:false ~header
:false;
1691 header_printer
= dummy_border_printer
;
1692 footer_printer
= dummy_border_printer
;
1694 interf_printer
= Some
{
1695 filename_generator
= name_gen ".h";
1696 decl_printer
= print_header_decl;
1697 prelude_printer
= print_prelude ~flat
:false ~header
:true;
1698 header_printer
= header_border_printer true;
1699 footer_printer
= header_border_printer false;
1702 filename_generator
= name_gen ".c";
1703 decl_printer
= print_decl ~flat
:true;
1704 prelude_printer
= print_prelude ~flat
:true ~header
:false;
1705 header_printer
= dummy_border_printer
;
1706 footer_printer
= dummy_border_printer
;
1711 Pdriver.register_printer
"c" c_printer
1715 compile-command: "unset LANG; make -C ../.."