[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
master
Josh Berdine 4 years ago committed by Facebook GitHub Bot
parent aa307294a5
commit aacdbc060a

@ -24,7 +24,7 @@ let rec classify e =
| Uninterpreted -> Uninterpreted ) | Uninterpreted -> Uninterpreted )
| Sized _ | Extract _ | Concat _ -> Interpreted | Sized _ | Extract _ | Concat _ -> Interpreted
| Var _ | Z _ | Q _ -> Atomic | Var _ | Z _ | Q _ -> Atomic
| Splat _ | Select _ | Update _ | Record _ | Apply _ -> Uninterpreted | Splat _ | Apply _ -> Uninterpreted
let interpreted e = equal_kind (classify e) Interpreted let interpreted e = equal_kind (classify e) Interpreted
let non_interpreted e = not (interpreted e) let non_interpreted e = not (interpreted e)

@ -229,15 +229,6 @@ module Term = struct
let concat elts = apNt Trm.concat elts let concat elts = apNt Trm.concat elts
(* records *)
let select ~rcd ~idx = ap1t (fun rcd -> Trm.select ~rcd ~idx) rcd
let update ~rcd ~idx ~elt =
ap2t (fun rcd elt -> Trm.update ~rcd ~idx ~elt) rcd elt
let record elts = apNt Trm.record elts
(* uninterpreted *) (* uninterpreted *)
let apply sym args = apNt (Trm.apply sym) args let apply sym args = apNt (Trm.apply sym) args

@ -46,16 +46,6 @@ module rec Term : sig
val concat : t array -> t val concat : t array -> t
(** Concatenation of sequences *) (** Concatenation of sequences *)
(* records (with fixed indices) *)
val select : rcd:t -> idx:int -> t
(** Select an index from a record *)
val update : rcd:t -> idx:int -> elt:t -> t
(** Record updated with element at index *)
val record : t array -> t
(** Record constant *)
(* uninterpreted *) (* uninterpreted *)
val apply : Funsym.t -> t array -> t val apply : Funsym.t -> t array -> t

