Skip to content

Commit

Permalink
feat(BV, CP): Add propagators for bvshl and bvlshr
Browse files Browse the repository at this point in the history
This patch adds interval and bitlist propagators for the bvshl (left
shift) and bvlshr (logical right shift) in the intervals and bitlist
domains for bit-vectors.

The interval propagator for left shift needs to be written specially in
order to properly deal with overflow, but the propagator for bvlshr is
written using a generic propagator for (bi)-monotone functions.

The bitlist propagator for bvshl is required because it needs to
propagate information regarding low bits that are not tracked by
intervals. However, I am not sure that the bitlist propagator for bvlshr
is actually needed since it might be subsumed by the interval propagator
for bvlshr (and consistency constraints) entirely, and we might want to
remove it.
  • Loading branch information
bclement-ocp committed Mar 29, 2024
1 parent 1caa328 commit e7b006f
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 4 deletions.
56 changes: 56 additions & 0 deletions src/lib/reasoners/bitlist.ml
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,59 @@ let mul a b =
in
concat (unknown (sz - width mid_bits - width low_bits) Ex.empty) @@
concat mid_bits low_bits

let shl a b =
(* If the minimum value for [b] is larger than the bitwidth, the result is
zero.
Otherwise, any low zero bit in [a] is also a zero bit in the result, and
the minimum value for [b] also accounts for that many minimum zeros (e.g.
?000 shifted by at least 2 has at least 5 low zeroes).
NB: we would like to use the lower bound from the interval domain for [b]
here. *)
match Z.to_int (increase_lower_bound b Z.zero) with
| n when n < width a ->
let low_zeros = Z.trailing_zeros @@ Z.lognot @@ a.bits_clr in
if low_zeros + n >= width a then
exact (width a) Z.zero (Ex.union (explanation a) (explanation b))
else if low_zeros + n > 0 then
concat (unknown (width a - low_zeros - n) Ex.empty) @@
exact (low_zeros + n) Z.zero (Ex.union (explanation a) (explanation b))
else
unknown (width a) Ex.empty
| _ | exception Z.Overflow ->
exact (width a) Z.zero (explanation b)

let lshr a b =
(* If the minimum value for [b] is larger than the bitwidth, the result is
zero.
Otherwise, any high zero bit in [a] is also a zero bit in the result, and
the minimum value for [b] also accounts for that many minimum zeros (e.g.
000??? shifted by at least 2 is 00000?).
NB: we would like to use the lower bound from the interval domain for [b]
here. *)
match Z.to_int (increase_lower_bound b Z.zero) with
| n when n < width a ->
let sz = width a in
if Z.testbit a.bits_clr (sz - 1) then (* MSB is zero *)
let low_msb_zero = Z.numbits @@ Z.extract (Z.lognot a.bits_clr) 0 sz in
let nb_msb_zeros = sz - low_msb_zero in
assert (nb_msb_zeros > 0);
let nb_zeros = nb_msb_zeros + n in
if nb_zeros >= sz then
exact sz Z.zero (Ex.union (explanation a) (explanation b))
else
concat
(exact nb_zeros Z.zero (Ex.union (explanation a) (explanation b)))
(unknown (sz - nb_zeros) Ex.empty)
else if n > 0 then
concat
(exact n Z.zero (explanation b))
(unknown (sz - n) Ex.empty)
else
unknown sz Ex.empty
| _ | exception Z.Overflow ->
exact (width a) Z.zero (explanation b)
6 changes: 6 additions & 0 deletions src/lib/reasoners/bitlist.mli
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ val logxor : t -> t -> t
val mul : t -> t -> t
(** Multiplication. *)

val shl : t -> t -> t
(** Logical left shift. *)

val lshr : t -> t -> t
(** Logical right shift. *)

val concat : t -> t -> t
(** Bit-vector concatenation. *)

Expand Down
4 changes: 3 additions & 1 deletion src/lib/reasoners/bitv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@ module Shostak(X : ALIEN) = struct
| Op (
Concat | Extract _ | BV2Nat
| BVnot | BVand | BVor | BVxor
| BVadd | BVsub | BVmul | BVudiv | BVurem)
| BVadd | BVsub | BVmul | BVudiv | BVurem
| BVshl | BVlshr)
-> true
| _ -> false

Expand Down Expand Up @@ -412,6 +413,7 @@ module Shostak(X : ALIEN) = struct
| { f = Op (
BVand | BVor | BVxor
| BVadd | BVsub | BVmul | BVudiv | BVurem
| BVshl | BVlshr
); _ } ->
X.term_embed t, []
| _ -> X.make t
Expand Down
72 changes: 71 additions & 1 deletion src/lib/reasoners/bitv_rel.ml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ module Constraint : sig
This uses the convention that [x % 0] is [x]. *)

val bvshl : X.r -> X.r -> X.r -> t
(** [bvshl r x y] is the constraint [r = x << y] *)

val bvlshr : X.r -> X.r -> X.r -> t
(** [bvshl r x y] is the constraint [r = x >> y] *)

