Newer
Older
(**************************************************************************)
(* *)
(* This file is part of WP plug-in of Frama-C. *)
(* *)
(* CEA (Commissariat a l'energie atomique et aux energies *)
(* alternatives) *)
(* *)
(* you can redistribute it and/or modify it under the terms of the GNU *)
(* Lesser General Public License as published by the Free Software *)
(* Foundation, version 2.1. *)
(* *)
(* It is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU Lesser General Public License for more details. *)
(* *)
(* See the GNU Lesser General Public License version 2.1 *)
(* for more details (enclosed in the file licenses/LGPLv2.1). *)
(* *)
(**************************************************************************)
(* -------------------------------------------------------------------------- *)
(* --- Floats Arithmetic Model --- *)
(* -------------------------------------------------------------------------- *)
open Ctypes
open Qed
open Lang
open Lang.F
(* -------------------------------------------------------------------------- *)
(* --- Library --- *)
(* -------------------------------------------------------------------------- *)
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)
(* Qed exact representations, linked to f32/f64 *)
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
(* -------------------------------------------------------------------------- *)
(* --- Model Setting --- *)
(* -------------------------------------------------------------------------- *)
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
| MUL
| DIV
| REAL
| ROUND
| EXACT
[@@@ warning "-32"]
let op_name = function
| LT -> "lt"
| EQ -> "eq"
| LE -> "le"
| NE -> "ne"
| NEG -> "neg"
| ADD -> "add"
| MUL -> "mul"
| DIV -> "div"
| REAL -> "of"
| ROUND -> "to"
| EXACT -> "exact"
[@@@ warning "+32"]
(* -------------------------------------------------------------------------- *)
(* --- Registry --- *)
(* -------------------------------------------------------------------------- *)
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
(* -------------------------------------------------------------------------- *)
(* --- Literals --- *)
(* -------------------------------------------------------------------------- *)
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]

Andre Maroneze
committed
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 ^ "$")
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
Warning.error "Unexpected constant literal %S" r
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)
(* -------------------------------------------------------------------------- *)
(* --- Literal Output --- *)
(* -------------------------------------------------------------------------- *)
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]+\\)?$"
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

Andre Maroneze
committed
| 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
(* -------------------------------------------------------------------------- *)
(* --- Finites --- *)
(* -------------------------------------------------------------------------- *)
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)
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 (* let Why3 take care of the division*)
in qmake ulp res
| 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
| 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

Andre Maroneze
committed
let compare = Stdlib.compare
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
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 = Pretty_utils.sfprintf "%s_%a" name pp_suffix ft in
let bool = Pretty_utils.sfprintf "%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
(* -------------------------------------------------------------------------- *)
(* --- Builtins --- *)
(* -------------------------------------------------------------------------- *)
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 ;
] ;
let () = Context.register
register_builtins Float32 ;
register_builtins Float64 ;
(* -------------------------------------------------------------------------- *)
(* -------------------------------------------------------------------------- *)
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
| Float32 -> "sqrtf"
| Float64 -> "sqrt"
LogicBuiltins.hack name choose
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) ;
in
register_builtin Float32 ;
register_builtin Float64
(* -------------------------------------------------------------------------- *)
(* --- Conversion Symbols --- *)
(* -------------------------------------------------------------------------- *)
let real_of_float f a =
match Context.get model with
| Real -> a
| Float -> e_fun ~result:Logic.Real (real_of_flt f) [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)
(* -------------------------------------------------------------------------- *)
(* --- Float Arithmetics --- *)
(* -------------------------------------------------------------------------- *)
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 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
(* -------------------------------------------------------------------------- *)
(* --- Registry --- *)
(* -------------------------------------------------------------------------- *)
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
(* -------------------------------------------------------------------------- *)