@ -82,10 +82,6 @@ and Trm : sig
| Sized of {seq: t; siz: t} | Sized of {seq: t; siz: t}
| Extract of {seq: t; off: t; len: t} | Extract of {seq: t; off: t; len: t}
| Concat of t array | Concat of t array
(* records (with fixed indices) *)
| Select of {idx: int; rcd: t}
| Update of {idx: int; rcd: t; elt: t}
| Record of t array
(* uninterpreted *) (* uninterpreted *)
| Apply of Funsym.t * t array | Apply of Funsym.t * t array
[@@deriving compare, equal, sexp] [@@deriving compare, equal, sexp]
@ -100,9 +96,6 @@ and Trm : sig
val _Sized : t -> t -> t val _Sized : t -> t -> t
val _Extract : t -> t -> t -> t val _Extract : t -> t -> t -> t
val _Concat : t array -> t val _Concat : t array -> t
val _Select : int -> t -> t
val _Update : int -> t -> t -> t
val _Record : t array -> t
val _Apply : Funsym.t -> t array -> t val _Apply : Funsym.t -> t array -> t
val add : t -> t -> t val add : t -> t -> t
val sub : t -> t -> t val sub : t -> t -> t
@ -121,9 +114,6 @@ end = struct
| Sized of {seq: t; siz: t} | Sized of {seq: t; siz: t}
| Extract of {seq: t; off: t; len: t} | Extract of {seq: t; off: t; len: t}
| Concat of t array | Concat of t array
| Select of {idx: int; rcd: t}
| Update of {idx: int; rcd: t; elt: t}
| Record of t array
| Apply of Funsym.t * t array | Apply of Funsym.t * t array
[@@deriving compare, equal, sexp] [@@deriving compare, equal, sexp]
@ -155,11 +145,15 @@ end = struct
| Sized {seq; siz} -> pf "@<1>⟨%a,%a@<1>⟩" pp siz pp seq | Sized {seq; siz} -> pf "@<1>⟨%a,%a@<1>⟩" pp siz pp seq
| Extract {seq; off; len} -> pf "%a[%a,%a)" pp seq pp off pp len | Extract {seq; off; len} -> pf "%a[%a,%a)" pp seq pp off pp len
| Concat [||] -> pf "@<2>⟨⟩" | Concat [||] -> pf "@<2>⟨⟩"
| Concat xs -> pf "(%a)" (Array.pp "@,^" pp) xs | Concat xs -> (
| Select {idx; rcd} -> pf "%a[%i]" pp rcd idx let exception Not_a_string in
| Update {idx; rcd; elt} -> try
pf "[%a@ @[| %i → %a@]]" pp rcd idx pp elt pf "%S"
| Record xs -> pf "{%a}" (ppx_record strength) xs (String.init (Array.length xs) ~f:(fun i ->
match xs.(i) with
| Sized {seq= Z c} -> Char.of_int_exn (Z.to_int c)
| _ -> raise_notrace Not_a_string ))
with _ -> pf "(%a)" (Array.pp "@,^" pp) xs )
| Apply (f, [||]) -> pf "%a" Funsym.pp f | Apply (f, [||]) -> pf "%a" Funsym.pp f
| Apply | Apply
( ( (Rem | BitAnd | BitOr | BitXor | BitShl | BitLshr | BitAshr) ( ( (Rem | BitAnd | BitOr | BitXor | BitShl | BitLshr | BitAshr)
@ -171,24 +165,6 @@ end = struct
in in
pp fs trm pp fs trm
and ppx_record strength fs elts =
[%Trace.fprintf
fs "%a"
(fun fs elts ->
let exception Not_a_string in
match
String.init (Array.length elts) ~f:(fun i ->
match elts.(i) with
| Z c -> Char.of_int_exn (Z.to_int c)
| _ -> raise_notrace Not_a_string )
with
| s -> Format.fprintf fs "%S" s
| exception (Not_a_string | Z.Overflow | Failure _) ->
Format.fprintf fs "@[<h>%a@]"
(Array.pp ",@ " (ppx strength))
elts )
elts]
let pp = ppx (fun _ -> None) let pp = ppx (fun _ -> None)
let invariant e = let invariant e =
@ -354,10 +330,6 @@ end = struct
let xs = Array.reduce_adjacent ~f:simp_adjacent xs in let xs = Array.reduce_adjacent ~f:simp_adjacent xs in
(if Array.length xs = 1 then xs.(0) else Concat xs) |> check invariant (if Array.length xs = 1 then xs.(0) else Concat xs) |> check invariant
let _Select idx rcd = Select {idx; rcd} |> check invariant
let _Update idx rcd elt = Update {idx; rcd; elt} |> check invariant
let _Record es = Record es |> check invariant
let _Apply f es = let _Apply f es =
( match Funsym.eval ~equal ~get_z ~ret_z:_Z ~get_q ~ret_q:_Q f es with ( match Funsym.eval ~equal ~get_z ~ret_z:_Z ~get_q ~ret_q:_Q f es with
| Some c -> c | Some c -> c
@ -370,16 +342,15 @@ end = struct
match e with match e with
| Var _ as v -> f (Var.of_ v) | Var _ as v -> f (Var.of_ v)
| Z _ | Q _ -> () | Z _ | Q _ -> ()
| Splat x | Select {rcd= x} -> iter_vars ~f x | Splat x -> iter_vars ~f x
| Sized {seq= x; siz= y} | Update {rcd= x; elt= y} -> | Sized {seq= x; siz= y} ->
iter_vars ~f x ; iter_vars ~f x ;
iter_vars ~f y iter_vars ~f y
| Extract {seq= x; off= y; len= z} -> | Extract {seq= x; off= y; len= z} ->
iter_vars ~f x ; iter_vars ~f x ;
iter_vars ~f y ; iter_vars ~f y ;
iter_vars ~f z iter_vars ~f z
| Concat xs | Record xs | Apply (_, xs) -> | Concat xs | Apply (_, xs) -> Array.iter ~f:(iter_vars ~f) xs
Array.iter ~f:(iter_vars ~f) xs
| Arith a -> Iter.iter ~f:(iter_vars ~f) (Arith.trms a) | Arith a -> Iter.iter ~f:(iter_vars ~f) (Arith.trms a)
let vars e = Iter.from_labelled_iter (iter_vars e) let vars e = Iter.from_labelled_iter (iter_vars e)
@ -428,12 +399,6 @@ let sized ~seq ~siz = _Sized seq siz
let extract ~seq ~off ~len = _Extract seq off len let extract ~seq ~off ~len = _Extract seq off len
let concat elts = _Concat elts let concat elts = _Concat elts
(* records *)
let select ~rcd ~idx = _Select idx rcd
let update ~rcd ~idx ~elt = _Update idx rcd elt
let record elts = _Record elts
(* uninterpreted *) (* uninterpreted *)
let apply sym args = _Apply sym args let apply sym args = _Apply sym args
@ -449,9 +414,6 @@ let rec map_vars e ~f =
| Sized {seq; siz} -> map2 (map_vars ~f) e _Sized seq siz | Sized {seq; siz} -> map2 (map_vars ~f) e _Sized seq siz
| Extract {seq; off; len} -> map3 (map_vars ~f) e _Extract seq off len | Extract {seq; off; len} -> map3 (map_vars ~f) e _Extract seq off len
| Concat xs -> mapN (map_vars ~f) e _Concat xs | Concat xs -> mapN (map_vars ~f) e _Concat xs
| Select {idx; rcd} -> map1 (map_vars ~f) e (_Select idx) rcd
| Update {idx; rcd; elt} -> map2 (map_vars ~f) e (_Update idx) rcd elt
| Record xs -> mapN (map_vars ~f) e _Record xs
| Apply (g, xs) -> mapN (map_vars ~f) e (_Apply g) xs | Apply (g, xs) -> mapN (map_vars ~f) e (_Apply g) xs
let map e ~f = let map e ~f =
@ -462,9 +424,6 @@ let map e ~f =
| Sized {seq; siz} -> map2 f e _Sized seq siz | Sized {seq; siz} -> map2 f e _Sized seq siz
| Extract {seq; off; len} -> map3 f e _Extract seq off len | Extract {seq; off; len} -> map3 f e _Extract seq off len
| Concat xs -> mapN f e _Concat xs | Concat xs -> mapN f e _Concat xs
| Select {idx; rcd} -> map1 f e (_Select idx) rcd
| Update {idx; rcd; elt} -> map2 f e (_Update idx) rcd elt
| Record xs -> mapN f e _Record xs
| Apply (g, xs) -> mapN f e (_Apply g) xs | Apply (g, xs) -> mapN f e (_Apply g) xs
(** Traverse *) (** Traverse *)
@ -473,15 +432,15 @@ let iter_subtrms e ~f =
match e with match e with
| Var _ | Z _ | Q _ -> () | Var _ | Z _ | Q _ -> ()
| Arith a -> Iter.iter ~f (Arith.trms a) | Arith a -> Iter.iter ~f (Arith.trms a)
| Splat x | Select {rcd= x} -> f x | Splat x -> f x
| Sized {seq= x; siz= y} | Update {rcd= x; elt= y} -> | Sized {seq= x; siz= y} ->
f x ; f x ;
f y f y
| Extract {seq= x; off= y; len= z} -> | Extract {seq= x; off= y; len= z} ->
f x ; f x ;
f y ; f y ;
f z f z
| Concat xs | Record xs | Apply (_, xs) -> Array.iter ~f xs | Concat xs | Apply (_, xs) -> Array.iter ~f xs
let subtrms e = Iter.from_labelled_iter (iter_subtrms e) let subtrms e = Iter.from_labelled_iter (iter_subtrms e)

@ -21,10 +21,6 @@ type t = private
| Sized of {seq: t; siz: t} | Sized of {seq: t; siz: t}
| Extract of {seq: t; off: t; len: t} | Extract of {seq: t; off: t; len: t}
| Concat of t array | Concat of t array
(* records (with fixed indices) *)
| Select of {idx: int; rcd: t}
| Update of {idx: int; rcd: t; elt: t}
| Record of t array
(* uninterpreted *) (* uninterpreted *)
| Apply of Funsym.t * t array | Apply of Funsym.t * t array
[@@deriving compare, equal, sexp] [@@deriving compare, equal, sexp]
@ -76,11 +72,6 @@ val sized : seq:t -> siz:t -> t
val extract : seq:t -> off:t -> len:t -> t val extract : seq:t -> off:t -> len:t -> t
val concat : t array -> t val concat : t array -> t
(* records (with fixed indices) *)
val select : rcd:t -> idx:int -> t
val update : rcd:t -> idx:int -> elt:t -> t
val record : t array -> t
(* uninterpreted *) (* uninterpreted *)
val apply : Funsym.t -> t array -> t val apply : Funsym.t -> t array -> t

@ -145,20 +145,14 @@ let rec pp fs exp =
[@@warning "-9"] [@@warning "-9"]
and pp_record fs elts = and pp_record fs elts =
[%Trace.fprintf
fs "%a"
(fun fs elts ->
match match
String.init (IArray.length elts) ~f:(fun i -> String.init (IArray.length elts) ~f:(fun i ->
match IArray.get elts i with match IArray.get elts i with
| Integer {data} -> Char.of_int_exn (Z.to_int data) | Integer {data; _} -> Char.of_int_exn (Z.to_int data)
| _ -> raise_notrace (Invalid_argument "not a string") ) | _ -> raise_notrace (Invalid_argument "not a string") )
with with
| s -> Format.fprintf fs "@[<h>%s@]" (String.escaped s) | s -> Format.fprintf fs "@[<h>%s@]" (String.escaped s)
| exception _ -> | exception _ -> Format.fprintf fs "@[<hv>%a@]" (IArray.pp ",@ " pp) elts
Format.fprintf fs "@[<h>%a@]" (IArray.pp ",@ " pp) elts )
elts]
[@@warning "-9"]
(** Invariant *) (** Invariant *)

@ -135,6 +135,20 @@ let size_of = function
byts byts
| Pointer _ -> 8 | Pointer _ -> 8
let offset_length_of_elt typ idx =
match typ with
| Array {elt} ->
let len = size_of elt in
(len * idx, len)
| Tuple {elts; byts} | Struct {elts; byts} ->
let oI, _ = IArray.get elts idx in
let oJ =
if idx = IArray.length elts - 1 then byts
else fst (IArray.get elts (idx + 1))
in
(oI, oJ - oI)
| _ -> fail "offset_length_of_elt: %a" pp typ ()
let rec equivalent t0 t1 = let rec equivalent t0 t1 =
match (t0, t1) with match (t0, t1) with
| (Pointer _ | Integer _), (Pointer _ | Integer _) -> | (Pointer _ | Integer _), (Pointer _ | Integer _) ->

@ -64,6 +64,11 @@ val size_of : t -> int
(** The number of bytes between adjacent values of the given type, including (** The number of bytes between adjacent values of the given type, including
alignment padding. Raises unless is_sized holds. *) alignment padding. Raises unless is_sized holds. *)
val offset_length_of_elt : t -> int -> int * int
(** The offset in bytes to, and number of bytes occupied by, the given
element of an aggretage type. Raises if type is not an aggregate or
index is invalid. *)
val equivalent : t -> t -> bool val equivalent : t -> t -> bool
(** Equivalent types are those that denote the same sets of values in the (** Equivalent types are those that denote the same sets of values in the
semantic model. An equivalence relation. *) semantic model. An equivalence relation. *)

@ -120,11 +120,31 @@ and term : Llair.Exp.t -> T.t =
| Ap2 (Ashr, _, d, e) -> ap_ttt (uap2 BitAshr) d e | Ap2 (Ashr, _, d, e) -> ap_ttt (uap2 BitAshr) d e
| Ap3 (Conditional, _, cnd, thn, els) -> | Ap3 (Conditional, _, cnd, thn, els) ->
T.ite ~cnd:(formula cnd) ~thn:(term thn) ~els:(term els) T.ite ~cnd:(formula cnd) ~thn:(term thn) ~els:(term els)
| Ap1 (Select idx, _, rcd) -> T.select ~rcd:(term rcd) ~idx | Ap1 (Select idx, typ, rcd) ->
| Ap2 (Update idx, _, rcd, elt) -> let off, len = Llair.Typ.offset_length_of_elt typ idx in
T.update ~rcd:(term rcd) ~idx ~elt:(term elt) let off = T.integer (Z.of_int off) in
| ApN (Record, _, elts) -> let len = T.integer (Z.of_int len) in
T.record (Array.map ~f:term (IArray.to_array elts)) 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) | Ap1 (Splat, _, byt) -> T.splat (term byt)
and formula e = F.dq0 (term e) and formula e = F.dq0 (term e)

Loading…
Cancel
Save