1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
open Ctypes
open Qed
open Lang
open Lang.F
let library = "cfloat"
let f32 = datatype ~library "f32"
let f64 = datatype ~library "f64"
let t32 = Lang.(t_datatype f32 [])
let t64 = Lang.(t_datatype f64 [])
let ftau = function
| Float32 -> t32
| Float64 -> t64
let ft_suffix = function Float32 -> "f32" | Float64 -> "f64"
let pp_suffix fmt ft = Format.pp_print_string fmt (ft_suffix ft)
let link phi = Qed.Engine.F_call phi
let fq32 = extern_f ~library ~result:t32 ~link:(link "to_f32") "q32"
let fq64 = extern_f ~library ~result:t64 ~link:(link "to_f64") "q64"
let f_model ft = extern_f ~library ~result:(ftau ft) "model_%a" pp_suffix ft
let f_delta ft = extern_f ~library ~result:(ftau ft) "delta_%a" pp_suffix ft
let f_epsilon ft = extern_f ~library ~result:(ftau ft) "epsilon_%a" pp_suffix ft
let f_sqrt ft = extern_f ~library ~result:(ftau ft) "sqrt_%a" pp_suffix ft
type model = Real | Float
let model = Context.create ~default:Float "Cfloat.model"
let tau_of_float f =
match Context.get model with
| Real -> Logic.Real
| Float -> ftau f
let float_name = function
| Float32 -> "float"
| Float64 -> "double"
let model_name = function
| Float -> "Float"
| Real -> "Real"
type op =
| LT
| EQ
| LE
| NE
| NEG
| ADD
| SUB
| MUL
| DIV
| REAL
| ROUND
| EXACT
[@@@ warning "-32"]
let op_name = function
| LT -> "lt"
| EQ -> "eq"
| LE -> "le"
| NE -> "ne"
| NEG -> "neg"
| ADD -> "add"
| SUB -> "sub"
| MUL -> "mul"
| DIV -> "div"
| REAL -> "of"
| ROUND -> "to"
| EXACT -> "exact"
[@@@ warning "+32"]
module REGISTRY = WpContext.Static
(struct
type key = lfun
type data = op * c_float
let name = "Wp.Cfloat.REGISTRY"
include Lang.Fun
end)
let find = REGISTRY.find
let () = Context.register
begin fun () ->
REGISTRY.define fq32 (EXACT,Float32) ;
REGISTRY.define fq64 (EXACT,Float64) ;
end
let rfloat = Floating_point.round_to_single_precision_float
let fmake ulp value = match ulp with
| Float32 -> F.e_fun ~result:t32 fq32 [F.e_float (rfloat value)]
| Float64 -> F.e_fun ~result:t64 fq64 [F.e_float value]
let qmake ulp q = fmake ulp (Q.to_float q)
let re_mantissa = "\\([-+]?[0-9]*\\)"
let re_comma = "\\(.\\(\\(0*[1-9]\\)*\\)0*\\)?"
let re_exponent = "\\([eE]\\([-+]?[0-9]*\\)\\)?"
let re_suffix = "\\([flFL]\\)?"
let re_real =
Str.regexp (re_mantissa ^ re_comma ^ re_exponent ^ re_suffix ^ "$")
let parse_literal ~model v r =
try
if Str.string_match re_real r 0 then
let has_suffix =
try ignore (Str.matched_group 7 r) ; true
with Not_found -> false in
if has_suffix && model = Float then
Q.of_float v
else
let ma = Str.matched_group 1 r in
let mb = try Str.matched_group 3 r with Not_found -> "" in
let me = try Str.matched_group 6 r with Not_found -> "0" in
let n = int_of_string me - String.length mb in
let d n =
let s = Bytes.make (succ n) '0' in
Bytes.set s 0 '1' ; Q.of_string (Bytes.to_string s) in
let m = Q.of_string (ma ^ mb) in
if n < 0 then Q.div m (d (-n)) else
if n > 0 then Q.mul m (d n) else m
else Q.of_float v
with Failure _ ->
Warning.error "Unexpected constant literal %S" r
let acsl_lit l =
let open Cil_types in
F.e_real (parse_literal ~model:(Context.get model) l.r_nearest l.r_literal)
let code_lit ulp value original =
match Context.get model , ulp , original with
| Float , Float32 , _ -> F.e_fun ~result:t32 fq32 [F.e_float value]
| Float , Float64 , _ -> F.e_fun ~result:t64 fq64 [F.e_float value]
| Real , _ , None -> F.e_float value
| Real , _ , Some r -> F.e_real (parse_literal ~model:Real value r)
let printers = [
Printf.sprintf "%.0g" ;
Printf.sprintf "%.1g" ;
Printf.sprintf "%.2g" ;
Printf.sprintf "%.3g" ;
Printf.sprintf "%.4g" ;
Printf.sprintf "%.5g" ;
Printf.sprintf "%.6g" ;
Printf.sprintf "%.9g" ;
Printf.sprintf "%.12g" ;
Printf.sprintf "%.15g" ;
Printf.sprintf "%.18g" ;
Printf.sprintf "%.21g" ;
Printf.sprintf "%.32g" ;
Printf.sprintf "%.64g" ;
]
let re_int_float = Str.regexp "\\(-?[0-9]+\\)\\(e[+-]?[0-9]+\\)?$"
let force_float r =
if Str.string_match re_int_float r 0
then
let group n r = try Str.matched_group n r with Not_found -> ""
in group 1 r ^ ".0" ^ group 2 r
else r
let float_lit ulp (q : Q.t) =
let v = match ulp with
| Float32 -> rfloat @@ Q.to_float q
| Float64 -> Q.to_float q in
let reparse ulp r =
match ulp with
| Float32 -> rfloat @@ float_of_string r
| Float64 -> float_of_string r
in
let rec lookup ulp v = function
| [] -> Pretty_utils.to_string Floating_point.pretty v
| pp::pps ->
let r = force_float @@ pp v in
if reparse ulp r = v then r else
lookup ulp v pps
in lookup ulp v printers
let fclass value _args =
match Context.get model with
| Real -> F.e_bool value
| Float -> raise Not_found
let () = Context.register
begin fun () ->
LogicBuiltins.hack "\\is_finite" (fclass true) ;
LogicBuiltins.hack "\\is_NaN" (fclass false) ;
LogicBuiltins.hack "\\is_infinite" (fclass false) ;
LogicBuiltins.hack "\\is_plus_infinity" (fclass false) ;
LogicBuiltins.hack "\\is_minus_infinity" (fclass false) ;
end
let rec exact e =
match F.repr e with
| Qed.Logic.Kreal r -> r
| Qed.Logic.Kint z -> Q.of_bigint z
| Qed.Logic.Fun( f , [ q ] ) when f == fq32 || f == fq64 -> exact q
| _ -> raise Not_found
let round ulp e =
match F.repr e with
| Qed.Logic.Fun( f , [ b ] ) ->
begin
match find f with
| REAL , ulp2 when ulp2 = ulp -> b
| _ -> qmake ulp (exact e )
end
| _ -> qmake ulp (exact e)
let compute_float op ulp xs =
match op , xs with
| NEG , [ x ] -> qmake ulp (Q.neg (exact x))
| ADD , [ x ; y ] -> qmake ulp (Q.add (exact x) (exact y))
| SUB , [ x ; y ] -> qmake ulp (Q.sub (exact x) (exact y))
| MUL , [ x ; y ] -> qmake ulp (Q.mul (exact x) (exact y))
| DIV , [ x ; y ] ->
let res = match Q.div (exact x) (exact y) with
| x when Q.classify x = Q.NZERO -> x
| _ -> raise Not_found
in qmake ulp res
| ROUND , [ x ] -> round ulp x
| REAL , [ x ] -> F.e_real (exact x)
| LE , [ x ; y ] -> F.e_bool (Q.leq (exact x) (exact y))
| LT , [ x ; y ] -> F.e_bool (Q.lt (exact x) (exact y))
| EQ , [ x ; y ] -> F.e_bool (Q.equal (exact x) (exact y))
| NE , [ x ; y ] -> F.e_bool (not (Q.equal (exact x) (exact y)))
| _ -> raise Not_found
let compute_real op xs =
match op , xs with
| NEG , [ x ] -> F.e_opp x
| ADD , [ x ; y ] -> F.e_add x y
| SUB , [ x ; y ] -> F.e_sub x y
| MUL , [ x ; y ] -> F.e_mul x y
| DIV , [ x ; y ] -> F.e_div x y
| (ROUND|REAL) , [ x ] -> x
| LE , [ x ; y ] -> F.e_leq x y
| LT , [ x ; y ] -> F.e_lt x y
| EQ , [ x ; y ] -> F.e_eq x y
| NE , [ x ; y ] -> F.e_neq x y
| _ -> raise Not_found
let return_type ft = function
| REAL -> Logic.Real
| _ -> ftau ft
module Compute = WpContext.StaticGenerator
(struct
type t = model * c_float * op
let compare = Stdlib.compare
let pretty fmt (m, ft, op) =
Format.fprintf fmt "%s_%a_%s" (model_name m) pp_suffix ft (op_name op)
end)
(struct
let name = "Wp.Cfloat.Compute"
type key = model * c_float * op
type data = lfun * (term list -> term)
let compile (m, ft, op) =
let impl = match m with
| Real -> compute_real op
| Float -> compute_float op ft
in
let name = op_name op in
let phi = match op with
| LT | EQ | LE | NE ->
let prop = Format.asprintf "%s_%a" name pp_suffix ft in
let bool = Format.asprintf "%s_%ab" name pp_suffix ft in
extern_p ~library ~bool ~prop ()
| _ ->
let result = return_type ft op in
extern_f ~library ~result "%s_%a" name pp_suffix ft
in
Lang.F.set_builtin phi impl ;
REGISTRY.define phi (op, ft) ;
(phi, impl)
end)
let flt_eq ft = Compute.get (Context.get model, ft, EQ) |> fst
let flt_neq ft = Compute.get (Context.get model, ft, NE) |> fst
let flt_le ft = Compute.get (Context.get model, ft, LE) |> fst
let flt_lt ft = Compute.get (Context.get model, ft, LT) |> fst
let flt_neg ft = Compute.get (Context.get model, ft, NEG) |> fst
let flt_add ft = Compute.get (Context.get model, ft, ADD) |> fst
let flt_sub ft = Compute.get (Context.get model, ft, SUB) |> fst
let flt_mul ft = Compute.get (Context.get model, ft, MUL) |> fst
let flt_div ft = Compute.get (Context.get model, ft, DIV) |> fst
let flt_of_real ft = Compute.get (Context.get model, ft, ROUND) |> fst
let real_of_flt ft = Compute.get (Context.get model, ft, REAL) |> fst
let builtin kind ft op xs =
let phi, impl = Compute.get ((Context.get model), ft, op) in
let xs = (if kind=`ReV then List.rev xs else xs) in
try impl xs with Not_found ->
let result = match kind with
| `Binop | `Unop -> ftau ft
| `Rel | `ReV -> Logic.Bool
in F.e_fun ~result phi xs
let register_builtins ft =
begin
let suffix = float_name ft in
let register (prefix,kind,op) =
LogicBuiltins.hack
(Printf.sprintf "\\%s_%s" prefix suffix)
(builtin kind ft op)
in List.iter register [
"eq",`Rel,EQ ;
"ne",`Rel,NE ;
"lt",`Rel,LT ;
"gt",`ReV,LT ;
"le",`Rel,LE ;
"ge",`ReV,LE ;
"neg",`Unop,NEG ;
"add",`Binop,ADD ;
"sub",`Binop,SUB ;
"mul",`Binop,MUL ;
"div",`Binop,DIV ;
] ;
end
let () = Context.register
begin fun () ->
register_builtins Float32 ;
register_builtins Float64 ;
end
let hack_sqrt_builtin ft =
let choose xs =
match Context.get model with
| Real -> F.e_fun ~result:t_real Cmath.f_sqrt xs
| Float -> F.e_fun ~result:(ftau ft) (f_sqrt ft) xs
in
let name = match ft with
| Float32 -> "sqrtf"
| Float64 -> "sqrt"
in
LogicBuiltins.hack name choose
let () =
let open LogicBuiltins in
let register_builtin ft =
add_builtin "\\model" [F ft] (f_model ft) ;
add_builtin "\\delta" [F ft] (f_delta ft) ;
add_builtin "\\epsilon" [F ft] (f_epsilon ft) ;
hack_sqrt_builtin ft
in
register_builtin Float32 ;
register_builtin Float64
let real_of_float f a =
match Context.get model with
| Real -> a
| Float -> e_fun ~result:Logic.Real (real_of_flt f) [a]
let float_of_real f a =
match Context.get model with
| Real -> a
| Float -> e_fun ~result:(ftau f) (flt_of_real f) [a]
let float_of_int f a = float_of_real f (Cmath.real_of_int a)
let fbinop rop fop f x y =
match Context.get model with
| Real -> rop x y
| Float -> e_fun ~result:(ftau f) (fop f) [x;y]
let fcmp rop fop f x y =
match Context.get model with
| Real -> rop x y
| Float -> p_call (fop f) [x;y]
let fadd = fbinop e_add flt_add
let fsub = fbinop e_sub flt_sub
let fmul = fbinop e_mul flt_mul
let fdiv = fbinop e_div flt_div
let fopp f x =
match Context.get model with
| Real -> e_opp x
| Float -> e_fun ~result:(ftau f) (flt_neg f) [x]
let flt = fcmp p_lt flt_lt
let fle = fcmp p_leq flt_le
let feq = fcmp p_equal flt_eq
let fneq = fcmp p_neq flt_neq
let configure m =
begin
let orig_model = Context.push model m in
let orig_floats = Context.push Lang.floats tau_of_float in
let rollback () =
Context.pop model orig_model ;
Context.pop Lang.floats orig_floats
in
rollback
end