1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
|
(******************************************************************************)
(* ASLRef *)
(******************************************************************************)
(*
* SPDX-FileCopyrightText: Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
* SPDX-License-Identifier: BSD-3-Clause
*)
(******************************************************************************)
(* Disclaimer: *)
(* This material covers both ASLv0 (viz, the existing ASL pseudocode language *)
(* which appears in the Arm Architecture Reference Manual) and ASLv1, a new, *)
(* experimental, and as yet unreleased version of ASL. *)
(* This material is work in progress, more precisely at pre-Alpha quality as *)
(* per Arm’s quality standards. *)
(* In particular, this means that it would be premature to base any *)
(* production tool development on this material. *)
(* However, any feedback, question, query and feature request would be most *)
(* welcome; those can be sent to Arm’s Architecture Formal Team Lead *)
(* Jade Alglave <jade.alglave@arm.com>, or by raising issues or PRs to the *)
(* herdtools7 github repository. *)
(******************************************************************************)
(* BitVector type *)
(*
Notations:
- A type t is named (length, data) or bv
- b is a bit
- n = length / 8 : ~the length of data~
- m = length mod 8 : the number of bits of the trailing character.
*)
(* --------------------------------------------------------------------------
Main type definition
--------------------------------------------------------------------------*)
type t = int * string
(** A bitvector is given by its length in bits and its containing data.
Note that the stored data is a string, but only for performance and
immutability of values reasons. This should not be printed as is, and
users should use the dedicated printing primitives.
Invariant:
For a value (length, data), we have
String.length data = length / 8 + 1
*)
(* Accessors. *)
let length = fst
let data = snd
(* --------------------------------------------------------------------------
Helpers
--------------------------------------------------------------------------*)
(* Constant. *)
let code_0 = Char.code '0'
let char_0 = Char.chr 0
let char_ff = Char.chr 0xff
(* Mask for the last character, given by [length mod 8]. *)
let last_char_mask m = (1 lsl m) - 1
(* [read_bit_raw i c] reads bit at index [i] in character [c]. *)
let read_bit_raw i c = (Char.code c lsr i) land 1
(* [read_bit i c] is the printable character representing bit [i] in
character [c]. *)
let read_bit i c = Char.chr (read_bit_raw i c + code_0)
(* [set_bit_raw i dst b] sets the [i]-th bit of [dst] to [b]. *)
let set_bit_raw i dst b = dst land lnot (1 lsl i) lor (b lsl i)
(* [set_bit src_pos dst_pos src dst] sets the [dst_pos]-th bit of [pos] to the [src_pos]-th bit of [src]. *)
let set_bit src_pos dst_pos src dst =
read_bit_raw src_pos src |> set_bit_raw dst_pos dst |> Char.chr
(* Debug printer. *)
let _pp_data f (length, data) =
let open Format in
pp_print_int f length;
pp_print_char f 'x';
String.iter (fun c -> fprintf f "%x" @@ Char.code c) data
let create_data_bytes length = Bytes.create ((length + 7) / 8)
(** [String.for_all] taken directly out of stdlib version 4.13 . *)
let string_for_all p s =
let n = String.length s in
let rec loop i =
if i = n then true
else if p (String.unsafe_get s i) then loop (succ i)
else false
in
loop 0
(** [remask bv] ensures that the extra bits on the trailing character are '0'.
It edits in place the string, so to use with prudence. *)
let remask (length, data) =
let n = length / 8 and m = length mod 8 in
if m == 0 then (length, data)
else
let trailing_d = String.get data n |> Char.code in
let masked_d = trailing_d land last_char_mask m in
let new_c = Char.chr masked_d in
let buf = Bytes.unsafe_of_string data in
let () = Bytes.set buf n new_c in
let data = Bytes.unsafe_to_string buf in
(length, data)
(* --------------------------------------------------------------------------
Signs and extensions
--------------------------------------------------------------------------*)
let sign_bit (length, data) =
match length with
| 0 -> 0
| _ ->
let n = length / 8 and m = length mod 8 in
let data_pos = if m = 0 then n - 1 else n in
let bit_index = (m + 7) mod 8 in
String.get data data_pos |> read_bit_raw bit_index
let extend signed nbytes (length, data) =
let to_length = 8 * nbytes in
let data_length = String.length data in
if length > to_length then (to_length, String.sub data 0 nbytes)
else if length = to_length then (length, data)
else
let result =
Bytes.extend (Bytes.unsafe_of_string data) 0 (nbytes - data_length)
in
let neg = signed && sign_bit (length, data) = 1 in
let () =
if neg then
let n = length / 8 and m = length mod 8 in
let sign_bit_pos = if m = 0 then n - 1 else n in
String.get data sign_bit_pos
|> Char.code
|> Int.logor [| 0; 0xff; 0xfe; 0xfc; 0xf8; 0xf0; 0xe0; 0xc0 |].(m)
|> Char.chr
|> Bytes.set result sign_bit_pos
in
Bytes.fill result data_length (nbytes - data_length)
(if neg then char_ff else char_0);
(to_length, Bytes.unsafe_to_string result)
let zero_extend = extend false
let sign_extend = extend true
(* --------------------------------------------------------------------------
Printers and conversions
--------------------------------------------------------------------------*)
let pp_t =
let open Format in
fun f (length, data) ->
pp_print_char f '\'';
pp_open_hbox f ();
let n = length / 8 and m = length mod 8 in
if m <> 0 then
let c = String.get data n in
for j = m - 1 downto 0 do
read_bit j c |> Format.pp_print_char f
done
else ();
for i = n - 1 downto 0 do
let c = String.get data i in
for j = 7 downto 0 do
read_bit j c |> Format.pp_print_char f
done
done;
pp_close_box f ();
pp_print_char f '\''
let to_string (length, data) =
let result = Buffer.create (length + 2) in
let n = length / 8 and m = length mod 8 in
Buffer.add_char result '\'';
if m <> 0 then
let c = String.get data n in
for j = m - 1 downto 0 do
read_bit j c |> Buffer.add_char result
done
else ();
for i = n - 1 downto 0 do
let c = String.get data i in
for j = 7 downto 0 do
read_bit j c |> Buffer.add_char result
done
done;
Buffer.add_char result '\'';
Buffer.contents result
let hex_number =
Array.get
[|
'0';
'1';
'2';
'3';
'4';
'5';
'6';
'7';
'8';
'9';
'a';
'b';
'c';
'd';
'e';
'f';
|]
let buffer_add_hex result y = y mod 16 |> hex_number |> Buffer.add_char result
let write_char_hex result c =
let x = Char.code c in
buffer_add_hex result (x lsr 4);
buffer_add_hex result x
let to_string_hexa (length, data) =
let result = Buffer.create (2 * (length + 2)) in
let n = length / 8 and m = length mod 8 in
Buffer.add_string result "0x";
(if m <> 0 then
let c = String.get data n in
if m > 4 then write_char_hex result c
else c |> Char.code |> buffer_add_hex result);
for i = n - 1 downto 0 do
String.get data i |> write_char_hex result
done;
Buffer.contents result
let to_int (length, data) =
let result = ref 0 in
let n = length / 8 and m = length mod 8 in
for i = 0 to n - 1 do
let c = String.get data i |> Char.code in
result := !result lor (c lsl (i * 8))
done;
if m != 0 then
let c = String.get data n |> Char.code |> ( land ) (last_char_mask m) in
result := !result lor (c lsl (n * 8))
else ();
!result
let to_int64_raw (length, data) =
let result = ref Int64.zero in
let n = length / 8 and m = length mod 8 in
for i = 0 to n - 1 do
let c = String.get data i |> Char.code in
result := Int64.logor !result (c lsl (i * 8) |> Int64.of_int)
done;
if m != 0 then
let c = String.get data n |> Char.code |> ( land ) (last_char_mask m) in
result := Int64.logor !result (c lsl (n * 8) |> Int64.of_int)
else ();
!result
let to_int64_signed bv = bv |> sign_extend 8 |> to_int64_raw
let to_int64_unsigned (length, data) =
let _, data = zero_extend 8 (length, data) in
let _, data = remask (63, data) in
to_int64_raw (64, data)
let to_z_unsigned (_, data) = Z.of_bits data
let to_z_signed ((sz, _) as bv) =
let sgn = sign_bit bv in
let r = to_z_unsigned bv in
if sgn = 0 then r else Z.sub r (Z.shift_left Z.one sz)
let z63 = Z.shift_left Z.one 63
let z64 = Z.shift_left Z.one 64
let printable bv =
let z = to_z_signed bv in
if Z.geq z z63 then Z.sub z z64 else z
let of_string s =
let result = Buffer.create ((String.length s / 8) + 1) in
let lengthr = ref 0 in
let last_char = ref 0 in
for i = String.length s - 1 downto 0 do
match String.get s i with
| ('0' | '1') as b ->
let length = !lengthr in
let () = lengthr := length + 1 in
let m = length mod 8 in
let c = !last_char in
let c = if b = '1' then c lor (1 lsl m) else c in
if m = 7 then (
Buffer.add_char result (Char.chr c);
last_char := 0)
else last_char := c
| _ -> ()
done;
let length = !lengthr in
let m = length mod 8 in
if m <> 0 then
let c = !last_char land last_char_mask m in
Buffer.add_char result (Char.chr c)
else ();
(length, Buffer.contents result)
let of_int64 s =
let result = create_data_bytes 64 in
for i = 0 to 7 do
Int64.shift_right_logical s (8 * i)
|> Int64.logand 255L |> Int64.to_int |> Char.chr |> Bytes.set result i
done;
(64, Bytes.unsafe_to_string result)
let of_int x = of_int64 (Int64.of_int x)
let of_z sz z =
let n = (sz + 7) / 8 and m = sz mod 8 in
let result = Bytes.make n char_0 in
let rec do_rec msk i =
if i >= 0 then (
let c = Z.extract z (i * 8) 8 |> Z.to_int |> ( land ) msk |> Char.chr in
Bytes.unsafe_set result i c;
do_rec 0xFF (i - 1))
in
let msk = last_char_mask (if m = 0 then 8 else m) in
do_rec msk (n - 1);
(sz, Bytes.unsafe_to_string result)
let of_int_sized sz i = of_z sz (Z.of_int i)
(* --------------------------------------------------------------------------
Operations
--------------------------------------------------------------------------*)
let ensure_equal_length length1 length2 =
if length1 = length2 then length1 else raise (Invalid_argument "bitwise_op")
(* [bitwise_op Int.logand bv1 bv2] computes the bitwise and on bv1 and bv2. *)
let bitwise_op int_op (length1, data1) (length2, data2) =
let length = ensure_equal_length length1 length2 in
let n = length / 8 and m = length mod 8 in
let result = create_data_bytes length in
for i = 0 to n - 1 do
let d1 = String.get data1 i |> Char.code
and d2 = String.get data2 i |> Char.code in
let c = int_op d1 d2 |> Char.chr in
Bytes.set result i c
done;
if m <> 0 then
let mask = last_char_mask m in
let d1 = String.get data1 n |> Char.code |> ( land ) mask
and d2 = String.get data2 n |> Char.code |> ( land ) mask in
let c = int_op d1 d2 |> ( land ) mask |> Char.chr in
Bytes.set result n c
else ();
(length, Bytes.unsafe_to_string result)
let lognot (length, data) =
let bnot c = c |> Char.code |> lnot |> ( land ) 0xff |> Char.chr in
let ndata = String.map bnot data in
remask (length, ndata)
let logand = bitwise_op ( land )
let logor = bitwise_op ( lor )
let logxor bv1 bv2 = bitwise_op ( lxor ) bv1 bv2 |> remask
let equal bv1 bv2 =
if false then Format.eprintf "@[%a =@ %a@]@." _pp_data bv1 _pp_data bv2;
length bv1 == length bv2
&& (* let bv1 = remask bv1 and bv2 = remask bv2 in *)
String.equal (data bv1) (data bv2)
let compare bv1 bv2 =
match Int.compare (length bv1) (length bv2) with
| 0 ->
(* let bv1 = remask bv1 and bv2 = remask bv2 in *)
String.compare (data bv1) (data bv2)
| i -> i
let string_fold_left f init s =
let state = ref init in
for i = 0 to String.length s - 1 do
state := f !state @@ String.get s i
done;
!state
let bitcount (_length, data) =
(* Inspired by both
https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
https://github.com/backtracking/bitv/blob/master/bitv.ml#L351
*)
let rec one_byte x = if x = 0 then 0 else 1 + one_byte (x land (x - 1)) in
(* Here Filliatre caches the count in an array, but we are not yet there. *)
let folder acc c = acc + one_byte (Char.code c) in
string_fold_left folder 0 data
let log2 =
let rec loop acc i = if i <= 0 then acc else loop (acc + 1) (i lsr 1) in
loop 0
let highest_set_bit (_length, data) =
let rec loop i =
if i < 0 then 0
else
let c = String.get data i |> Char.code in
if c != 0 then log2 c + (8 * i) else loop (i - 1)
in
loop (String.length data - 1)
let to_int_signed (length, data) =
if length = 0 then 0
else if sign_bit (length, data) = 1 then
-to_int (lognot (length - 1, data)) - 1
else to_int (length - 1, data)
(* --------------------------------------------------------------------------
Slices
--------------------------------------------------------------------------*)
(* Write into dst at position pos_dst the bit of src at position pos_src. *)
let copy_bit dst src pos_src pos_dst =
let c_src = String.get src (pos_src / 8)
and c_dst = Bytes.get dst (pos_dst / 8) |> Char.code in
let new_char_dst = set_bit (pos_src mod 8) (pos_dst mod 8) c_src c_dst in
Bytes.set dst (pos_dst / 8) new_char_dst
let prefix (len, s) dst_len =
assert (dst_len <= len);
let sz8 = (dst_len + 7) / 8 in
let dst_s = String.sub s 0 sz8 in
(dst_len, dst_s)
let extract_slice (_length_src, data_src) positions =
try
let length = List.length positions in
let result = create_data_bytes length in
(* Same effect than [List.rev positions], as we build those from the end. *)
let copy_bit_here i pos = copy_bit result data_src pos (length - 1 - i) in
let () = List.iteri copy_bit_here positions in
remask (length, Bytes.unsafe_to_string result)
with Invalid_argument msg ->
raise (Invalid_argument (Printf.sprintf "extract_sliced (%s)" msg))
let write_slice (length_dst, data_dst) (length_src, data_src) positions =
let min x y = if x <= y then x else y in
let length_src = min (List.length positions) length_src in
let result = Bytes.of_string data_dst in
(* Same effect as [List.rev positions], since we build those from the end. *)
let copy_bit_here i pos = copy_bit result data_src (length_src - 1 - i) pos in
let () = List.iteri copy_bit_here positions in
remask (length_dst, Bytes.unsafe_to_string result)
(* Retuns length of destination *)
let pp (l, bs) = Printf.sprintf "%s<%d>" (to_string (l, Bytes.to_string bs)) l
let pp_bytes n dst =
let sz = (n + 1) * 8 in
pp (sz, dst)
(* [mix_chars_start off low_c high_c] return a char,
whose off lower order bits are from low_c and
the 8-off higher order bits are the 8-off lower
order bits of high_c *)
let mix_chars_start off low_c high_c =
let low_bits = ((1 lsl off) - 1) land Char.code low_c
and high_bits = Char.code high_c lsl off in
low_bits lor high_bits |> ( land ) 0xff |> Char.chr
(* [mix_chars_body off low_c high_c] return a char,
whose off lower order bits are the off higher
order bits of low_c and the 8-off higher order bits are the
8-off lower order bits of high_c *)
let mix_chars_body off low_c high_c =
let low_bits = Char.code low_c lsr (8 - off)
and high_bits = Char.code high_c lsl off in
low_bits lor high_bits |> ( land ) 0xff |> Char.chr
(* [mix_chars_final off c] return a char,
whose sz lower order bits are the
sz bits of c at possition off. *)
let mix_char_final off sz c =
(Char.code c lsr off) land ((1 lsl sz) - 1) |> Char.chr
let copy_into dst ((length_src, data_src) as b) offset =
let () =
if false then
Printf.eprintf "copy_into %s + %s, offset=%d\n%!" (to_string b)
(pp (offset, dst))
offset
in
let length_dst = offset + length_src in
if length_src <= 0 then length_dst
else
let n_off = offset / 8 and m_off = offset mod 8 in
let () =
if m_off = 0 then
Bytes.blit_string data_src 0 dst n_off (String.length data_src)
else
(*
* First handle the first written char:
* The 8-m_off low order bits of src.[0] are
* copied into the 8-m_off high order_bits of
* dst.[n_off] *)
let prec_c = Bytes.get dst n_off and next_c = String.get data_src 0 in
mix_chars_start m_off prec_c next_c |> Bytes.set dst n_off;
let () =
if false then Printf.eprintf "Start: %s\n%!" (pp_bytes n_off dst)
in
(*
* Now, loop.
* At each step, 8-off bits are taken from
* the higher order bits of some source char,
* while the off higher order bits are taken from the
* lower order bits of the next_char
*)
(* Number of bits still to write *)
let rem_bits = length_src - (8 - m_off) in
(* Useful string length *)
let src_str_len = (length_src + 7) / 8 in
let rec do_rec i_src i_dst rem_bits =
if i_src + 1 >= src_str_len then (i_src, i_dst, rem_bits)
else
let prec_c = String.get data_src i_src in
let next_c = String.get data_src (i_src + 1) in
mix_chars_body m_off prec_c next_c |> Bytes.set dst i_dst;
let () =
if false then
Printf.eprintf "Body -> %s\n%!" (pp_bytes i_dst dst)
in
do_rec (i_src + 1) (i_dst + 1) (rem_bits - 8)
in
let i_src, i_dst, rem_bits = do_rec 0 (n_off + 1) rem_bits in
let () =
if false then
Printf.eprintf "i_src=%d, i_dst=%d, rem_bits=%d\n%!" i_src i_dst
rem_bits
in
if rem_bits > 0 then
let c = String.get data_src i_src in
mix_char_final (8 - m_off) rem_bits c |> Bytes.set dst i_dst
in
let () =
if false then
Printf.eprintf "copy_into %s + %s ->%s\n%!" (to_string b)
(pp (offset, dst))
(pp (length_dst, dst))
in
length_dst
let concat bvs =
(if false then
let pp = List.map to_string bvs in
Printf.eprintf "Concat %s\n%!" (String.concat "," pp));
let length = List.fold_left (fun acc bv -> acc + length bv) 0 bvs in
let result = create_data_bytes length in
let _ = List.fold_right (copy_into result) bvs 0 in
(length, Bytes.unsafe_to_string result)
(* --------------------------------------------------------------------------
Small utils
--------------------------------------------------------------------------*)
let zeros length =
let n = length / 8 and m = length mod 8 in
let data = String.make (if m = 0 then n else n + 1) char_0 in
(length, data)
let ones length =
let n = length / 8 and m = length mod 8 in
let data = String.make (if m = 0 then n else n + 1) char_ff in
remask (length, data)
let zero = zeros 1
let one = ones 1
let empty = (0, "")
let is_zeros bv =
let _length, data = remask bv in
string_for_all (( = ) char_0) data
let is_ones bv = length bv |> ones |> equal bv
type mask = {
length : int;
set : string;
unset : string;
specified : string;
initial_string : string;
}
let mask_length mask = mask.length
let mask_of_string s =
let length_set, set =
String.map (function 'x' -> '0' | '0' -> '0' | '1' -> '1' | c -> c) s
|> of_string
and length_unset, unset =
String.map (function 'x' -> '0' | '0' -> '1' | '1' -> '0' | c -> c) s
|> of_string
and length_specified, specified =
String.map (function 'x' -> '0' | '0' -> '1' | '1' -> '1' | c -> c) s
|> of_string
in
let () =
if false then
Format.eprintf "Parsing %s gave %a and %a@." s _pp_data (length_set, set)
_pp_data (length_unset, unset)
in
if length_set != length_unset || length_set != length_specified then
raise (Invalid_argument "Mask")
else { length = length_set; set; unset; specified; initial_string = s }
let mask_of_bitvector ((length, data) as bv) =
let set = data
and _, unset = lognot bv
and _, specified = ones length
and initial_string = to_string bv in
{ length; set; unset; specified; initial_string }
let matches bv mask =
if length bv != mask.length then raise (Invalid_argument "mask_matches");
equal
(mask.length, mask.specified)
(logor
(logand bv (mask.length, mask.set))
(logand (lognot bv) (mask.length, mask.unset)))
let mask_to_string mask = mask.initial_string
let mask_to_canonical_string mask =
let set = to_string (mask.length, mask.set)
and unset = to_string (mask.length, mask.unset) in
let result = Bytes.create (String.length set) in
for i = 0 to String.length set - 1 do
let b =
match (String.get set i, String.get unset i) with
| '0', '0' -> 'x'
| '1', _ -> '1'
| _, '1' -> '0'
| c, _ -> c
in
Bytes.set result i b
done;
Bytes.unsafe_to_string result
let mask_set mask = (mask.length, mask.set)
let mask_unset mask = (mask.length, mask.unset)
let mask_specified mask = (mask.length, mask.specified)
|