Skip to content

Commit cdc8248

Browse files
authored
improves the performance of bitvectors (#1405)
The comparison function was very heavyweight and was allocating objects on each call. The new version will allocate only when comparing unsigned bitvectors. In addition, this removes superflous renormalizations of bitvectors and adds the bigint_unsafe function to bitvec, so that we can create bitvec from bap_bitvectors without forcing a renormalization. This commit changes the semantics of ordering. The default ordering now allows bitvectors with different sizes (though I was assuming that this was the behavior and this is what was written in the documentation, so that this change could be seen as a bug fix).
1 parent b6afbe1 commit cdc8248

File tree

3 files changed

+38
-26
lines changed

3 files changed

+38
-26
lines changed

lib/bap_types/bap_bitvector.ml

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module Packed : sig
4646
val payload : t -> Bitvec.t
4747

4848
val hash : t -> int
49+
val compare : t -> t -> int
4950

5051
val lift1 : t -> (Bitvec.t -> Bitvec.t Bitvec.m) -> t
5152
val lift2 : t -> t -> (Bitvec.t -> Bitvec.t -> Bitvec.t Bitvec.m) -> t
@@ -73,12 +74,12 @@ end = struct
7374
let {data} = data x in
7475
Z.hash data
7576
let payload x =
76-
let m = modulus x in
77-
let z = data x in
78-
Bitvec.(bigint z.data mod m)
77+
let {data} = data x in
78+
Bitvec.bigint_unsafe data
7979
[@@inline]
8080

8181
let is_signed {packed=x} = Z.(is_odd x) [@@inline]
82+
let compare {packed=x} {packed=y} = Z.compare x y [@@inline]
8283

8384
let unsigned ({packed} as x) =
8485
if is_signed x
@@ -261,14 +262,20 @@ type packed = Packed.t [@@deriving bin_io]
261262
let sexp_of_packed = Sexp_hum.sexp_of_t
262263
let packed_of_sexp = Sexp_hum.t_of_sexp
263264

264-
let compare_mono x y =
265-
if is_signed x || is_signed y then
266-
let x_is_neg = msb x and y_is_neg = msb y in
267-
match x_is_neg, y_is_neg with
268-
| true,false -> -1
269-
| false,true -> 1
270-
| _ -> Bitvec.compare (payload x) (payload y)
271-
else Bitvec.compare (payload x) (payload y)
265+
let compare_signed x y =
266+
if phys_equal x y then 0
267+
else match Packed.compare x y with
268+
| 0 -> 0
269+
| r ->
270+
if is_signed x || is_signed y then
271+
let x_is_neg = msb x and y_is_neg = msb y in
272+
match x_is_neg, y_is_neg with
273+
| true,false -> -1
274+
| false,true -> 1
275+
| _ -> Bitvec.compare (payload x) (payload y)
276+
else r
277+
278+
272279

273280
let with_validation t ~f = Or_error.map ~f (Validate.result t)
274281

@@ -562,11 +569,10 @@ let enum_bits bv endian =
562569

563570
module Mono = Comparable.Make(struct
564571
type t = packed [@@deriving sexp]
565-
let compare x y =
566-
if phys_equal x y then 0
567-
else match Int.compare (bitwidth x) (bitwidth y) with
568-
| 0 -> compare_mono x y
569-
| _ -> failwith "Non monomorphic comparison"
572+
let compare x y = match compare_signed x y with
573+
| 0 -> 0
574+
| r when Int.equal (bitwidth x) (bitwidth y) -> r
575+
| _ -> failwith "Non monomorphic comparison"
570576
end)
571577

572578

@@ -616,12 +622,7 @@ end
616622
include Or_error.Monad_infix
617623
include Regular.Make(struct
618624
type t = packed [@@deriving bin_io, sexp]
619-
let compare x y =
620-
if phys_equal x y then 0
621-
else match Int.compare (bitwidth x) (bitwidth y) with
622-
| 0 -> compare_mono x y
623-
| r -> r
624-
[@@inline]
625+
let compare = compare_signed
625626
let version = "2.0.0"
626627
let module_name = Some "Bap.Std.Word"
627628
let pp ppf = pp_generic ppf
@@ -656,7 +657,7 @@ end
656657
module Stable = struct
657658
module V1 = struct
658659
type t = Packed.t
659-
let compare = compare
660+
let compare = compare_signed
660661

661662
let of_legacy {V1.z; w; signed=s} =
662663
let x = pack z w in
@@ -683,7 +684,7 @@ module Stable = struct
683684

684685
module V2 = struct
685686
type t = Packed.t [@@deriving bin_io, sexp]
686-
let compare = compare
687+
let compare = compare_signed
687688
end
688689
end
689690

lib/bitvec/bitvec.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ let int x m = norm m @@ Z.of_int x [@@inline]
9090
let int32 x m = norm m @@ Z.of_int32 x [@@inline]
9191
let int64 x m = norm m @@ Z.of_int64 x [@@inline]
9292
let bigint x m = norm m @@ x [@@inline]
93+
let bigint_unsafe x = x
9394

9495
let append w1 w2 x y =
9596
let w = w1 + w2 in

lib/bitvec/bitvec.mli

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ val m1 : modulus
2121
(** [m8 = modulus 8] = $255$ is the modulus of bitvectors with size [8] *)
2222
val m8 : modulus
2323

24-
(** [m16 = modulus 16] = $65535$ is the modulus of bitvectors with size [16]
24+
(** [m16 = modulus 16] = $65535$ is the modulus of bitvectors with size [16]
2525
@since 2.5.0 *)
2626
val m16 : modulus
2727

@@ -398,6 +398,16 @@ val to_string : t -> string
398398
val of_string : string -> t
399399

400400

401+
402+
(** [bigint_unsafe x] directly interprets a zarith value as bitvector.
403+
404+
The function is safe only if [x] is the result of [to_bigint],
405+
otherwise the resulting value is unpredictable.
406+
407+
@since 2.5.0
408+
*)
409+
val bigint_unsafe : Z.t -> t
410+
401411
(** [of_substring ~pos ~len s] is a bitvector that corresponds to a
402412
substring of [s] designated by the start position [pos] and length
403413
[len].
@@ -621,6 +631,6 @@ module M32 : D
621631
module M64 : D
622632

623633
(** [modular n] returns a module [M], which implements
624-
all operations in [S] modulo the bitwidth [n].
634+
all operations in [S] modulo the bitwidth [n].
625635
@since 2.5.0 *)
626636
val modular : int -> (module D)

0 commit comments

Comments
 (0)