val bvule : X.r -> X.r -> t

val bvugt : X.r -> X.r -> t
Expand All @@ -274,6 +280,8 @@ end = struct
| Band | Bor | Bxor
(* Arithmetic operations *)
| Badd | Bmul | Budiv | Burem
(* Shift operations *)
| Bshl | Blshr

let pp_binop ppf = function
| Band -> Fmt.pf ppf "bvand"
Expand All @@ -283,14 +291,16 @@ end = struct
| Bmul -> Fmt.pf ppf "bvmul"
| Budiv -> Fmt.pf ppf "bvudiv"
| Burem -> Fmt.pf ppf "bvurem"
| Bshl -> Fmt.pf ppf "bvshl"
| Blshr -> Fmt.pf ppf "bvlshr"

let equal_binop : binop -> binop -> bool = Stdlib.(=)

let hash_binop : binop -> int = Hashtbl.hash

let is_commutative = function
| Band | Bor | Bxor | Badd | Bmul -> true
| Budiv | Burem -> false
| Budiv | Burem | Bshl | Blshr -> false

let propagate_binop ~ex dx op dy dz =
let open Domains.Ephemeral in
Expand Down Expand Up @@ -324,6 +334,12 @@ end = struct
(* TODO: full adder propagation *)
()

| Bshl -> (* Only forward propagation for now *)
update ~ex dx (Bitlist.shl !!dy !!dz)

| Blshr -> (* Only forward propagation for now *)
update ~ex dx (Bitlist.lshr !!dy !!dz)

| Bmul -> (* Only forward propagation for now *)
update ~ex dx (Bitlist.mul !!dy !!dz)

Expand All @@ -342,6 +358,12 @@ end = struct
update ~ex dy @@ norm @@ Intervals.sub !!dr !!dx;
update ~ex dx @@ norm @@ Intervals.sub !!dr !!dy

| Bshl -> (* Only forward propagation for now *)
update ~ex dr @@ Intervals.bvshl sz !!dx !!dy

| Blshr -> (* Only forward propagation for now *)
update ~ex dr @@ Intervals.lshr !!dx !!dy

| Bmul -> (* Only forward propagation for now *)
update ~ex dr @@ norm @@ Intervals.mult !!dx !!dy

Expand Down Expand Up @@ -557,6 +579,8 @@ end = struct
let bvmul = cbinop Bmul
let bvudiv = cbinop Budiv
let bvurem = cbinop Burem
let bvshl = cbinop Bshl
let bvlshr = cbinop Blshr

let crel r = hcons @@ Crel r

Expand Down Expand Up @@ -712,6 +736,27 @@ end = struct
) else
false

(* Add the constraint: r = x >> c *)
let add_lshr_const acts r x c =
let sz = bitwidth r in
match Z.to_int c with
| 0 -> add_eq acts r x
| n when n < sz ->
assert (n > 0);
let r_bitv = Shostak.Bitv.embed r in
let low_bits =
Shostak.Bitv.is_mine @@
Bitv.extract sz n (sz - 1) (Shostak.Bitv.embed x)
in
add_eq acts
(Shostak.Bitv.is_mine @@ Bitv.extract sz 0 (sz - 1 - n) r_bitv)
low_bits;
add_eq_const acts
(Shostak.Bitv.is_mine @@ Bitv.extract sz (sz - n) (sz - 1) r_bitv)
Z.zero
| _ | exception Z.Overflow ->
add_eq_const acts r Z.zero

(* Ground evaluation rules for binary operators. *)
let eval_binop op ty x y =
match op with
Expand All @@ -730,6 +775,18 @@ end = struct
cast ty x
else
cast ty @@ Z.rem x y
| Bshl -> (
match ty, Z.to_int y with
| Tbitv sz, y when y < sz ->
cast ty @@ Z.shift_left x y
| _ | exception Z.Overflow -> cast ty Z.zero
)
| Blshr -> (
match ty, Z.to_int y with
| Tbitv sz, y when y < sz ->
cast ty @@ Z.shift_right x y
| _ | exception Z.Overflow -> cast ty Z.zero
)

(* Constant simplification rules for binary operators.
Expand Down Expand Up @@ -776,6 +833,17 @@ end = struct

| Budiv | Burem -> false

(* shifts becomes a simple extraction when we know the right-hand side *)
| Bshl when X.is_constant y ->
add_shl_const acts r x (value y);
true
| Bshl -> false

| Blshr when X.is_constant y ->
add_lshr_const acts r x (value y);
true
| Blshr -> false

(* Algebraic rewrite rules for binary operators.
Rules based on constant simplifications are in [rw_binop_const]. *)
Expand Down Expand Up @@ -847,6 +915,8 @@ let extract_binop =
| BVmul -> Some bvmul
| BVudiv -> Some bvudiv
| BVurem -> Some bvurem
| BVshl -> Some bvshl
| BVlshr -> Some bvlshr
| _ -> None

let extract_constraints bcs uf r t =
Expand Down
Loading

0 comments on commit e7b006f

Please sign in to comment.