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

open Types
open Syntax

module StrType = struct
  type t = binary str_type

  let compare (x : t) (y : t) = Types.compare_str_type x y
end

module TypeMap = Map.Make (StrType)

let typemap (types : binary str_type Named.t) =
  Named.fold
    (fun idx typ acc -> TypeMap.add typ (Raw idx) acc)
    types TypeMap.empty

let find (named : 'a Named.t) : _ -> binary indice = function
  | Raw _i as indice -> indice
  | Text name -> (
    match String_map.find_opt name named.named with
    | None -> assert false
    | Some i -> Raw i )

let get error (named : 'a Named.t) indice : 'a Indexed.t Result.t =
  let (Raw i) = find named indice in
  (* TODO change Named.t structure to make that sensible *)
  match List.nth_opt named.values i with
  | None -> Error error
  | Some v -> Ok v

let find_global (modul : Assigned.t) id : binary indice = find modul.global id

let find_memory (modul : Assigned.t) id : binary indice = find modul.mem id

let rewrite_block_type (typemap : binary indice TypeMap.t) (modul : Assigned.t)
  (block_type : text block_type) : binary block_type Result.t =
  match block_type with
  | Bt_ind id -> begin
    let+ v = get (`Unknown_type id) modul.typ id in
    match Indexed.get v with
    | Def_func_t t' ->
      let idx = Indexed.get_index v in
      Bt_raw (Some (Raw idx), t')
    | _ -> assert false
  end
  | Bt_raw (_, func_type) ->
    let+ t = Binary_types.convert_func_type None func_type in
    let idx =
      match TypeMap.find_opt (Def_func_t t) typemap with
      | None -> assert false
      | Some idx -> idx
    in
    Bt_raw (Some idx, t)

let rewrite_expr (typemap : binary indice TypeMap.t) (modul : Assigned.t)
  (locals : binary param list) (iexpr : text expr) : binary expr Result.t =
  (* block_ids handling *)
  let block_id_to_raw (loop_count, block_ids) id =
    let* id =
      match id with
      | Text id -> begin
        match
          List.find_index
            (function Some id' -> String.equal id id' | None -> false)
            block_ids
        with
        | None -> Error (`Unknown_label (Text id))
        | Some id -> Ok id
      end
      | Raw id -> Ok id
    in
    (* this is > and not >= because you can `br 0` without any block to target the function *)
    if id > List.length block_ids + loop_count then
      Error (`Unknown_label (Raw id))
    else Ok (Raw id)
  in

  (* block_types handling *)
  let block_ty_opt_rewrite = function
    | Some bt ->
      let+ bt = rewrite_block_type typemap modul bt in
      Some bt
    | None -> Ok None
  in

  let* locals, _after_last_assigned_local =
    list_fold_left
      (fun (locals, next_free_int) ((name, _type) : binary param) ->
        match name with
        | None -> Ok (locals, next_free_int + 1)
        | Some name ->
          if String_map.mem name locals then Error (`Duplicate_local name)
          else Ok (String_map.add name next_free_int locals, next_free_int + 1) )
      (String_map.empty, 0) locals
  in

  let find_local = function
    | Raw _i as id -> id
    | Text name -> (
      match String_map.find_opt name locals with
      | None -> assert false
      | Some id -> Raw id )
  in

  let find_table id = find modul.table id in
  let find_func id = find modul.func id in
  let find_data id = find modul.data id in
  let find_elem id = find modul.elem id in
  let find_type id = find modul.typ id in

  let rec body (loop_count, block_ids) : text instr -> binary instr Result.t =
    function
    | Br_table (ids, id) ->
      let block_id_to_raw = block_id_to_raw (loop_count, block_ids) in
      let* ids = array_map block_id_to_raw ids in
      let+ id = block_id_to_raw id in
      Br_table (ids, id)
    | Br_if id ->
      let+ id = block_id_to_raw (loop_count, block_ids) id in
      Br_if id
    | Br id ->
      let+ id = block_id_to_raw (loop_count, block_ids) id in
      Br id
    | Call id ->
      let id = find_func id in
      Ok (Call id)
    | Return_call id ->
      let id = find_func id in
      Ok (Return_call id)
    | Local_set id ->
      let id = find_local id in
      Ok (Local_set id)
    | Local_get id ->
      let id = find_local id in
      Ok (Local_get id)
    | Local_tee id ->
      let id = find_local id in
      Ok (Local_tee id)
    | If_else (id, bt, e1, e2) ->
      let* bt = block_ty_opt_rewrite bt in
      let block_ids = id :: block_ids in
      let* e1 = expr e1 (loop_count, block_ids) in
      let+ e2 = expr e2 (loop_count, block_ids) in
      If_else (id, bt, e1, e2)
    | Loop (id, bt, e) ->
      let* bt = block_ty_opt_rewrite bt in
      let+ e = expr e (loop_count + 1, id :: block_ids) in
      Loop (id, bt, e)
    | Block (id, bt, e) ->
      let* bt = block_ty_opt_rewrite bt in
      let+ e = expr e (loop_count, id :: block_ids) in
      Block (id, bt, e)
    | Call_indirect (tbl_i, bt) ->
      let tbl_i = find_table tbl_i in
      let+ bt = rewrite_block_type typemap modul bt in
      Call_indirect (tbl_i, bt)
    | Return_call_indirect (tbl_i, bt) ->
      let tbl_i = find_table tbl_i in
      let+ bt = rewrite_block_type typemap modul bt in
      Return_call_indirect (tbl_i, bt)
    | Call_ref t ->
      let t = find_type t in
      Ok (Call_ref t)
    | Return_call_ref bt ->
      let+ bt = rewrite_block_type typemap modul bt in
      Return_call_ref bt
    | Global_set id ->
      let idx = find_global modul id in
      Ok (Global_set idx)
    | Global_get id ->
      let idx = find_global modul id in
      Ok (Global_get idx)
    | Ref_func id ->
      let id = find_func id in
      Ok (Ref_func id)
    | Table_size id ->
      let id = find_table id in
      Ok (Table_size id)
    | Table_get id ->
      let id = find_table id in
      Ok (Table_get id)
    | Table_set id ->
      let id = find_table id in
      Ok (Table_set id)
    | Table_grow id ->
      let id = find_table id in
      Ok (Table_grow id)
    | Table_init (i, i') ->
      let table = find_table i in
      let elem = find_elem i' in
      Ok (Table_init (table, elem))
    | Table_fill id ->
      let id = find_table id in
      Ok (Table_fill id)
    | Table_copy (i, i') ->
      let table = find_table i in
      let table' = find_table i' in
      Ok (Table_copy (table, table'))
    | Memory_init id ->
      let id = find_data id in
      Ok (Memory_init id)
    | Data_drop id ->
      let id = find_data id in
      Ok (Data_drop id)
    | Elem_drop id ->
      let id = find_elem id in
      Ok (Elem_drop id)
    | Select typ -> begin
      match typ with
      | None -> Ok (Select None)
      | Some [ t ] ->
        let+ t = Binary_types.convert_val_type None t in
        Select (Some [ t ])
      | Some [] | Some (_ :: _ :: _) -> Error `Invalid_result_arity
    end
    | Array_new_default id ->
      let id = find_type id in
      Ok (Array_new_default id)
    | Array_set id ->
      let id = find_type id in
      Ok (Array_set id)
    | Array_get id ->
      let id = find_type id in
      Ok (Array_set id)
    | Ref_null heap_type ->
      let+ t = Binary_types.convert_heap_type None heap_type in
      Ref_null t
    | Br_on_cast (i, t1, t2) ->
      let i = find_type i in
      let* t1 = Binary_types.convert_ref_type None t1 in
      let+ t2 = Binary_types.convert_ref_type None t2 in
      Br_on_cast (i, t1, t2)
    | Br_on_cast_fail (i, null, ht) ->
      let i = find_type i in
      let+ ht = Binary_types.convert_heap_type None ht in
      Br_on_cast_fail (i, null, ht)
    | Struct_new_default i ->
      let i = find_type i in
      Ok (Struct_new_default i)
    | Ref_cast (null, ht) ->
      let+ ht = Binary_types.convert_heap_type None ht in
      Ref_cast (null, ht)
    | Ref_test (null, ht) ->
      let+ ht = Binary_types.convert_heap_type None ht in
      Ref_test (null, ht)
    | ( I_unop _ | I_binop _ | I_testop _ | I_relop _ | F_unop _ | F_relop _
      | I32_wrap_i64 | F_reinterpret_i _ | I_reinterpret_f _ | I64_extend_i32 _
      | I64_extend32_s | F32_demote_f64 | I_extend8_s _ | I_extend16_s _
      | F64_promote_f32 | F_convert_i _ | I_trunc_f _ | I_trunc_sat_f _
      | Ref_is_null | F_binop _ | F32_const _ | F64_const _ | I32_const _
      | I64_const _ | Unreachable | Drop | Nop | Return | Ref_i31 | I31_get_s
      | I31_get_u | Array_len | Ref_as_non_null | Extern_externalize
      | Extern_internalize | Ref_eq | I_load8 _ | I_store8 _ | I_load16 _
      | I_store16 _ | I64_load32 _ | I64_store32 _ | I_load _ | F_load _
      | F_store _ | I_store _ | Memory_copy | Memory_size | Memory_fill
      | Memory_grow ) as i ->
      Ok i
    | ( Array_new_data _ | Array_new _ | Array_new_elem _ | Array_new_fixed _
      | Array_get_u _ | Struct_get _ | Struct_get_s _ | Struct_set _
      | Struct_new _ | Br_on_non_null _ | Br_on_null _ ) as _i ->
      assert false
  and expr (e : text expr) (loop_count, block_ids) : binary expr Result.t =
    list_map (fun i -> body (loop_count, block_ids) i) e
  in
  expr iexpr (0, [])

let rewrite_global (typemap : binary indice TypeMap.t) (modul : Assigned.t)
  (global : Text.global) : Binary.global Result.t =
  let* init = rewrite_expr typemap modul [] global.init in
  let mut, val_type = global.typ in
  let+ val_type = Binary_types.convert_val_type None val_type in
  let typ = (mut, val_type) in
  { Binary.id = global.id; init; typ }

let rewrite_elem (typemap : binary indice TypeMap.t) (modul : Assigned.t)
  (elem : Text.elem) : Binary.elem Result.t =
  let* (mode : Binary.elem_mode) =
    match elem.mode with
    | Elem_declarative -> Ok Binary.Elem_declarative
    | Elem_passive -> Ok Elem_passive
    | Elem_active (None, _expr) -> assert false
    | Elem_active (Some id, expr) ->
      let (Raw indice) = find modul.table id in
      let+ expr = rewrite_expr typemap modul [] expr in
      Binary.Elem_active (Some indice, expr)
  in
  let* init = list_map (rewrite_expr typemap modul []) elem.init in
  let+ typ = Binary_types.convert_ref_type None elem.typ in
  { Binary.init; mode; id = elem.id; typ }

let rewrite_data (typemap : binary indice TypeMap.t) (modul : Assigned.t)
  (data : Text.data) : Binary.data Result.t =
  let+ mode =
    match data.mode with
    | Data_passive -> Ok Binary.Data_passive
    | Data_active (None, _expr) -> assert false
    | Data_active (Some indice, expr) ->
      let (Raw indice) = find_memory modul indice in
      let+ expr = rewrite_expr typemap modul [] expr in
      Binary.Data_active (indice, expr)
  in
  { Binary.mode; id = data.id; init = data.init }

let rewrite_export named (exports : Grouped.opt_export list) :
  Binary.export list =
  List.map
    (fun { Grouped.name; id } ->
      let (Raw id) = find named id in
      { Binary.name; id } )
    exports

let rewrite_exports (modul : Assigned.t) (exports : Grouped.opt_exports) :
  Binary.exports =
  let global = rewrite_export modul.global exports.global in
  let mem = rewrite_export modul.mem exports.mem in
  let table = rewrite_export modul.table exports.table in
  let func = rewrite_export modul.func exports.func in
  { Binary.global; mem; table; func }

let rewrite_func (typemap : binary indice TypeMap.t) (modul : Assigned.t)
  ({ id; type_f; locals; body; _ } : text func) : binary func Result.t =
  let* (Bt_raw (_, (params, _)) as type_f) =
    rewrite_block_type typemap modul type_f
  in
  let* locals = list_map (Binary_types.convert_param None) locals in
  let+ body = rewrite_expr typemap modul (params @ locals) body in
  { body; type_f; id; locals }

let rewrite_import (f : 'a -> 'b Result.t) (import : 'a Imported.t) :
  'b Imported.t Result.t =
  let+ desc = f import.desc in
  { import with desc }

let rewrite_runtime f g r =
  match r with
  | Runtime.Local v ->
    let+ v = f v in
    Runtime.Local v
  | Imported i ->
    let+ i = g i in
    Runtime.Imported i

let rewrite_named f named =
  let+ values =
    list_map
      (fun ind ->
        let index = Indexed.get_index ind in
        let value = Indexed.get ind in
        let+ value = f value in
        Indexed.return index value )
      named.Named.values
  in
  { named with Named.values }

let rewrite_types (_modul : Assigned.t) (t : binary str_type) :
  binary rec_type Result.t =
  (* TODO: the input `t` should actually be a `binary rec_type` *)
  let t = [ (None, (Final, [], t)) ] in
  Ok t

let rec rewrite_term ~(binder_list : string option list) ~(modul : Assigned.t)
  ~(func_param_list : string option list) :
  text Spec.term -> binary Spec.term Result.t =
  let rec find_raw_indice error acc id = function
    | [] -> Error error
    | Some id' :: bl ->
      if String.equal id id' then Ok (Raw acc)
      else find_raw_indice error (acc + 1) id bl
    | None :: bl -> find_raw_indice error (acc + 1) id bl
  in

  let find_binder (binder_list : string option list) (ind : text indice) :
    binary indice Result.t =
    match ind with
    | Raw id -> Ok (Raw id)
    | Text id -> find_raw_indice (`Spec_unknown_binder ind) 0 id binder_list
  in

  let find_param (func_param_list : string option list) (ind : text indice) :
    binary indice Result.t =
    match ind with
    | Raw id -> Ok (Raw id)
    | Text id -> find_raw_indice (`Spec_unknown_param ind) 0 id func_param_list
  in

  let find_global (modul : Assigned.t) (ind : text indice) :
    binary indice Result.t =
    match ind with
    | Raw id -> Ok (Raw id)
    | Text id -> (
      match String_map.find_opt id modul.global.named with
      | None -> Error (`Unknown_global ind)
      | Some i -> Ok (Raw i) )
  in

  let open Spec in
  function
  | Int32 i32 -> Ok (Int32 i32)
  | Int64 i64 -> Ok (Int64 i64)
  | Float32 f32 -> Ok (Float32 f32)
  | Float64 f64 -> Ok (Float64 f64)
  | Var ind -> (
    match
      ( find_binder binder_list ind
      , find_param func_param_list ind
      , find_global modul ind )
    with
    | Ok ind, _, _ -> Ok (BinderVar ind)
    | _, Ok ind, _ -> Ok (ParamVar ind)
    | _, _, Ok ind -> Ok (GlobalVar ind)
    | _, _, _ -> Error (`Spec_unknown_variable ind) )
  | ParamVar ind ->
    let+ ind = find_param func_param_list ind in
    ParamVar ind
  | GlobalVar ind ->
    let+ ind = find_global modul ind in
    GlobalVar ind
  | BinderVar ind ->
    let+ ind = find_binder binder_list ind in
    BinderVar ind
  | UnOp (u, tm1) ->
    let+ tm1 = rewrite_term ~binder_list ~modul ~func_param_list tm1 in
    UnOp (u, tm1)
  | BinOp (b, tm1, tm2) ->
    let* tm1 = rewrite_term ~binder_list ~modul ~func_param_list tm1 in
    let+ tm2 = rewrite_term ~binder_list ~modul ~func_param_list tm2 in
    BinOp (b, tm1, tm2)
  | Result i -> Ok (Result i)
  | Memory tm1 ->
    let+ tm1 = rewrite_term ~binder_list ~modul ~func_param_list tm1 in
    Memory tm1

let rec rewrite_prop ~(binder_list : string option list) ~(modul : Assigned.t)
  ~(func_param_list : string option list) :
  text Spec.prop -> binary Spec.prop Result.t =
  let open Spec in
  function
  | Const b -> Ok (Const b)
  | BinPred (b, tm1, tm2) ->
    let* tm1 = rewrite_term ~binder_list ~modul ~func_param_list tm1 in
    let+ tm2 = rewrite_term ~binder_list ~modul ~func_param_list tm2 in
    BinPred (b, tm1, tm2)
  | UnConnect (u, pr1) ->
    let+ pr1 = rewrite_prop ~binder_list ~modul ~func_param_list pr1 in
    UnConnect (u, pr1)
  | BinConnect (b, pr1, pr2) ->
    let* pr1 = rewrite_prop ~binder_list ~modul ~func_param_list pr1 in
    let+ pr2 = rewrite_prop ~binder_list ~modul ~func_param_list pr2 in
    BinConnect (b, pr1, pr2)
  | Binder (b, bt, id_opt, pr1) ->
    let binder_list = id_opt :: binder_list in
    let+ pr1 = rewrite_prop ~binder_list ~modul ~func_param_list pr1 in
    Binder (b, bt, id_opt, pr1)

let rewrite_contract (modul : Assigned.t) :
  text Contract.t -> binary Contract.t Result.t =
 fun { Contract.funcid; preconditions; postconditions } ->
  let funcid = find modul.func funcid in
  let* func =
    let (Raw i) = funcid in
    match Indexed.get_at i modul.func.values with
    | Some v -> Ok v
    | None -> Error (`Spec_invalid_indice (Int.to_string i))
  in
  let func_bt =
    match func with
    | Local { type_f; _ } -> type_f
    | Imported { desc; _ } -> desc
  in
  let* func_param_list =
    match func_bt with
    | Bt_ind ind -> (
      let (Raw i) = find modul.typ ind in
      match Indexed.get_at i modul.typ.values with
      | Some (Def_func_t (func_pt, _)) -> Ok (List.map fst func_pt)
      | _ -> Error (`Spec_invalid_indice (Int.to_string i)) )
    | Bt_raw (_, (func_pt, _)) -> Ok (List.map fst func_pt)
  in
  let* preconditions =
    list_map
      (rewrite_prop ~binder_list:[] ~modul ~func_param_list)
      preconditions
  in
  let+ postconditions =
    list_map
      (rewrite_prop ~binder_list:[] ~modul ~func_param_list)
      postconditions
  in
  { Contract.funcid; preconditions; postconditions }

let rewrite_annot (modul : Assigned.t) :
  text Annot.annot -> Binary.custom Result.t = function
  | Contract contract ->
    let+ contract = rewrite_contract modul contract in
    Binary.From_annot (Contract contract)
  | Annot annot -> ok @@ Binary.From_annot (Annot annot)

let rewrite_annots (modul : Assigned.t) :
  text Annot.annot list -> Binary.custom list Result.t =
  list_map (rewrite_annot modul)

let modul (modul : Assigned.t) : Binary.modul Result.t =
  Log.debug0 "rewriting    ...@\n";
  let typemap = typemap modul.typ in
  let* global =
    let+ { Named.named; values } =
      rewrite_named
        (rewrite_runtime (rewrite_global typemap modul) ok)
        modul.global
    in
    let values = List.rev values in
    { Named.named; values }
  in
  let* elem = rewrite_named (rewrite_elem typemap modul) modul.elem in
  let* data = rewrite_named (rewrite_data typemap modul) modul.data in
  let exports = rewrite_exports modul modul.exports in
  let* func =
    let import = rewrite_import (rewrite_block_type typemap modul) in
    let runtime = rewrite_runtime (rewrite_func typemap modul) import in
    rewrite_named runtime modul.func
  in
  let* types = rewrite_named (rewrite_types modul) modul.typ in
  let* start =
    match modul.start with
    | None -> Ok None
    | Some id ->
      let (Raw id) = find func id in
      Ok (Some id)
  in
  let+ custom = rewrite_annots modul modul.annots in

  let id = modul.id in
  let mem = Named.to_array modul.mem in
  let table = Named.to_array modul.table in
  let types = Named.to_array types in
  let global = Named.to_array global in
  let elem = Named.to_array elem in
  let data = Named.to_array data in
  let func = Named.to_array func in

  { Binary.id
  ; mem
  ; table
  ; types
  ; global
  ; elem
  ; data
  ; exports
  ; func
  ; start
  ; custom
  }