-
Loïc Correnson authoredLoïc Correnson authored
Cfloat.ml 12.30 KiB
(**************************************************************************)
(* *)
(* This file is part of WP plug-in of Frama-C. *)
(* *)
(* Copyright (C) 2007-2019 *)
(* 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)
let link phi = Lang.infoprover (Qed.Engine.F_call phi)
(* 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
(* -------------------------------------------------------------------------- *)
(* --- 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
(* -------------------------------------------------------------------------- *)
(* --- Operators --- *)
(* -------------------------------------------------------------------------- *)
type op =
| LT
| EQ
| LE
| NE
| NEG
| ADD
| MUL
| DIV
| REAL
| ROUND
| EXACT
[@@@ warning "-32"]
let op_name = function
| LT -> "flt"
| EQ -> "feq"
| LE -> "fle"
| NE -> "fne"
| NEG -> "fneg"
| ADD -> "fadd"
| MUL -> "fmul"
| DIV -> "fdiv"
| REAL -> "freal"
| ROUND -> "fround"
| EXACT -> "fexact"
[@@@ warning "+32"]
(* -------------------------------------------------------------------------- *)
(* --- Registry --- *)
(* -------------------------------------------------------------------------- *)
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
(* -------------------------------------------------------------------------- *)
(* --- Literals --- *)
(* -------------------------------------------------------------------------- *)
let rfloat = Floating_point.round_to_single_precision_float
let fmake ulp value = match ulp with
| Float32 -> F.e_fun fq32 [F.e_float (rfloat value)]
| Float64 -> F.e_fun fq64 [F.e_float value]
let qmake ulp q = fmake ulp (Transitioning.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 fq32 [F.e_float value]
| Float , Float64 , _ -> F.e_fun fq64 [F.e_float value]
| Real , _ , None -> F.e_float value
| Real , _ , Some r -> F.e_real (parse_literal ~model:Real value r)
(* -------------------------------------------------------------------------- *)
(* --- Computations --- *)
(* -------------------------------------------------------------------------- *)
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 compute 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))
| MUL , [ x ; y ] -> qmake ulp (Q.mul (exact x) (exact y))
| DIV , [ x ; y ] -> qmake ulp (Q.div (exact x) (exact y))
| ROUND , [ x ] -> qmake ulp (exact 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
(* -------------------------------------------------------------------------- *)
(* --- Operations --- *)
(* -------------------------------------------------------------------------- *)
let make_fun_float ?result name op ft =
let result = match result with None -> ftau ft | Some r -> r in
let phi = extern_f ~library ~result "%s_%a" name pp_suffix ft in
Lang.F.set_builtin phi (compute op ft) ;
REGISTRY.define phi (op,ft) ; phi
let make_pred_float name op ft =
let prop = Pretty_utils.sfprintf "%s_%a" name pp_suffix ft in
let bool = Pretty_utils.sfprintf "%s_%ab" name pp_suffix ft in
let phi = extern_p ~library ~bool ~prop () in
Lang.F.set_builtin phi (compute op ft) ;
REGISTRY.define phi (op,ft) ; phi
let f_memo = Ctypes.f_memo
let real_of_flt = f_memo (make_fun_float ~result:Logic.Real "of" REAL)
let flt_of_real = f_memo (make_fun_float "to" ROUND)
let flt_add = f_memo (make_fun_float "add" ADD)
let flt_mul = f_memo (make_fun_float "mul" MUL)
let flt_div = f_memo (make_fun_float "div" DIV)
let flt_neg = f_memo (make_fun_float "neg" NEG)
let flt_lt = f_memo (make_pred_float "lt" LT)
let flt_eq = f_memo (make_pred_float "eq" EQ)
let flt_le = f_memo (make_pred_float "le" LE)
let flt_neq = f_memo (make_pred_float "ne" NE)
(* -------------------------------------------------------------------------- *)
(* --- Builtins --- *)
(* -------------------------------------------------------------------------- *)
let register_builtin_comparison suffix ft =
begin
let open Qed.Logic in
let params = [Sdata;Sdata] in
let sort = Sprop in
let gt = generated_f ~params ~sort "\\gt_%s" suffix in
let ge = generated_f ~params ~sort "\\ge_%s" suffix in
let open LogicBuiltins in
let signature = [F ft;F ft] in
add_builtin ("\\eq_" ^ suffix) signature (flt_eq ft) ;
add_builtin ("\\ne_" ^ suffix) signature (flt_neq ft) ;
add_builtin ("\\lt_" ^ suffix) signature (flt_lt ft) ;
add_builtin ("\\le_" ^ suffix) signature (flt_le ft) ;
add_builtin ("\\gt_" ^ suffix) signature gt ;
add_builtin ("\\ge_" ^ suffix) signature ge ;
Context.register
begin fun () ->
let converse phi x y = e_fun phi [y;x] in
Lang.F.set_builtin_2 gt (converse (flt_lt ft)) ;
Lang.F.set_builtin_2 ge (converse (flt_le ft)) ;
end
end
let () =
begin
register_builtin_comparison "float" Float32 ;
register_builtin_comparison "double" Float64 ;
end
(* -------------------------------------------------------------------------- *)
(* --- Models --- *)
(* -------------------------------------------------------------------------- *)
let () =
begin
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 ;
end
(* -------------------------------------------------------------------------- *)
(* --- Conversion Symbols --- *)
(* -------------------------------------------------------------------------- *)
let real_of_float f a =
match Context.get model with
| Real -> a
| Float -> e_fun (real_of_flt f) [a]
let float_of_real f a =
match Context.get model with
| Real -> a
| Float -> e_fun (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 (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 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 (flt_neg f) [x]
let fsub f x y = fadd f x (fopp f y)
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 configure m =
begin
Context.set model m ;
Context.set Lang.floats tau_of_float ;
end
(* -------------------------------------------------------------------------- *)