|
|
|
(*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*)
|
|
|
|
|
|
|
|
open Fol
|
|
|
|
module T = Term
|
|
|
|
module F = Formula
|
|
|
|
|
|
|
|
let lookup_func lookup term =
|
|
|
|
match Term.get_trm term with
|
|
|
|
| Some (Apply (Uninterp name, [||])) -> lookup name
|
|
|
|
| _ -> None
|
|
|
|
|
|
|
|
let uconst name = T.apply (Funsym.uninterp name) [||]
|
|
|
|
let global g = uconst (Llair.Global.name g)
|
|
|
|
let reg r = Var.identified ~name:(Llair.Reg.name r) ~id:(Llair.Reg.id r)
|
|
|
|
let regs rs = Var.Set.of_iter (Iter.map ~f:reg (Llair.Reg.Set.to_iter rs))
|
|
|
|
let uap0 f = T.apply f [||]
|
|
|
|
let uap1 f a = T.apply f [|a|]
|
|
|
|
let uap2 f a b = T.apply f [|a; b|]
|
|
|
|
let lit2 p a b = F.lit p [|a; b|]
|
|
|
|
let nlit2 p a b = F.not_ (lit2 p a b)
|
|
|
|
|
|
|
|
let rec ap_ttt : 'a. (T.t -> T.t -> 'a) -> _ -> _ -> 'a =
|
|
|
|
fun f a b -> f (term a) (term b)
|
|
|
|
and ap_ttf (f : T.t -> T.t -> F.t) a b = F.inject (ap_ttt f a b)
|
|
|
|
|
|
|
|
and ap_fff (f : F.t -> F.t -> F.t) a b =
|
|
|
|
F.inject (f (formula a) (formula b))
|
|
|
|
|
|
|
|
and ap_uut : 'a. (T.t -> T.t -> 'a) -> _ -> _ -> _ -> 'a =
|
|
|
|
fun f typ a b ->
|
|
|
|
let bits = Llair.Typ.bit_size_of typ in
|
|
|
|
let unsigned x = uap1 (Unsigned bits) x in
|
|
|
|
f (unsigned (term a)) (unsigned (term b))
|
|
|
|
|
|
|
|
and ap_uuf (f : T.t -> T.t -> F.t) typ a b = F.inject (ap_uut f typ a b)
|
|
|
|
|
|
|
|
and term : Llair.Exp.t -> T.t =
|
|
|
|
fun e ->
|
|
|
|
let imp p q = F.or_ (F.not_ p) q in
|
|
|
|
let nimp p q = F.and_ p (F.not_ q) in
|
|
|
|
let if_ p q = F.or_ p (F.not_ q) in
|
|
|
|
let nif p q = F.and_ (F.not_ p) q in
|
|
|
|
match e with
|
|
|
|
(* formulas *)
|
|
|
|
| Ap2 (Eq, Integer {bits= 1; _}, p, q) -> ap_fff F.iff p q
|
|
|
|
| Ap2 (Dq, Integer {bits= 1; _}, p, q) -> ap_fff F.xor p q
|
|
|
|
| Ap2 ((Gt | Ugt), Integer {bits= 1; _}, p, q) -> ap_fff nimp p q
|
|
|
|
| Ap2 ((Lt | Ult), Integer {bits= 1; _}, p, q) -> ap_fff nif p q
|
|
|
|
| Ap2 ((Ge | Uge), Integer {bits= 1; _}, p, q) -> ap_fff if_ p q
|
|
|
|
| Ap2 ((Le | Ule), Integer {bits= 1; _}, p, q) -> ap_fff imp p q
|
|
|
|
| Ap2 (Add, Integer {bits= 1; _}, p, q) -> ap_fff F.xor p q
|
|
|
|
| Ap2 (Sub, Integer {bits= 1; _}, p, q) -> ap_fff F.xor p q
|
|
|
|
| Ap2 (Mul, Integer {bits= 1; _}, p, q) -> ap_fff F.and_ p q
|
|
|
|
(* div and rem are not formulas even if bits=1 due to division by 0 *)
|
|
|
|
| Ap2 (And, Integer {bits= 1; _}, p, q) -> ap_fff F.and_ p q
|
|
|
|
| Ap2 (Or, Integer {bits= 1; _}, p, q) -> ap_fff F.or_ p q
|
|
|
|
| Ap2 (Xor, Integer {bits= 1; _}, p, q) -> ap_fff F.xor p q
|
|
|
|
| Ap2 ((Shl | Lshr), Integer {bits= 1; _}, p, q) -> ap_fff nimp p q
|
|
|
|
| Ap2 (Ashr, Integer {bits= 1; _}, p, q) -> ap_fff F.or_ p q
|
|
|
|
| Ap3 (Conditional, Integer {bits= 1; _}, cnd, pos, neg) ->
|
|
|
|
F.inject
|
|
|
|
(F.cond ~cnd:(formula cnd) ~pos:(formula pos) ~neg:(formula neg))
|
|
|
|
(* terms *)
|
|
|
|
| Reg {name; id; typ= _} -> T.var (Var.identified ~name ~id)
|
|
|
|
| Global {name; typ= _} | Function {name; typ= _} -> uconst name
|
|
|
|
| Label {parent; name} ->
|
|
|
|
uap0 (Funsym.uninterp ("label_" ^ parent ^ "_" ^ name))
|
|
|
|
| Integer {typ= _; data} -> T.integer data
|
|
|
|
| Float {data; typ= _} -> (
|
|
|
|
match Q.of_float (Float.of_string_exn data) with
|
|
|
|
| q when Q.is_real q -> T.rational q
|
|
|
|
| _ | (exception Invalid_argument _) ->
|
|
|
|
uap0 (Funsym.uninterp ("float_" ^ data)) )
|
|
|
|
| Ap1 (Signed {bits}, _, e) ->
|
|
|
|
let a = term e in
|
|
|
|
if bits = 1 then
|
|
|
|
match F.project a with
|
|
|
|
| Some fml -> F.inject fml
|
|
|
|
| _ -> uap1 (Signed bits) a
|
|
|
|
else uap1 (Signed bits) a
|
|
|
|
| Ap1 (Unsigned {bits}, _, e) ->
|
|
|
|
let a = term e in
|
|
|
|
if bits = 1 then
|
|
|
|
match F.project a with
|
|
|
|
| Some fml -> F.inject fml
|
|
|
|
| _ -> uap1 (Unsigned bits) a
|
|
|
|
else uap1 (Unsigned bits) a
|
|
|
|
| Ap1 (Convert {src}, dst, e) when Llair.Typ.equivalent src dst -> term e
|
|
|
|
| Ap1 (Convert {src= Float _}, Float _, e) -> term e
|
|
|
|
| Ap1 (Convert {src}, dst, e) ->
|
|
|
|
let s =
|
|
|
|
Format.asprintf "convert_%a_of_%a" Llair.Typ.pp dst Llair.Typ.pp src
|
|
|
|
in
|
|
|
|
uap1 (Funsym.uninterp s) (term e)
|
|
|
|
| Ap2 (Eq, _, d, e) -> ap_ttf F.eq d e
|
|
|
|
| Ap2 (Dq, _, d, e) -> ap_ttf F.dq d e
|
|
|
|
| Ap2 (Gt, _, d, e) -> ap_ttf F.gt d e
|
|
|
|
| Ap2 (Lt, _, d, e) -> ap_ttf F.lt d e
|
|
|
|
| Ap2 (Ge, _, d, e) -> ap_ttf F.ge d e
|
|
|
|
| Ap2 (Le, _, d, e) -> ap_ttf F.le d e
|
|
|
|
| Ap2 (Ugt, typ, d, e) -> ap_uuf F.gt typ d e
|
|
|
|
| Ap2 (Ult, typ, d, e) -> ap_uuf F.lt typ d e
|
|
|
|
| Ap2 (Uge, typ, d, e) -> ap_uuf F.ge typ d e
|
|
|
|
| Ap2 (Ule, typ, d, e) -> ap_uuf F.le typ d e
|
|
|
|
| Ap2 (Ord, _, d, e) -> ap_ttf (lit2 (Predsym.uninterp "ord")) d e
|
|
|
|
| Ap2 (Uno, _, d, e) -> ap_ttf (nlit2 (Predsym.uninterp "ord")) d e
|
|
|
|
| Ap2 (Add, _, d, e) -> ap_ttt T.add d e
|
|
|
|
| Ap2 (Sub, _, d, e) -> ap_ttt T.sub d e
|
|
|
|
| Ap2 (Mul, _, d, e) -> ap_ttt T.mul d e
|
|
|
|
| Ap2 (Div, _, d, e) -> ap_ttt T.div d e
|
|
|
|
| Ap2 (Rem, _, d, e) -> ap_ttt (uap2 Rem) d e
|
|
|
|
| Ap2 (Udiv, typ, d, e) -> ap_uut T.div typ d e
|
|
|
|
| Ap2 (Urem, typ, d, e) -> ap_uut (uap2 Rem) typ d e
|
|
|
|
| Ap2 (And, _, d, e) -> ap_ttt (uap2 BitAnd) d e
|
|
|
|
| Ap2 (Or, _, d, e) -> ap_ttt (uap2 BitOr) d e
|
|
|
|
| Ap2 (Xor, _, d, e) -> ap_ttt (uap2 BitXor) d e
|
|
|
|
| Ap2 (Shl, _, d, e) -> ap_ttt (uap2 BitShl) d e
|
|
|
|
| Ap2 (Lshr, _, d, e) -> ap_ttt (uap2 BitLshr) d e
|
|
|
|
| Ap2 (Ashr, _, d, e) -> ap_ttt (uap2 BitAshr) d e
|
|
|
|
| Ap3 (Conditional, _, cnd, thn, els) ->
|
|
|
|
T.ite ~cnd:(formula cnd) ~thn:(term thn) ~els:(term els)
|
[sledge] Remove record theory from backend, encode using sequence theory
Summary:
LLVM and Llair use a form of records, in particular for values of
constant structs and arrays. In Llair, these use standard `select` and
`update` operations a la McCarthy's theory of functional arrays, with
a compact `record` operation for constructing complete records. This
is fine and logically well-understood. The issue is that once
constructed, these values are accessed using instructions that (may)
operate over byte-ranges, rather than struct member indices. The
backend uses a theory of sequences to represent such values (the
contents of memory). So some code depends on high fidelity
interoperation between these two views.
This diff resolves this by removing the record theory from the backend
and instead encoding them using the sequence theory. The approach
taken keeps records in Llair and translates them to sequences in
Llair_to_Fol. This choice is made since the encoding into the sequence
theory involves terms that do not have types that are expressible in
terms of the source types. In particular, `(update r i e)`, is encoded
as the concatenation of the prefix of `r` up to the offset of index
`i`, followed by `e` (possibly with padding), and then the suffix of
`r` from index `i+1` on. The prefix and suffix sequences do not
necessarily have source-expressible types.
Reviewed By: jvillard
Differential Revision: D24800866
fbshipit-source-id: e7238c558
4 years ago
|
|
|
| Ap1 (Select idx, typ, rcd) ->
|
|
|
|
let off, len = Llair.Typ.offset_length_of_elt typ idx in
|
|
|
|
let off = T.integer (Z.of_int off) in
|
|
|
|
let len = T.integer (Z.of_int len) in
|
|
|
|
T.extract ~seq:(term rcd) ~off ~len
|
|
|
|
| Ap2 (Update idx, typ, rcd, elt) ->
|
|
|
|
let oI, lI = Llair.Typ.offset_length_of_elt typ idx in
|
|
|
|
let oJ = oI + lI in
|
|
|
|
let off0 = T.zero in
|
|
|
|
let len0 = T.integer (Z.of_int oI) in
|
|
|
|
let len1 = T.integer (Z.of_int lI) in
|
|
|
|
let off2 = T.integer (Z.of_int oJ) in
|
|
|
|
let len2 = T.integer (Z.of_int (Llair.Typ.size_of typ - oI - lI)) in
|
|
|
|
let seq = term rcd in
|
|
|
|
T.concat
|
|
|
|
[| T.extract ~seq ~off:off0 ~len:len0
|
|
|
|
; T.sized ~seq:(term elt) ~siz:len1
|
|
|
|
; T.extract ~seq ~off:off2 ~len:len2 |]
|
|
|
|
| ApN (Record, typ, elts) ->
|
|
|
|
let elt_siz i =
|
|
|
|
T.integer (Z.of_int (snd (Llair.Typ.offset_length_of_elt typ i)))
|
|
|
|
in
|
|
|
|
T.concat
|
|
|
|
(Array.mapi (IArray.to_array elts) ~f:(fun i elt ->
|
|
|
|
T.sized ~seq:(term elt) ~siz:(elt_siz i) ))
|
|
|
|
| Ap1 (Splat, _, byt) -> T.splat (term byt)
|
|
|
|
|
|
|
|
and formula e = F.dq0 (term e)
|