|
|
|
(*
|
|
|
|
* 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.
|
|
|
|
*)
|
|
|
|
|
|
|
|
(** Expressions
|
|
|
|
|
|
|
|
Pure (heap-independent) expressions are complex arithmetic,
|
|
|
|
bitwise-logical, etc. operations over literal values and registers. *)
|
|
|
|
|
|
|
|
type op1 =
|
[sledge] Simplify type conversions
Summary:
The treatment of type conversions is too complicated, non-uniform,
etc. This diff attempts to simplify things by separating integer to
integer conversions, which are interpreted, from others, which are
essentially just uninterpreted functions. Integer conversions are now
handled using two expression and term forms: Signed and
Unsigned. These each interpret their argument as either a signed or
unsigned number of a given bitwidth:
```
| Signed of {bits: int}
(** [Ap1 (Signed {bits= n}, dst, arg)] is [arg] interpreted as an
[n]-bit signed integer and injected into the [dst] type. That is,
it two's-complement--decodes the low [n] bits of the infinite
two's-complement encoding of [arg]. The injection into [dst] is a
no-op, so [dst] must be an integer type with bitwidth at least
[n]. *)
| Unsigned of {bits: int}
(** [Ap1 (Unsigned {bits= n}, dst, arg)] is [arg] interpreted as an
[n]-bit unsigned integer and injected into the [dst] type. That
is, it unsigned-binary--decodes the low [n] bits of the infinite
two's-complement encoding of [arg]. The injection into [dst] is a
no-op, so [dst] must be an integer type with bitwidth greater than
[n]. *)
| Convert of {src: Typ.t}
(** [Ap1 (Convert {src}, dst, arg)] is [arg] converted from type [src]
to type [dst], possibly with loss of information. The [src] and
[dst] types must be [Typ.convertible] and must not both be
[Integer] types. *)
```
Reviewed By: ngorogiannis
Differential Revision: D18298140
fbshipit-source-id: 690f065b4
5 years ago
|
|
|
| Signed of {bits: int}
|
|
|
|
(** [Ap1 (Signed {bits= n}, dst, arg)] is [arg] interpreted as an
|
|
|
|
[n]-bit signed integer and injected into the [dst] type. That is,
|
|
|
|
it two's-complement--decodes the low [n] bits of the infinite
|
|
|
|
two's-complement encoding of [arg]. The injection into [dst] is a
|
|
|
|
no-op, so [dst] must be an integer type with bitwidth at least
|
|
|
|
[n]. *)
|
|
|
|
| Unsigned of {bits: int}
|
|
|
|
(** [Ap1 (Unsigned {bits= n}, dst, arg)] is [arg] interpreted as an
|
|
|
|
[n]-bit unsigned integer and injected into the [dst] type. That
|
|
|
|
is, it unsigned-binary--decodes the low [n] bits of the infinite
|
|
|
|
two's-complement encoding of [arg]. The injection into [dst] is a
|
|
|
|
no-op, so [dst] must be an integer type with bitwidth greater than
|
|
|
|
[n]. *)
|
|
|
|
| Convert of {src: Typ.t}
|
|
|
|
(** [Ap1 (Convert {src}, dst, arg)] is [arg] converted from type [src]
|
|
|
|
to type [dst], possibly with loss of information. The [src] and
|
|
|
|
[dst] types must be [Typ.convertible] and must not both be
|
|
|
|
[Integer] types. *)
|
|
|
|
| Splat (** Iterated concatenation of a single byte *)
|
|
|
|
| Select of int (** Select an index from a record *)
|
|
|
|
[@@deriving compare, equal, hash, sexp]
|
|
|
|
|
|
|
|
type op2 =
|
|
|
|
| Eq (** Equal test *)
|
|
|
|
| Dq (** Disequal test *)
|
|
|
|
| Gt (** Greater-than test *)
|
|
|
|
| Ge (** Greater-than-or-equal test *)
|
|
|
|
| Lt (** Less-than test *)
|
|
|
|
| Le (** Less-than-or-equal test *)
|
|
|
|
| Ugt (** Unsigned greater-than test *)
|
|
|
|
| Uge (** Unsigned greater-than-or-equal test *)
|
|
|
|
| Ult (** Unsigned less-than test *)
|
|
|
|
| Ule (** Unsigned less-than-or-equal test *)
|
|
|
|
| Ord (** Ordered test (neither arg is nan) *)
|
|
|
|
| Uno (** Unordered test (some arg is nan) *)
|
|
|
|
| Add (** Addition *)
|
|
|
|
| Sub (** Subtraction *)
|
|
|
|
| Mul (** Multiplication *)
|
|
|
|
| Div (** Division, for integers result is truncated toward zero *)
|
|
|
|
| Rem
|
|
|
|
(** Remainder of division, satisfies [a = b * div a b + rem a b] and
|
|
|
|
for integers [rem a b] has same sign as [a], and [|rem a b| < |b|] *)
|
|
|
|
| Udiv (** Unsigned division *)
|
|
|
|
| Urem (** Remainder of unsigned division *)
|
[sledge] Add typ of integer constants
Summary:
Types of integer constants, in particular their bit-width, are
necessary for:
- correctly interpreting bitwise operations (e.g. `-1 xor 1` at type
`i1` is `0` while without the type the result is `-2`), and;
- distinguishing between integers and booleans, which are one-bit
integers, since booleans admit stronger algebraic simplification.
Note that code does genuinely treat 1-bit integers interchangeably as
booleans and integers, e.g. with expressions such as `e + (b != 42)`.
Therefore a lighter-weight early syntactic distinction between boolean
and bitwise operations is nontrivial/impossible to make robust.
This patch:
- adds the type to the representation of Exp.Integer;
- adds checks that Integer values fit within their specified bit-width
- factors out code handling 1-bit integers as booleans into `Z`, as it
is easy to make mistakes when forgetting that `-1`, not `1`, is the
representation of `true`;
- corrects the treatment of Exp.Convert in some cases involving
treating negative integers as unsigned;
- corrects and strengthens Exp simplification based on the bit-width
information;
- removes the `e - e ==> 0` simplification, due to not having the type
for `0`.
Reviewed By: mbouaziz
Differential Revision: D10488407
fbshipit-source-id: ff4320a29
6 years ago
|
|
|
| And (** Conjunction, boolean or bitwise *)
|
|
|
|
| Or (** Disjunction, boolean or bitwise *)
|
|
|
|
| Xor (** Exclusive-or, bitwise *)
|
|
|
|
| Shl (** Shift left, bitwise *)
|
|
|
|
| Lshr (** Logical shift right, bitwise *)
|
|
|
|
| Ashr (** Arithmetic shift right, bitwise *)
|
|
|
|
| Update of int (** Constant record with updated index *)
|
|
|
|
[@@deriving compare, equal, hash, sexp]
|
|
|
|
|
|
|
|
type op3 = Conditional (** If-then-else *)
|
|
|
|
[@@deriving compare, equal, hash, sexp]
|
|
|
|
|
|
|
|
type opN =
|
|
|
|
| Record (** Record (array / struct) constant *)
|
|
|
|
| Struct_rec
|
|
|
|
(** Struct constant that may recursively refer to itself
|
|
|
|
(transitively) from [elts]. NOTE: represented by cyclic values. *)
|
|
|
|
[@@deriving compare, equal, hash, sexp]
|
|
|
|
|
|
|
|
type t = private {desc: desc; term: Term.t}
|
|
|
|
|
|
|
|
and desc = private
|
|
|
|
| Reg of {name: string; typ: Typ.t} (** Virtual register *)
|
|
|
|
| Nondet of {msg: string; typ: Typ.t}
|
|
|
|
(** Anonymous register with arbitrary value, representing
|
|
|
|
non-deterministic approximation of value described by [msg] *)
|
|
|
|
| Label of {parent: string; name: string}
|
|
|
|
(** Address of named code block within parent function *)
|
|
|
|
| Integer of {data: Z.t; typ: Typ.t} (** Integer constant *)
|
|
|
|
| Float of {data: string; typ: Typ.t} (** Floating-point constant *)
|
|
|
|
| Ap1 of op1 * Typ.t * t
|
|
|
|
| Ap2 of op2 * Typ.t * t * t
|
|
|
|
| Ap3 of op3 * Typ.t * t * t * t
|
|
|
|
| ApN of opN * Typ.t * t iarray
|
|
|
|
[@@deriving compare, equal, hash, sexp]
|
|
|
|
|
|
|
|
val pp : t pp
|
|
|
|
|
|
|
|
include Invariant.S with type t := t
|
|
|
|
|
|
|
|
(** Exp.Reg is re-exported as Reg *)
|
|
|
|
module Reg : sig
|
|
|
|
type exp := t
|
|
|
|
type t = private exp [@@deriving compare, equal, hash, sexp]
|
|
|
|
|
|
|
|
module Set : sig
|
|
|
|
include Set.S with type elt := t
|
|
|
|
|
|
|
|
val sexp_of_t : t -> Sexp.t
|
|
|
|
val t_of_sexp : Sexp.t -> t
|
|
|
|
val pp : t pp
|
|
|
|
val vars : t -> Var.Set.t
|
|
|
|
end
|
|
|
|
|
|
|
|
module Map : Map.S with type key := t
|
|
|
|
|
|
|
|
val demangle : (string -> string option) ref
|
|
|
|
val pp : t pp
|
|
|
|
val pp_demangled : t pp
|
|
|
|
|
|
|
|
include Invariant.S with type t := t
|
|
|
|
|
|
|
|
val of_exp : exp -> t option
|
|
|
|
val program : ?global:unit -> Typ.t -> string -> t
|
|
|
|
val var : t -> Var.t
|
|
|
|
val name : t -> string
|
|
|
|
val typ : t -> Typ.t
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Construct *)
|
|
|
|
|
|
|
|
(* registers *)
|
|
|
|
val reg : Reg.t -> t
|
|
|
|
|
|
|
|
(* constants *)
|
|
|
|
val nondet : Typ.t -> string -> t
|
|
|
|
val label : parent:string -> name:string -> t
|
|
|
|
val null : t
|
|
|
|
val bool : bool -> t
|
|
|
|
val true_ : t
|
|
|
|
val false_ : t
|
|
|
|
val integer : Typ.t -> Z.t -> t
|
|
|
|
val float : Typ.t -> string -> t
|
|
|
|
|
|
|
|
(* type conversions *)
|
[sledge] Simplify type conversions
Summary:
The treatment of type conversions is too complicated, non-uniform,
etc. This diff attempts to simplify things by separating integer to
integer conversions, which are interpreted, from others, which are
essentially just uninterpreted functions. Integer conversions are now
handled using two expression and term forms: Signed and
Unsigned. These each interpret their argument as either a signed or
unsigned number of a given bitwidth:
```
| Signed of {bits: int}
(** [Ap1 (Signed {bits= n}, dst, arg)] is [arg] interpreted as an
[n]-bit signed integer and injected into the [dst] type. That is,
it two's-complement--decodes the low [n] bits of the infinite
two's-complement encoding of [arg]. The injection into [dst] is a
no-op, so [dst] must be an integer type with bitwidth at least
[n]. *)
| Unsigned of {bits: int}
(** [Ap1 (Unsigned {bits= n}, dst, arg)] is [arg] interpreted as an
[n]-bit unsigned integer and injected into the [dst] type. That
is, it unsigned-binary--decodes the low [n] bits of the infinite
two's-complement encoding of [arg]. The injection into [dst] is a
no-op, so [dst] must be an integer type with bitwidth greater than
[n]. *)
| Convert of {src: Typ.t}
(** [Ap1 (Convert {src}, dst, arg)] is [arg] converted from type [src]
to type [dst], possibly with loss of information. The [src] and
[dst] types must be [Typ.convertible] and must not both be
[Integer] types. *)
```
Reviewed By: ngorogiannis
Differential Revision: D18298140
fbshipit-source-id: 690f065b4
5 years ago
|
|
|
val signed : int -> t -> to_:Typ.t -> t
|
|
|
|
val unsigned : int -> t -> to_:Typ.t -> t
|
|
|
|
val convert : Typ.t -> to_:Typ.t -> t -> t
|
|
|
|
|
|
|
|
(* comparisons *)
|
|
|
|
val eq : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val dq : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val gt : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val ge : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val lt : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val le : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val ugt : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val uge : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val ult : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val ule : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val ord : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val uno : ?typ:Typ.t -> t -> t -> t
|
|
|
|
|
|
|
|
(* arithmetic *)
|
|
|
|
val add : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val sub : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val mul : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val div : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val rem : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val udiv : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val urem : ?typ:Typ.t -> t -> t -> t
|
|
|
|
|
|
|
|
(* boolean / bitwise *)
|
|
|
|
val and_ : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val or_ : ?typ:Typ.t -> t -> t -> t
|
|
|
|
|
|
|
|
(* bitwise *)
|
|
|
|
val xor : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val shl : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val lshr : ?typ:Typ.t -> t -> t -> t
|
|
|
|
val ashr : ?typ:Typ.t -> t -> t -> t
|
|
|
|
|
|
|
|
(* if-then-else *)
|
|
|
|
val conditional : ?typ:Typ.t -> cnd:t -> thn:t -> els:t -> t
|
|
|
|
|
|
|
|
(* memory *)
|
|
|
|
val splat : Typ.t -> t -> t
|
|
|
|
|
|
|
|
(* records (struct / array values) *)
|
|
|
|
val record : Typ.t -> t iarray -> t
|
|
|
|
val select : Typ.t -> t -> int -> t
|
|
|
|
val update : Typ.t -> rcd:t -> int -> elt:t -> t
|
|
|
|
|
|
|
|
val struct_rec :
|
|
|
|
(module Hashtbl.Key_plain with type t = 'id)
|
|
|
|
-> (id:'id -> Typ.t -> t lazy_t iarray -> t) Staged.t
|
|
|
|
(** [struct_rec Id id element_thunks] constructs a possibly-cyclic [Struct]
|
|
|
|
value. Cycles are detected using [Id]. The caller of [struct_rec Id]
|
|
|
|
must ensure that a single unstaging of [struct_rec Id] is used for each
|
|
|
|
complete cyclic value. Also, the caller must ensure that recursive calls
|
|
|
|
to [struct_rec Id] provide [id] values that uniquely identify at least
|
|
|
|
one point on each cycle. Failure to obey these requirements will lead to
|
|
|
|
stack overflow. *)
|
|
|
|
|
|
|
|
val size_of : t -> t
|
|
|
|
|
|
|
|
(** Traverse *)
|
|
|
|
|
|
|
|
val fold_regs : t -> init:'a -> f:('a -> Reg.t -> 'a) -> 'a
|
|
|
|
|
|
|
|
(** Query *)
|
|
|
|
|
|
|
|
val term : t -> Term.t
|
|
|
|
val typ : t -> Typ.t
|
|
|
|
val is_true : t -> bool
|
|
|
|
val is_false : t -> bool
|