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
(* SPDX-License-Identifier: AGPL-3.0-or-later *)
(* Copyright © 2021-2026 OCamlPro *)
(* Written by the Owi programmers *)

module Map = Map.Make (Int32)

type t =
  { data : Smtml.Typed.Bitv8.t Map.t
  ; size : Symbolic_i32.t
  ; limits : Binary.Mem.Type.limits
  }

let address a =
  let open Symbolic_choice in
  match Smtml.Typed.view a with
  | Val (Bitv bv) -> return (Smtml.Bitvector.to_int32 bv)
  | _ -> select_i32 a

let empty_byte = Smtml.Typed.Bitv8.v (Smtml.Bitvector.of_int8 0)

let load_byte a { data; _ } =
  match Map.find_opt a data with None -> empty_byte | Some v -> v

let replace_byte a (v : Smtml.Typed.Bitv8.t) data = Map.add a v data

let page_size = 65_536

let page_size_i32 = Symbolic_i32.of_int page_size

(******************************************)

let i32 v =
  match Smtml.Expr.view v with
  | Val (Bitv i) when Smtml.Bitvector.numbits i = 32 ->
    Smtml.Bitvector.to_int32 i
  | _ -> assert false

let grow m delta =
  let old_size = Symbolic_i32.mul m.size page_size_i32 in
  let new_size = Symbolic_i32.(div (add old_size delta) page_size_i32) in
  let size =
    Symbolic_boolean.ite (Symbolic_i32.lt m.size new_size) new_size m.size
  in
  { m with size }

let size { size; _ } = Symbolic_i32.mul size page_size_i32

let size_in_pages { size; _ } = size

let load_8_s m a =
  let open Symbolic_choice in
  let+ a = address a in
  let v = load_byte a m in
  Smtml.Typed.Bitv32.of_int8_s v

let load_8_u m a =
  let open Symbolic_choice in
  let+ a = address a in
  let v = load_byte a m in
  Smtml.Typed.Bitv32.of_int8_u v

let load_16_unchecked m a : Smtml.Typed.Bitv16.t =
  let lsb = load_byte a m in
  let msb = load_byte (Int32.add a 1l) m in
  Smtml.Typed.Bitv8.concat msb lsb

let load_16_s m a =
  let open Symbolic_choice in
  let+ a = address a in
  let v = load_16_unchecked m a in
  Smtml.Typed.Bitv32.of_int16_s v

let load_16_u m a =
  let open Symbolic_choice in
  let+ a = address a in
  let v = load_16_unchecked m a in
  Smtml.Typed.Bitv32.of_int16_u v

let load_32_unchecked m a : Smtml.Typed.Bitv32.t =
  let low = load_16_unchecked m a in
  let high = load_16_unchecked m (Int32.add a 2l) in
  Smtml.Typed.Bitv16.concat high low

let load_32 m a =
  let open Symbolic_choice in
  let+ a = address a in
  let v = load_32_unchecked m a in
  Smtml.Typed.simplify v

let load_64_unchecked m a : Smtml.Typed.Bitv64.t =
  let low = load_32_unchecked m a in
  let high = load_32_unchecked m (Int32.add a 4l) in
  Smtml.Typed.Bitv32.concat high low

let load_64 m a =
  let open Symbolic_choice in
  let+ a = address a in
  load_64_unchecked m a

let load_128_unchecked m a : Smtml.Typed.Bitv128.t =
  let low = load_64_unchecked m a in
  let high = load_64_unchecked m (Int32.add a 8l) in
  Smtml.Typed.Bitv64.concat high low

let load_128 m a =
  let open Symbolic_choice in
  let+ a = address a in
  load_128_unchecked m a

let store_8 m ~addr v =
  let open Symbolic_choice in
  let+ addr = address addr in
  let data =
    replace_byte addr (Smtml.Typed.Bitv32.extract v ~high:7 ~low:0) m.data
  in
  { m with data }

let store_16 m ~addr v =
  let open Symbolic_choice in
  let+ addr = address addr in
  let data =
    replace_byte addr (Smtml.Typed.Bitv32.extract v ~high:7 ~low:0) m.data
    |> replace_byte (Int32.add addr 1l)
         (Smtml.Typed.Bitv32.extract v ~high:15 ~low:8)
  in
  { m with data }

let store_byte_list data start_addr bytes =
  let rec loop data offset = function
    | [] -> data
    | byte :: remaining ->
      let addr = Int32.add start_addr offset in
      let data = replace_byte addr byte data in
      loop data (Int32.add offset 1l) remaining
  in
  loop data 0l bytes

let store_32 m ~addr v =
  let open Symbolic_choice in
  let+ addr = address addr in
  let data = store_byte_list m.data addr (Smtml.Typed.Bitv32.to_bytes v) in
  { m with data }

let store_64 m ~(addr : Symbolic_i32.t) v =
  let open Symbolic_choice in
  let+ addr = address addr in
  let data = store_byte_list m.data addr (Smtml.Typed.Bitv64.to_bytes v) in
  { m with data }

let store_128 m ~(addr : Symbolic_i32.t) v =
  let open Symbolic_choice in
  let+ addr = address addr in
  let data = store_byte_list m.data addr (Smtml.Typed.Bitv128.to_bytes v) in
  { m with data }

let fill m ~(pos : Symbolic_i32.t) ~(len : Symbolic_i32.t) (c : char) =
  let open Symbolic_choice in
  let* len = select_i32 len in
  let len = Int32.to_int len in
  let* pos = select_i32 pos in
  let pos = Int32.to_int pos in
  let c = Symbolic_i32.of_int (int_of_char c) in

  let rec loop i m =
    if i = len then return m
    else
      let addr = Symbolic_i32.of_int (pos + i) in
      let* m = store_8 m ~addr c in
      loop (i + 1) m
  in
  loop 0 m

let blit ~src ~src_idx ~dst ~dst_idx ~len =
  let open Symbolic_choice in
  let* len = select_i32 len in
  let len = Int32.to_int len in
  let* src_idx = select_i32 src_idx in
  let src_idx = Int32.to_int src_idx in
  let* dst_idx = select_i32 dst_idx in
  let dst_idx = Int32.to_int dst_idx in

  let rec loop i dst =
    if i = len then return dst
    else
      let addr = Symbolic_i32.of_int (src_idx + i) in
      let* v = load_8_s src addr in
      let addr = Symbolic_i32.of_int (dst_idx + i) in
      let* dst = store_8 dst ~addr v in
      loop (i + 1) dst
  in
  loop 0 dst

let blit_string m str ~src ~dst ~len =
  (* This function is only used in memory init so everything will be concrete *)
  (* TODO: I am not sure this is true, this should be investigated and fixed at some point *)
  let open Symbolic_choice in
  let src = Smtml.Typed.Unsafe.unwrap src in
  let dst = Smtml.Typed.Unsafe.unwrap dst in
  let len = Smtml.Typed.Unsafe.unwrap len in
  let src = Int32.to_int @@ i32 src in
  let dst = Int32.to_int @@ i32 dst in
  let len = Int32.to_int @@ i32 len in
  let rec loop i m =
    if i = len then return m
    else
      let byte = Char.code @@ String.get str (src + i) in
      let addr = Symbolic_i32.of_int (dst + i) in
      let* m =
        store_8 m ~addr (Smtml.Typed.Bitv32.v (Smtml.Bitvector.of_int8 byte))
      in
      loop (i + 1) m
  in
  loop 0 m

let get_limit_max { limits; _ } =
  match limits with
  | I32 { max; _ } -> Option.map Int32.to_int max
  | I64 { max; _ } -> max

let get_min : Binary.Mem.Type.limits -> int = function
  | I32 { min; _ } -> Int32.to_int min
  | I64 { min; _ } -> min

let init limits =
  let size = get_min limits in
  { data = Map.empty; size = Symbolic_i32.of_int size; limits }

let get_limits { limits; _ } = limits