https://gitlab.com/tezos/tezos
Raw File
Tip revision: e17061a0b356817e120198e7eb3b21cc189a1ebb authored by martoon on 15 September 2023, 21:39:34 UTC
MIR: Describe how to test rollup in deployment
Tip revision: e17061a
script_interpreter_logging.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com>                *)
(* Copyright (c) 2022 DaiLambda, Inc. <contact@dailambda,jp>                 *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

open Protocol
open Environment
open Error_monad
open Alpha_context
open Script_typed_ir

module Stack_utils = struct
  type kinstr_rewritek = {
    apply :
      'b 'u 'r 'f.
      ('b, 'u) stack_ty -> ('b, 'u, 'r, 'f) kinstr -> ('b, 'u, 'r, 'f) kinstr;
  }
  [@@ocaml.unboxed]

  (* An existential wrapper around failed [kinstr], whose final stack type
     is hidden as it is irrelevant. *)
  type ('a, 's) failed_kinstr_cast = {cast : 'b 'u. ('a, 's, 'b, 'u) kinstr}
  [@@ocaml.unboxed]

  (* This is a view on a deconstructed [kinstr]. Its type parameters refer to
     the type of the viewed [kinstr], while existentials inside describe types of
     [kinstr]'s components. The [reconstruct] field in each record stores a
     function which reconstructs the original instruction from its components. *)
  type ('a, 's, 'r, 'f) ex_split_kinstr =
    | Ex_split_kinstr : {
        cont_init_stack : ('b, 'u) stack_ty;
        continuation : ('b, 'u, 'r, 'f) kinstr;
        reconstruct : ('b, 'u, 'r, 'f) kinstr -> ('a, 's, 'r, 'f) kinstr;
      }
        -> ('a, 's, 'r, 'f) ex_split_kinstr
    | Ex_split_log : {
        stack : ('a, 's) stack_ty;
        continuation : ('a, 's, 'r, 'f) kinstr;
        reconstruct : ('a, 's, 'r, 'f) kinstr -> ('a, 's, 'r, 'f) kinstr;
      }
        -> ('a, 's, 'r, 'f) ex_split_kinstr
    | Ex_split_loop_may_fail : {
        body_init_stack : ('b, 'u) stack_ty;
        body : ('b, 'u, 'r, 'f) kinstr;
        cont_init_stack : ('c, 'v) stack_ty;
        continuation : ('c, 'v, 't, 'g) kinstr;
        reconstruct :
          ('b, 'u, 'r, 'f) kinstr ->
          ('c, 'v, 't, 'g) kinstr ->
          ('a, 's, 't, 'g) kinstr;
      }
        -> ('a, 's, 't, 'g) ex_split_kinstr
    | Ex_split_loop_may_not_fail : {
        body_init_stack : ('b, 'u) stack_ty;
        body : ('b, 'u, 'r, 'f) kinstr;
        continuation : ('c, 'v, 't, 'g) kinstr;
        aft_body_stack_transform :
          ('r, 'f) stack_ty -> ('c, 'v) stack_ty tzresult;
        reconstruct :
          ('b, 'u, 'r, 'f) kinstr ->
          ('c, 'v, 't, 'g) kinstr ->
          ('a, 's, 't, 'g) kinstr;
      }
        -> ('a, 's, 't, 'g) ex_split_kinstr
    | Ex_split_if : {
        left_init_stack : ('b, 'u) stack_ty;
        left_branch : ('b, 'u, 'r, 'f) kinstr;
        right_init_stack : ('c, 'v) stack_ty;
        right_branch : ('c, 'v, 'r, 'f) kinstr;
        continuation : ('r, 'f, 't, 'g) kinstr;
        reconstruct :
          ('b, 'u, 'r, 'f) kinstr ->
          ('c, 'v, 'r, 'f) kinstr ->
          ('r, 'f, 't, 'g) kinstr ->
          ('a, 's, 't, 'g) kinstr;
      }
        -> ('a, 's, 't, 'g) ex_split_kinstr
    | Ex_split_halt : Script.location -> ('a, 's, 'a, 's) ex_split_kinstr
    | Ex_split_failwith : {
        location : Script.location;
        arg_ty : ('a, _) ty;
        cast : ('a, 's) failed_kinstr_cast;
      }
        -> ('a, 's, 'r, 'f) ex_split_kinstr

  (** An existential container for an instruction paired with its
    initial stack type. This is used internally to pack together
    execution branches with different initial stack types but
    the same final stack type (which we want to compute). *)
  type ('r, 'f) ex_init_stack_ty =
    | Ex_init_stack_ty :
        ('a, 's) stack_ty * ('a, 's, 'r, 'f) kinstr
        -> ('r, 'f) ex_init_stack_ty

  let rec stack_prefix_preservation_witness_split_input :
      type a s b t c u d v.
      (b, t, c, u, a, s, d, v) stack_prefix_preservation_witness ->
      (a, s) stack_ty ->
      (b, t) stack_ty =
   fun w s ->
    match (w, s) with
    | KPrefix (_, _, w), Item_t (_, s) ->
        stack_prefix_preservation_witness_split_input w s
    | KRest, s -> s

  let rec stack_prefix_preservation_witness_split_output :
      type a s b t c u d v.
      (b, t, c, u, a, s, d, v) stack_prefix_preservation_witness ->
      (c, u) stack_ty ->
      (d, v) stack_ty =
   fun w s ->
    match (w, s) with
    | KPrefix (_, a, w), s ->
        Item_t (a, stack_prefix_preservation_witness_split_output w s)
    | KRest, s -> s

  (* We apply this function to optional type information which must be present
     if functions from this module were called. Use with care. *)
  let assert_some = function None -> assert false | Some x -> x

  let kinstr_split :
      type a s r f.
      (a, s) stack_ty ->
      (a, s, r, f) kinstr ->
      (a, s, r, f) ex_split_kinstr tzresult =
    let open Result_syntax in
    fun s i ->
      let dummy = Micheline.dummy_location in
      match (i, s) with
      | IDrop (loc, k), Item_t (_a, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IDrop (loc, k));
               }
      | IDup (loc, k), Item_t (a, s) ->
          let s = Item_t (a, Item_t (a, s)) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IDup (loc, k));
               }
      | ISwap (loc, k), Item_t (a, Item_t (b, s)) ->
          let s = Item_t (b, Item_t (a, s)) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISwap (loc, k));
               }
      | IPush (loc, a, x, k), s ->
          let s = Item_t (a, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IPush (loc, a, x, k));
               }
      | IUnit (loc, k), s ->
          let s = Item_t (unit_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IUnit (loc, k));
               }
      | ICons_pair (loc, k), Item_t (a, Item_t (b, s)) ->
          let+ (Ty_ex_c c) = pair_t dummy a b in
          let s = Item_t (c, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ICons_pair (loc, k));
            }
      | ICar (loc, k), Item_t (Pair_t (a, _b, _meta, _), s) ->
          let s = Item_t (a, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ICar (loc, k));
               }
      | ICdr (loc, k), Item_t (Pair_t (_a, b, _meta, _), s) ->
          let s = Item_t (b, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ICdr (loc, k));
               }
      | IUnpair (loc, k), Item_t (Pair_t (a, b, _meta, _), s) ->
          let s = Item_t (a, Item_t (b, s)) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IUnpair (loc, k));
               }
      | ICons_some (loc, k), Item_t (a, s) ->
          let+ o = option_t dummy a in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ICons_some (loc, k));
            }
      | ICons_none (loc, a, k), s ->
          let+ o = option_t dummy a in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ICons_none (loc, a, k));
            }
      | ( IIf_none {loc; branch_if_none; branch_if_some; k},
          Item_t (Option_t (a, _meta, _), s) ) ->
          return
          @@ Ex_split_if
               {
                 left_init_stack = s;
                 left_branch = branch_if_none;
                 right_init_stack = Item_t (a, s);
                 right_branch = branch_if_some;
                 continuation = k;
                 reconstruct =
                   (fun branch_if_none branch_if_some k ->
                     IIf_none {loc; branch_if_none; branch_if_some; k});
               }
      | IOpt_map {loc; body; k}, Item_t (Option_t (a, _meta, _), s) ->
          return
          @@ Ex_split_loop_may_not_fail
               {
                 body_init_stack = Item_t (a, s);
                 body;
                 continuation = k;
                 aft_body_stack_transform =
                   (function
                   | Item_t (b, s) ->
                       let+ o = option_t dummy b in
                       Item_t (o, s));
                 reconstruct = (fun body k -> IOpt_map {loc; body; k});
               }
      | ICons_left (loc, b, k), Item_t (a, s) ->
          let+ (Ty_ex_c c) = or_t dummy a b in
          let s = Item_t (c, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ICons_left (loc, b, k));
            }
      | ICons_right (loc, a, k), Item_t (b, s) ->
          let+ (Ty_ex_c c) = or_t dummy a b in
          let s = Item_t (c, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ICons_right (loc, a, k));
            }
      | ( IIf_left {loc; branch_if_left; branch_if_right; k},
          Item_t (Or_t (a, b, _meta, _), s) ) ->
          return
          @@ Ex_split_if
               {
                 left_init_stack = Item_t (a, s);
                 left_branch = branch_if_left;
                 right_init_stack = Item_t (b, s);
                 right_branch = branch_if_right;
                 continuation = k;
                 reconstruct =
                   (fun branch_if_left branch_if_right k ->
                     IIf_left {loc; branch_if_left; branch_if_right; k});
               }
      | ICons_list (loc, k), Item_t (_a, Item_t (l, s)) ->
          let s = Item_t (l, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ICons_list (loc, k));
               }
      | INil (loc, a, k), s ->
          let+ l = list_t dummy a in
          let s = Item_t (l, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> INil (loc, a, k));
            }
      | ( IIf_cons {loc; branch_if_cons; branch_if_nil; k},
          Item_t ((List_t (a, _meta) as l), s) ) ->
          return
          @@ Ex_split_if
               {
                 left_init_stack = Item_t (a, Item_t (l, s));
                 left_branch = branch_if_cons;
                 right_init_stack = s;
                 right_branch = branch_if_nil;
                 continuation = k;
                 reconstruct =
                   (fun branch_if_cons branch_if_nil k ->
                     IIf_cons {loc; branch_if_cons; branch_if_nil; k});
               }
      | IList_map (loc, body, ty, k), Item_t (List_t (a, _meta), s) ->
          let s = Item_t (a, s) in
          return
          @@ Ex_split_loop_may_not_fail
               {
                 body_init_stack = s;
                 body;
                 continuation = k;
                 aft_body_stack_transform =
                   (function
                   | Item_t (b, s) ->
                       let+ l = list_t dummy b in
                       Item_t (l, s));
                 reconstruct = (fun body k -> IList_map (loc, body, ty, k));
               }
      | IList_iter (loc, ty, body, k), Item_t (List_t (a, _meta), s) ->
          return
          @@ Ex_split_loop_may_fail
               {
                 body_init_stack = Item_t (a, s);
                 body;
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun body k -> IList_iter (loc, ty, body, k));
               }
      | IList_size (loc, k), Item_t (_l, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IList_size (loc, k));
               }
      | IEmpty_set (loc, a, k), s ->
          let+ b = set_t dummy a in
          let s = Item_t (b, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IEmpty_set (loc, a, k));
            }
      | ISet_iter (loc, a, body, k), Item_t (_b, s) ->
          return
          @@ Ex_split_loop_may_fail
               {
                 body_init_stack = Item_t (assert_some a, s);
                 body;
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun body k -> ISet_iter (loc, a, body, k));
               }
      | ISet_mem (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISet_mem (loc, k));
               }
      | ISet_update (loc, k), Item_t (_, Item_t (_, s)) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISet_update (loc, k));
               }
      | ISet_size (loc, k), Item_t (_, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISet_size (loc, k));
               }
      | IEmpty_map (loc, cty, vty, k), s ->
          let+ m = map_t dummy cty (assert_some vty) in
          let s = Item_t (m, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IEmpty_map (loc, cty, vty, k));
            }
      | IMap_map (loc, ty, body, k), Item_t (Map_t (kty, vty, _meta), s) ->
          let (Map_t (key_ty, _, _)) = assert_some ty in
          let+ (Ty_ex_c p) = pair_t dummy key_ty vty in
          Ex_split_loop_may_not_fail
            {
              body_init_stack = Item_t (p, s);
              body;
              continuation = k;
              aft_body_stack_transform =
                (fun (Item_t (b, s)) ->
                  let+ m = map_t dummy kty b in
                  Item_t (m, s));
              reconstruct = (fun body k -> IMap_map (loc, ty, body, k));
            }
      | IMap_iter (loc, kvty, body, k), Item_t (_, stack) ->
          return
          @@ Ex_split_loop_may_fail
               {
                 body_init_stack = Item_t (assert_some kvty, stack);
                 body;
                 cont_init_stack = stack;
                 continuation = k;
                 reconstruct = (fun body k -> IMap_iter (loc, kvty, body, k));
               }
      | IMap_mem (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMap_mem (loc, k));
               }
      | IMap_get (loc, k), Item_t (_, Item_t (Map_t (_kty, vty, _meta), s)) ->
          let+ o = option_t dummy vty in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IMap_get (loc, k));
            }
      | IMap_update (loc, k), Item_t (_, Item_t (_, s)) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMap_update (loc, k));
               }
      | IMap_get_and_update (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMap_get_and_update (loc, k));
               }
      | IMap_size (loc, k), Item_t (_, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMap_size (loc, k));
               }
      | IEmpty_big_map (loc, cty, ty, k), s ->
          let+ b = big_map_t dummy cty ty in
          let s = Item_t (b, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IEmpty_big_map (loc, cty, ty, k));
            }
      | IBig_map_mem (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBig_map_mem (loc, k));
               }
      | ( IBig_map_get (loc, k),
          Item_t (_, Item_t (Big_map_t (_kty, vty, _meta), s)) ) ->
          let+ o = option_t dummy vty in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IBig_map_get (loc, k));
            }
      | IBig_map_update (loc, k), Item_t (_, Item_t (_, s)) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBig_map_update (loc, k));
               }
      | IBig_map_get_and_update (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBig_map_get_and_update (loc, k));
               }
      | IConcat_string (loc, k), Item_t (_, s) ->
          let s = Item_t (string_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IConcat_string (loc, k));
               }
      | IConcat_string_pair (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IConcat_string_pair (loc, k));
               }
      | ISlice_string (loc, k), Item_t (_, Item_t (_, Item_t (_, s))) ->
          let s = Item_t (option_string_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISlice_string (loc, k));
               }
      | IString_size (loc, k), Item_t (_, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IString_size (loc, k));
               }
      | IConcat_bytes (loc, k), Item_t (_, s) ->
          let s = Item_t (bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IConcat_bytes (loc, k));
               }
      | IConcat_bytes_pair (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IConcat_bytes_pair (loc, k));
               }
      | ISlice_bytes (loc, k), Item_t (_, Item_t (_, Item_t (_, s))) ->
          let s = Item_t (option_bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISlice_bytes (loc, k));
               }
      | IBytes_size (loc, k), Item_t (_, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBytes_size (loc, k));
               }
      | ILsl_bytes (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ILsl_bytes (loc, k));
               }
      | ILsr_bytes (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ILsr_bytes (loc, k));
               }
      | IOr_bytes (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IOr_bytes (loc, k));
               }
      | IAnd_bytes (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAnd_bytes (loc, k));
               }
      | IXor_bytes (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IXor_bytes (loc, k));
               }
      | INot_bytes (loc, k), Item_t (_, s) ->
          let s = Item_t (bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INot_bytes (loc, k));
               }
      | IBytes_nat (loc, k), Item_t (_, s) ->
          let s = Item_t (bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBytes_nat (loc, k));
               }
      | INat_bytes (loc, k), Item_t (_, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INat_bytes (loc, k));
               }
      | IBytes_int (loc, k), Item_t (_, s) ->
          let s = Item_t (bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBytes_int (loc, k));
               }
      | IInt_bytes (loc, k), Item_t (_, s) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IInt_bytes (loc, k));
               }
      | IAdd_seconds_to_timestamp (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_seconds_to_timestamp (loc, k));
               }
      | IAdd_timestamp_to_seconds (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (timestamp_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_timestamp_to_seconds (loc, k));
               }
      | ISub_timestamp_seconds (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (timestamp_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISub_timestamp_seconds (loc, k));
               }
      | IDiff_timestamps (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IDiff_timestamps (loc, k));
               }
      | IAdd_tez (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_tez (loc, k));
               }
      | ISub_tez (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (option_mutez_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISub_tez (loc, k));
               }
      | ISub_tez_legacy (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISub_tez_legacy (loc, k));
               }
      | IMul_teznat (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (mutez_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_teznat (loc, k));
               }
      | IMul_nattez (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_nattez (loc, k));
               }
      | IEdiv_teznat (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (option_pair_mutez_mutez_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IEdiv_teznat (loc, k));
               }
      | IEdiv_tez (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (option_pair_nat_mutez_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IEdiv_tez (loc, k));
               }
      | IOr (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IOr (loc, k));
               }
      | IAnd (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAnd (loc, k));
               }
      | IXor (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IXor (loc, k));
               }
      | INot (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INot (loc, k));
               }
      | IIs_nat (loc, k), Item_t (_, s) ->
          let s = Item_t (option_nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IIs_nat (loc, k));
               }
      | INeg (loc, k), Item_t (_, s) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INeg (loc, k));
               }
      | IAbs_int (loc, k), Item_t (_, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAbs_int (loc, k));
               }
      | IInt_nat (loc, k), Item_t (_, s) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IInt_nat (loc, k));
               }
      | IAdd_int (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_int (loc, k));
               }
      | IAdd_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_nat (loc, k));
               }
      | ISub_int (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISub_int (loc, k));
               }
      | IMul_int (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_int (loc, k));
               }
      | IMul_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_nat (loc, k));
               }
      | IEdiv_int (loc, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (option_pair_int_nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IEdiv_int (loc, k));
               }
      | IEdiv_nat (loc, k), Item_t (_, Item_t (a, s)) ->
          let* (Ty_ex_c p) = pair_t dummy a nat_t in
          let+ o = option_t dummy p in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IEdiv_nat (loc, k));
            }
      | ILsl_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ILsl_nat (loc, k));
               }
      | ILsr_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ILsr_nat (loc, k));
               }
      | IOr_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IOr_nat (loc, k));
               }
      | IAnd_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAnd_nat (loc, k));
               }
      | IAnd_int_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAnd_int_nat (loc, k));
               }
      | IXor_nat (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IXor_nat (loc, k));
               }
      | INot_int (loc, k), Item_t (_, s) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INot_int (loc, k));
               }
      | IIf {loc; branch_if_true; branch_if_false; k}, Item_t (_, s) ->
          return
          @@ Ex_split_if
               {
                 left_init_stack = s;
                 left_branch = branch_if_true;
                 right_init_stack = s;
                 right_branch = branch_if_false;
                 continuation = k;
                 reconstruct =
                   (fun branch_if_true branch_if_false k ->
                     IIf {loc; branch_if_true; branch_if_false; k});
               }
      | ILoop (loc, body, k), Item_t (_, s) ->
          return
          @@ Ex_split_loop_may_fail
               {
                 body_init_stack = s;
                 body;
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun body k -> ILoop (loc, body, k));
               }
      | ILoop_left (loc, kl, kr), Item_t (Or_t (a, b, _meta, _), s) ->
          return
          @@ Ex_split_loop_may_fail
               {
                 body_init_stack = Item_t (a, s);
                 body = kl;
                 cont_init_stack = Item_t (b, s);
                 continuation = kr;
                 reconstruct = (fun kl kr -> ILoop_left (loc, kl, kr));
               }
      | IDip (loc, body, ty, k), Item_t (a, s) ->
          return
          @@ Ex_split_loop_may_not_fail
               {
                 body_init_stack = s;
                 body;
                 continuation = k;
                 aft_body_stack_transform = (fun s -> return (Item_t (a, s)));
                 reconstruct = (fun body k -> IDip (loc, body, ty, k));
               }
      | IExec (loc, sty, k), Item_t (_, Item_t (Lambda_t (_, b, _meta), s)) ->
          let s = Item_t (b, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IExec (loc, sty, k));
               }
      | ( IApply (loc, ty, k),
          Item_t (_, Item_t (Lambda_t (Pair_t (_, a, _, _), b, _), s)) ) ->
          let+ l = lambda_t dummy a b in
          let s = Item_t (l, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IApply (loc, ty, k));
            }
      | ILambda (loc, (Lam (desc, _) as l), k), s ->
          let (Item_t (a, Bot_t)) = desc.kbef in
          let (Item_t (b, Bot_t)) = desc.kaft in
          let+ lam = lambda_t dummy a b in
          let s = Item_t (lam, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ILambda (loc, l, k));
            }
      | ILambda (loc, (LamRec (desc, _) as l), k), s ->
          let (Item_t (a, Item_t (Lambda_t _, Bot_t))) = desc.kbef in
          let (Item_t (b, Bot_t)) = desc.kaft in
          let+ lam = lambda_t dummy a b in
          let s = Item_t (lam, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ILambda (loc, l, k));
            }
      | IFailwith (location, arg_ty), _ ->
          return
          @@ Ex_split_failwith
               {location; arg_ty; cast = {cast = IFailwith (location, arg_ty)}}
      | ICompare (loc, ty, k), Item_t (_, Item_t (_, s)) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ICompare (loc, ty, k));
               }
      | IEq (loc, k), Item_t (_, s) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IEq (loc, k));
               }
      | INeq (loc, k), Item_t (_, s) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INeq (loc, k));
               }
      | ILt (loc, k), Item_t (_, s) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ILt (loc, k));
               }
      | IGt (loc, k), Item_t (_, s) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IGt (loc, k));
               }
      | ILe (loc, k), Item_t (_, s) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ILe (loc, k));
               }
      | IGe (loc, k), Item_t (_, s) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IGe (loc, k));
               }
      | IAddress (loc, k), Item_t (_, s) ->
          let s = Item_t (address_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAddress (loc, k));
               }
      | IContract (loc, ty, code, k), Item_t (_, s) ->
          let* c = contract_t dummy ty in
          let+ o = option_t dummy c in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IContract (loc, ty, code, k));
            }
      | ITransfer_tokens (loc, k), Item_t (_, Item_t (_, Item_t (_, s))) ->
          let s = Item_t (operation_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ITransfer_tokens (loc, k));
               }
      | ( IView (loc, (View_signature {output_ty; _} as view_signature), sty, k),
          Item_t (_, Item_t (_, s)) ) ->
          let+ b = option_t dummy output_ty in
          let s = Item_t (b, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IView (loc, view_signature, sty, k));
            }
      | IImplicit_account (loc, k), Item_t (_, s) ->
          let s = Item_t (contract_unit_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IImplicit_account (loc, k));
               }
      | ( ICreate_contract {loc; storage_type; code; k},
          Item_t (_, Item_t (_, Item_t (_, s))) ) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = Item_t (operation_t, Item_t (address_t, s));
                 continuation = k;
                 reconstruct =
                   (fun k -> ICreate_contract {loc; storage_type; code; k});
               }
      | ISet_delegate (loc, k), Item_t (_, s) ->
          let s = Item_t (operation_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISet_delegate (loc, k));
               }
      | INow (loc, k), s ->
          let s = Item_t (timestamp_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INow (loc, k));
               }
      | IBalance (loc, k), s ->
          let s = Item_t (mutez_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBalance (loc, k));
               }
      | ILevel (loc, k), s ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ILevel (loc, k));
               }
      | ICheck_signature (loc, k), Item_t (_, Item_t (_, Item_t (_, s))) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ICheck_signature (loc, k));
               }
      | IHash_key (loc, k), Item_t (_, s) ->
          let s = Item_t (key_hash_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IHash_key (loc, k));
               }
      | IPack (loc, ty, k), Item_t (_, s) ->
          let s = Item_t (bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IPack (loc, ty, k));
               }
      | IUnpack (loc, ty, k), Item_t (_, s) ->
          let+ o = option_t dummy ty in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IUnpack (loc, ty, k));
            }
      | IBlake2b (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IBlake2b (loc, k));
               }
      | ISha256 (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISha256 (loc, k));
               }
      | ISha512 (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISha512 (loc, k));
               }
      | ISource (loc, k), s ->
          let s = Item_t (address_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISource (loc, k));
               }
      | ISender (loc, k), s ->
          let s = Item_t (address_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISender (loc, k));
               }
      | ISelf (loc, ty, ep, k), s ->
          let+ c = contract_t dummy ty in
          let s = Item_t (c, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ISelf (loc, ty, ep, k));
            }
      | ISelf_address (loc, k), s ->
          let s = Item_t (address_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISelf_address (loc, k));
               }
      | IAmount (loc, k), s ->
          let s = Item_t (mutez_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAmount (loc, k));
               }
      | ISapling_empty_state (loc, memo_size, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = Item_t (sapling_state_t ~memo_size, s);
                 continuation = k;
                 reconstruct =
                   (fun k -> ISapling_empty_state (loc, memo_size, k));
               }
      | ( ISapling_verify_update_deprecated (loc, k),
          Item_t (_, Item_t (state_ty, s)) ) ->
          let* (Ty_ex_c pair_ty) = pair_t dummy int_t state_ty in
          let+ ty = option_t dummy pair_ty in
          Ex_split_kinstr
            {
              cont_init_stack = Item_t (ty, s);
              continuation = k;
              reconstruct =
                (fun k -> ISapling_verify_update_deprecated (loc, k));
            }
      | ISapling_verify_update (loc, k), Item_t (_, Item_t (state_ty, s)) ->
          let* (Ty_ex_c int_state_ty) = pair_t dummy int_t state_ty in
          let* (Ty_ex_c pair_ty) = pair_t dummy bytes_t int_state_ty in
          let+ ty = option_t dummy pair_ty in
          let s = Item_t (ty, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ISapling_verify_update (loc, k));
            }
      | IDig (loc, n, p, k), s ->
          let (Item_t (b, s)) =
            stack_prefix_preservation_witness_split_input p s
          in
          let s = stack_prefix_preservation_witness_split_output p s in
          let s = Item_t (b, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IDig (loc, n, p, k));
               }
      | IDug (loc, n, p, k), Item_t (a, s) ->
          let s = stack_prefix_preservation_witness_split_input p s in
          let s = Item_t (a, s) in
          let s = stack_prefix_preservation_witness_split_output p s in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IDug (loc, n, p, k));
               }
      | IDipn (loc, n, p, k1, k2), s ->
          return
          @@ Ex_split_loop_may_not_fail
               {
                 body_init_stack =
                   stack_prefix_preservation_witness_split_input p s;
                 body = k1;
                 continuation = k2;
                 aft_body_stack_transform =
                   (fun s ->
                     return
                     @@ stack_prefix_preservation_witness_split_output p s);
                 reconstruct = (fun k1 k2 -> IDipn (loc, n, p, k1, k2));
               }
      | IDropn (loc, n, p, k), s ->
          let s = stack_prefix_preservation_witness_split_input p s in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IDropn (loc, n, p, k));
               }
      | IChainId (loc, k), s ->
          let s = Item_t (chain_id_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IChainId (loc, k));
               }
      | INever location, Item_t (arg_ty, _) ->
          return
          @@ Ex_split_failwith
               {location; arg_ty; cast = {cast = INever location}}
      | IVoting_power (loc, k), Item_t (_, s) ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IVoting_power (loc, k));
               }
      | ITotal_voting_power (loc, k), s ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ITotal_voting_power (loc, k));
               }
      | IKeccak (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IKeccak (loc, k));
               }
      | ISha3 (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> ISha3 (loc, k));
               }
      | IAdd_bls12_381_g1 (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_bls12_381_g1 (loc, k));
               }
      | IAdd_bls12_381_g2 (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_bls12_381_g2 (loc, k));
               }
      | IAdd_bls12_381_fr (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IAdd_bls12_381_fr (loc, k));
               }
      | IMul_bls12_381_g1 (loc, k), Item_t (g1, Item_t (_, s)) ->
          let s = Item_t (g1, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_bls12_381_g1 (loc, k));
               }
      | IMul_bls12_381_g2 (loc, k), Item_t (g2, Item_t (_, s)) ->
          let s = Item_t (g2, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_bls12_381_g2 (loc, k));
               }
      | IMul_bls12_381_fr (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_bls12_381_fr (loc, k));
               }
      | IMul_bls12_381_z_fr (loc, k), Item_t (fr, Item_t (_, s)) ->
          let s = Item_t (fr, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_bls12_381_z_fr (loc, k));
               }
      | IMul_bls12_381_fr_z (loc, k), Item_t (_, s) ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMul_bls12_381_fr_z (loc, k));
               }
      | IInt_bls12_381_fr (loc, k), Item_t (_, s) ->
          let s = Item_t (int_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IInt_bls12_381_fr (loc, k));
               }
      | INeg_bls12_381_g1 (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INeg_bls12_381_g1 (loc, k));
               }
      | INeg_bls12_381_g2 (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INeg_bls12_381_g2 (loc, k));
               }
      | INeg_bls12_381_fr (loc, k), s ->
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> INeg_bls12_381_fr (loc, k));
               }
      | IPairing_check_bls12_381 (loc, k), Item_t (_, s) ->
          let s = Item_t (bool_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IPairing_check_bls12_381 (loc, k));
               }
      | IComb (loc, n, p, k), s ->
          let rec aux :
              type a b s c d t.
              (a, b * s) stack_ty ->
              (a, b, s, c, d, t) comb_gadt_witness ->
              (c, d * t) stack_ty tzresult =
           fun s w ->
            match (w, s) with
            | Comb_one, s -> return s
            | Comb_succ w, Item_t (a, s) ->
                let* (Item_t (c, t)) = aux s w in
                let+ (Ty_ex_c p) = pair_t dummy a c in
                Item_t (p, t)
          in
          let+ s = aux s p in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IComb (loc, n, p, k));
            }
      | IUncomb (loc, n, p, k), s ->
          let rec aux :
              type a b s c d t.
              (a, b * s) stack_ty ->
              (a, b, s, c, d, t) uncomb_gadt_witness ->
              (c, d * t) stack_ty =
           fun s w ->
            match (w, s) with
            | Uncomb_one, s -> s
            | Uncomb_succ w, Item_t (Pair_t (a, b, _meta, _), s) ->
                let s = aux (Item_t (b, s)) w in
                Item_t (a, s)
          in
          let s = aux s p in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IUncomb (loc, n, p, k));
               }
      | IComb_get (loc, n, p, k), Item_t (c, s) ->
          let rec aux :
              type c cc a.
              (c, cc) ty -> (c, a) comb_get_gadt_witness -> a ty_ex_c =
           fun c w ->
            match (w, c) with
            | Comb_get_zero, c -> Ty_ex_c c
            | Comb_get_one, Pair_t (hd, _tl, _meta, _) -> Ty_ex_c hd
            | Comb_get_plus_two w, Pair_t (_hd, tl, _meta, _) -> aux tl w
          in
          let s =
            let (Ty_ex_c ty) = aux c p in
            Item_t (ty, s)
          in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IComb_get (loc, n, p, k));
               }
      | IComb_set (loc, n, p, k), Item_t (a, Item_t (b, s)) ->
          let rec aux :
              type a b c ca cb.
              (a, ca) ty ->
              (b, cb) ty ->
              (a, b, c) comb_set_gadt_witness ->
              c ty_ex_c tzresult =
           fun a b w ->
            match (w, b) with
            | Comb_set_zero, _ -> return (Ty_ex_c a)
            | Comb_set_one, Pair_t (_hd, tl, _meta, _) -> pair_t dummy a tl
            | Comb_set_plus_two w, Pair_t (hd, tl, _meta, _) ->
                let* (Ty_ex_c c) = aux a tl w in
                pair_t dummy hd c
          in
          let+ (Ty_ex_c c) = aux a b p in
          let s = Item_t (c, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IComb_set (loc, n, p, k));
            }
      | IDup_n (loc, n, p, k), s ->
          let rec aux :
              type a b s t.
              (a, b * s) stack_ty ->
              (a, b, s, t) dup_n_gadt_witness ->
              t ty_ex_c =
           fun s w ->
            match (w, s) with
            | Dup_n_succ w, Item_t (_, s) -> aux s w
            | Dup_n_zero, Item_t (a, _) -> Ty_ex_c a
          in
          let s =
            let (Ty_ex_c ty) = aux s p in
            Item_t (ty, s)
          in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IDup_n (loc, n, p, k));
               }
      | ITicket (loc, cty, k), Item_t (_, Item_t (_, s)) ->
          let* ty = ticket_t dummy (assert_some cty) in
          let+ t = option_t loc ty in
          let s = Item_t (t, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ITicket (loc, cty, k));
            }
      | ITicket_deprecated (loc, cty, k), Item_t (_, Item_t (_, s)) ->
          let+ t = ticket_t dummy (assert_some cty) in
          let s = Item_t (t, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ITicket_deprecated (loc, cty, k));
            }
      | IRead_ticket (loc, a, k), s ->
          let* (Ty_ex_c p) = pair_t dummy (assert_some a) nat_t in
          let+ (Ty_ex_c t) = pair_t dummy address_t p in
          let s = Item_t (t, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IRead_ticket (loc, a, k));
            }
      | ISplit_ticket (loc, k), Item_t (t, Item_t (_, s)) ->
          let* (Ty_ex_c p) = pair_t dummy t t in
          let+ o = option_t dummy p in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> ISplit_ticket (loc, k));
            }
      | IJoin_tickets (loc, ty, k), Item_t (Pair_t (t, _t, _meta, _), s) ->
          let+ o = option_t dummy t in
          let s = Item_t (o, s) in
          Ex_split_kinstr
            {
              cont_init_stack = s;
              continuation = k;
              reconstruct = (fun k -> IJoin_tickets (loc, ty, k));
            }
      | IOpen_chest (loc, k), Item_t (_, Item_t (_, Item_t (_, s))) ->
          let s = Item_t (option_bytes_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IOpen_chest (loc, k));
               }
      | IMin_block_time (loc, k), s ->
          let s = Item_t (nat_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IMin_block_time (loc, k));
               }
      | IEmit {loc; ty; unparsed_ty; tag; k}, Item_t (_, s) ->
          let s = Item_t (operation_t, s) in
          return
          @@ Ex_split_kinstr
               {
                 cont_init_stack = s;
                 continuation = k;
                 reconstruct = (fun k -> IEmit {loc; ty; unparsed_ty; tag; k});
               }
      | IEmit _, Bot_t -> .
      | IHalt loc, _s -> return @@ Ex_split_halt loc
      | ILog (loc, _stack_ty, event, logger, continuation), stack ->
          return
          @@ Ex_split_log
               {
                 stack;
                 continuation;
                 reconstruct = (fun k -> ILog (loc, s, event, logger, k));
               }

  (* [kinstr_final_stack_type sty instr] computes the stack type after
     [instr] has been executed, assuming [sty] is the type of the stack
     prior to execution. For the rare instructions which can return stacks
     of any type ([FAILWITH] and [NEVER]), this function returns [None]. *)
  let rec kinstr_final_stack_type :
      type a s r f.
      (a, s) stack_ty -> (a, s, r, f) kinstr -> (r, f) stack_ty option tzresult
      =
    let open Result_syntax in
    fun s i ->
      let* ex_split_kinstr = kinstr_split s i in
      match ex_split_kinstr with
      | Ex_split_kinstr {cont_init_stack; continuation; _} ->
          kinstr_final_stack_type cont_init_stack continuation
      | Ex_split_log {stack; continuation; _} ->
          kinstr_final_stack_type stack continuation
      | Ex_split_loop_may_fail {cont_init_stack; continuation; _} ->
          kinstr_final_stack_type cont_init_stack continuation
      | Ex_split_loop_may_not_fail
          {body_init_stack; body; continuation; aft_body_stack_transform; _}
        -> (
          let* sty = kinstr_final_stack_type body_init_stack body in
          match sty with
          | Some after_body ->
              let* before_k = aft_body_stack_transform after_body in
              kinstr_final_stack_type before_k continuation
          | None -> return_none)
      | Ex_split_if
          {
            left_init_stack;
            left_branch;
            right_init_stack;
            right_branch;
            continuation;
            _;
          } -> (
          let* sty = kinstr_final_stack_type left_init_stack left_branch in
          match sty with
          | Some after_branch_a ->
              kinstr_final_stack_type after_branch_a continuation
          | None -> (
              let* sty =
                kinstr_final_stack_type right_init_stack right_branch
              in
              match sty with
              | Some after_branch_b ->
                  kinstr_final_stack_type after_branch_b continuation
              | None -> return_none))
      | Ex_split_halt _ -> return_some s
      | Ex_split_failwith {cast = {cast = _}; _} -> return_none

  (* The same as [kinstr_final_stack_type], but selects from multiple
     possible execution branches. If the first instr ends with FAILWITH,
     it will try the next and so on. Note that all instructions must
     result in the same stack type. *)
  let rec branched_final_stack_type :
      type r f. (r, f) ex_init_stack_ty list -> (r, f) stack_ty option tzresult
      =
    let open Result_syntax in
    function
    | [] -> return_none
    | Ex_init_stack_ty (init_sty, branch) :: bs -> (
        let* sty = kinstr_final_stack_type init_sty branch in
        match sty with
        | Some _ as sty -> return sty
        | None -> branched_final_stack_type bs)

  let kinstr_rewritek :
      type a s r f.
      (a, s) stack_ty ->
      (a, s, r, f) kinstr ->
      kinstr_rewritek ->
      (a, s, r, f) kinstr tzresult =
    let open Result_syntax in
    fun s i f ->
      let* ex_split_kinstr = kinstr_split s i in
      match ex_split_kinstr with
      | Ex_split_kinstr {cont_init_stack; continuation; reconstruct} ->
          return @@ reconstruct (f.apply cont_init_stack continuation)
      | Ex_split_log {continuation; reconstruct; _} ->
          return @@ reconstruct continuation
      | Ex_split_loop_may_fail
          {body_init_stack; body; cont_init_stack; continuation; reconstruct} ->
          return
          @@ reconstruct
               (f.apply body_init_stack body)
               (f.apply cont_init_stack continuation)
      | Ex_split_loop_may_not_fail
          {
            body_init_stack;
            body;
            continuation;
            aft_body_stack_transform;
            reconstruct;
          } ->
          let+ k =
            let* sty = kinstr_final_stack_type body_init_stack body in
            match sty with
            | Some after_body ->
                let+ before_k = aft_body_stack_transform after_body in
                f.apply before_k continuation
            | None -> return continuation
          in
          reconstruct (f.apply body_init_stack body) k
      | Ex_split_if
          {
            left_init_stack;
            left_branch;
            right_init_stack;
            right_branch;
            continuation;
            reconstruct;
          } ->
          let+ k =
            let* sty = kinstr_final_stack_type left_init_stack left_branch in
            match sty with
            | Some after_left_branch ->
                return @@ f.apply after_left_branch continuation
            | None -> (
                let* sty =
                  kinstr_final_stack_type right_init_stack right_branch
                in
                match sty with
                | Some after_right_branch ->
                    return @@ f.apply after_right_branch continuation
                | None -> return continuation)
          in
          reconstruct
            (f.apply left_init_stack left_branch)
            (f.apply right_init_stack right_branch)
            k
      | Ex_split_halt loc -> return @@ IHalt loc
      | Ex_split_failwith {location; arg_ty; _} ->
          return @@ IFailwith (location, arg_ty)

  (** [instrument_cont logger sty] creates a function instrumenting
    continuations starting from the stack type described by [sty].
    Instrumentation consists in wrapping inner continuations in
    [KLog] continuation so that logging continues. *)
  let instrument_cont :
      type a b c d.
      logger ->
      (a, b) stack_ty ->
      (a, b, c, d) continuation ->
      (a, b, c, d) continuation =
   fun logger sty -> function KLog _ as k -> k | k -> KLog (k, sty, logger)
end

module type Logger_base = sig
  val log_interp : ('a, 's, 'b, 'f, 'c, 'u) logging_function

  val log_entry : ('a, 's, 'b, 'f, 'a, 's) logging_function

  val log_control : ('a, 's, 'b, 'f) continuation -> unit

  val log_exit : ('a, 's, 'b, 'f, 'c, 'u) logging_function

  val get_log : unit -> execution_trace option tzresult Lwt.t
end

module Logger (Base : Logger_base) = struct
  open Stack_utils
  open Local_gas_counter
  open Script_interpreter_defs
  open Script_interpreter.Internals.Raw

  (** [log_entry ctxt gas instr sty accu stack] simply calls the
      [Base.log_entry] function with the appropriate arguments. *)
  let log_entry ctxt gas k sty accu stack =
    let ctxt = Local_gas_counter.update_context gas ctxt in
    Base.log_entry k ctxt (kinstr_location k) sty (accu, stack)

  (** [log_exit ctxt gas loc instr sty accu stack] simply calls the
      [Base.log_exit] function with the appropriate arguments. *)
  let log_exit ctxt gas loc_prev k sty accu stack =
    let ctxt = Local_gas_counter.update_context gas ctxt in
    Base.log_exit k ctxt loc_prev sty (accu, stack)

  (** [log_control continuation] simply calls the [Base.log_control]
      function with the appropriate arguments. *)
  let log_control ks = Base.log_control ks

  (** [log_kinstr logger sty instr] returns [instr] prefixed by an
      [ILog] instruction to log the first instruction in [instr]. *)
  let log_kinstr logger sty i =
    ILog (kinstr_location i, sty, LogEntry, logger, i)

  (* [log_next_kinstr logger i] instruments the next instruction of [i]
     with [ILog] instructions to make sure it will be logged.
     This instrumentation has a performance cost, but importantly, it is
     only ever paid when logging is enabled. Otherwise, the possibility
     to instrument the script is costless.

     Notice that the instrumentation breaks the sharing of continuations
     that is normally enforced between branches of conditionals. This
     has a performance cost. Anyway, the instrumentation allocates many
     new [ILog] instructions and [KLog] continuations which makes
     the execution of instrumented code significantly slower than
     non-instrumented code. "Zero-cost logging" means that the normal
     non-instrumented execution is not impacted by the ability to
     instrument it, not that the logging itself has no cost.
  *)
  let log_next_kinstr logger sty i =
    let apply sty k =
      ILog
        ( kinstr_location k,
          sty,
          LogExit (kinstr_location i),
          logger,
          log_kinstr logger sty k )
    in
    kinstr_rewritek sty i {apply}

  (** [log_next_continuation logger sty cont] instruments the next
    continuation in [cont] with [KLog] continuations to ensure
    logging.

    This instrumentation has a performance cost, but importantly, it
    is only ever paid when logging is enabled. Otherwise, the
    possibility to instrument the script is costless. *)
  let log_next_continuation :
      type a b c d.
      logger ->
      (a, b) stack_ty ->
      (a, b, c, d) continuation ->
      (a, b, c, d) continuation tzresult =
    let open Result_syntax in
    fun logger stack_ty cont ->
      let enable_log sty ki = log_kinstr logger sty ki in
      match cont with
      | KCons (ki, k) -> (
          let ki' = enable_log stack_ty ki in
          let+ sty = kinstr_final_stack_type stack_ty ki in
          match sty with
          | None -> KCons (ki', k)
          | Some sty -> KCons (ki', instrument_cont logger sty k))
      | KLoop_in (ki, k) ->
          let (Item_t (Bool_t, sty)) = stack_ty in
          return @@ KLoop_in (enable_log sty ki, instrument_cont logger sty k)
      | KReturn (stack, sty, k) ->
          let k' = instrument_cont logger (assert_some sty) k in
          return @@ KReturn (stack, sty, k')
      | KLoop_in_left (ki, k) ->
          let (Item_t (Or_t (a_ty, b_ty, _, _), rest)) = stack_ty in
          let ki' = enable_log (Item_t (a_ty, rest)) ki in
          let k' = instrument_cont logger (Item_t (b_ty, rest)) k in
          return @@ KLoop_in_left (ki', k')
      | KUndip (x, ty, k) ->
          let k' =
            instrument_cont logger (Item_t (assert_some ty, stack_ty)) k
          in
          return @@ KUndip (x, ty, k')
      | KIter (body, xty, xs, k) ->
          let body' = enable_log (Item_t (assert_some xty, stack_ty)) body in
          let k' = instrument_cont logger stack_ty k in
          return @@ KIter (body', xty, xs, k')
      | KList_enter_body (body, xs, ys, ty, len, k) ->
          let k' =
            instrument_cont logger (Item_t (assert_some ty, stack_ty)) k
          in
          return @@ KList_enter_body (body, xs, ys, ty, len, k')
      | KList_exit_body (body, xs, ys, ty, len, k) ->
          let (Item_t (_, sty)) = stack_ty in
          let k' = instrument_cont logger (Item_t (assert_some ty, sty)) k in
          return @@ KList_exit_body (body, xs, ys, ty, len, k')
      | KMap_enter_body (body, xs, ys, ty, k) ->
          let k' =
            instrument_cont logger (Item_t (assert_some ty, stack_ty)) k
          in
          return @@ KMap_enter_body (body, xs, ys, ty, k')
      | KMap_exit_body (body, xs, ys, yk, ty, k) ->
          let (Item_t (_, sty)) = stack_ty in
          let k' = instrument_cont logger (Item_t (assert_some ty, sty)) k in
          return @@ KMap_exit_body (body, xs, ys, yk, ty, k')
      | KMap_head (_, _)
      | KView_exit (_, _)
      | KLog _ (* This case should never happen. *) | KNil ->
          return cont

  (*

    Zero-cost logging
    =================

  *)

  (*

     The following functions insert a logging instruction to continue
     the logging process in the next execution steps.

     There is a special treatment of instructions that generate fresh
     continuations: we pass a constructor as argument to their
     evaluation rules so that they can instrument these fresh
     continuations by themselves.

     This on-the-fly instrumentation of the execution allows zero-cost
     logging since logging instructions are only introduced if an
     initial logging continuation is pushed in the initial continuation
     that starts the evaluation.

  *)
  let ilog :
      type a s b t r f.
      logger -> logging_event -> (a, s) stack_ty -> (a, s, b, t, r, f) step_type
      =
    let open Lwt_result_syntax in
    fun logger event sty ((ctxt, _) as g) old_gas k ks accu stack ->
      (match (k, event) with
      | ILog _, LogEntry -> ()
      | _, LogEntry -> log_entry ctxt old_gas k sty accu stack
      | _, LogExit prev_loc -> log_exit ctxt old_gas prev_loc k sty accu stack) ;
      let*? k = log_next_kinstr logger sty k in
      (* We need to match on instructions that create continuations so
         that we can instrument those continuations with [KLog] (see
         comment above).  For functions that don't do this, we simply call
         [step], as they don't require any special treatment. *)
      match consume_instr old_gas k accu stack with
      | None -> tzfail Gas.Operation_quota_exceeded
      | Some gas -> (
          match k with
          | IIf_none {branch_if_none; branch_if_some; k; _} -> (
              let (Item_t (Option_t (ty, _, _), rest)) = sty in
              let*? sty_opt =
                branched_final_stack_type
                  [
                    Ex_init_stack_ty (rest, branch_if_none);
                    Ex_init_stack_ty (Item_t (ty, rest), branch_if_some);
                  ]
              in
              let ks' =
                match sty_opt with
                | None -> KCons (k, ks)
                | Some sty' -> instrument_cont logger sty' @@ KCons (k, ks)
              in
              match accu with
              | None ->
                  let accu, stack = stack in
                  (step [@ocaml.tailcall]) g gas branch_if_none ks' accu stack
              | Some v ->
                  (step [@ocaml.tailcall]) g gas branch_if_some ks' v stack)
          | IOpt_map {body; k; loc = _} -> (
              match accu with
              | None -> (step [@ocaml.tailcall]) g gas k ks None stack
              | Some v ->
                  let (Item_t (Option_t (ty, _, _), rest)) = sty in
                  let bsty = Item_t (ty, rest) in
                  let kmap_head = KMap_head (Option.some, KCons (k, ks)) in
                  let*? sty_opt = kinstr_final_stack_type bsty body in
                  let ks' =
                    match sty_opt with
                    | None -> kmap_head
                    | Some sty' -> instrument_cont logger sty' kmap_head
                  in
                  (step [@ocaml.tailcall]) g gas body ks' v stack)
          | IIf_left {branch_if_left; branch_if_right; k; _} -> (
              let (Item_t (Or_t (lty, rty, _, _), rest)) = sty in
              let*? sty_opt =
                branched_final_stack_type
                  [
                    Ex_init_stack_ty (Item_t (lty, rest), branch_if_left);
                    Ex_init_stack_ty (Item_t (rty, rest), branch_if_right);
                  ]
              in
              let k' =
                match sty_opt with
                | None -> KCons (k, ks)
                | Some sty' -> instrument_cont logger sty' @@ KCons (k, ks)
              in
              match accu with
              | L v -> (step [@ocaml.tailcall]) g gas branch_if_left k' v stack
              | R v -> (step [@ocaml.tailcall]) g gas branch_if_right k' v stack
              )
          | IIf_cons {branch_if_cons; branch_if_nil; k; _} -> (
              let (Item_t ((List_t (elty, _) as lty), rest)) = sty in
              let*? sty' =
                branched_final_stack_type
                  [
                    Ex_init_stack_ty (rest, branch_if_nil);
                    Ex_init_stack_ty
                      (Item_t (elty, Item_t (lty, rest)), branch_if_cons);
                  ]
              in
              let k' =
                match sty' with
                | None -> KCons (k, ks)
                | Some sty' -> instrument_cont logger sty' @@ KCons (k, ks)
              in
              match Script_list.uncons accu with
              | None ->
                  let accu, stack = stack in
                  (step [@ocaml.tailcall]) g gas branch_if_nil k' accu stack
              | Some (hd, tl) ->
                  (step [@ocaml.tailcall]) g gas branch_if_cons k' hd (tl, stack)
              )
          | IList_map (_, body, ty, k) ->
              let (Item_t (_, sty')) = sty in
              let instrument = instrument_cont logger sty' in
              (ilist_map [@ocaml.tailcall])
                instrument
                g
                gas
                body
                k
                ks
                ty
                accu
                stack
          | IList_iter (_, ty, body, k) ->
              let (Item_t (_, sty')) = sty in
              let instrument = instrument_cont logger sty' in
              (ilist_iter [@ocaml.tailcall])
                instrument
                g
                gas
                body
                ty
                k
                ks
                accu
                stack
          | ISet_iter (_, ty, body, k) ->
              let (Item_t (_, rest)) = sty in
              let instrument = instrument_cont logger rest in
              (iset_iter [@ocaml.tailcall])
                instrument
                g
                gas
                body
                ty
                k
                ks
                accu
                stack
          | IMap_map (_, ty, body, k) ->
              let (Item_t (_, rest)) = sty in
              let instrument = instrument_cont logger rest in
              (imap_map [@ocaml.tailcall])
                instrument
                g
                gas
                body
                k
                ks
                ty
                accu
                stack
          | IMap_iter (_, kvty, body, k) ->
              let (Item_t (_, rest)) = sty in
              let instrument = instrument_cont logger rest in
              (imap_iter [@ocaml.tailcall])
                instrument
                g
                gas
                body
                kvty
                k
                ks
                accu
                stack
          | IMul_teznat (loc, k) ->
              (imul_teznat [@ocaml.tailcall])
                (Some logger)
                g
                gas
                loc
                k
                ks
                accu
                stack
          | IMul_nattez (loc, k) ->
              (imul_nattez [@ocaml.tailcall])
                (Some logger)
                g
                gas
                loc
                k
                ks
                accu
                stack
          | ILsl_nat (loc, k) ->
              (ilsl_nat [@ocaml.tailcall])
                (Some logger)
                g
                gas
                loc
                k
                ks
                accu
                stack
          | ILsr_nat (loc, k) ->
              (ilsr_nat [@ocaml.tailcall])
                (Some logger)
                g
                gas
                loc
                k
                ks
                accu
                stack
          | IIf {branch_if_true; branch_if_false; k; _} ->
              let (Item_t (Bool_t, rest)) = sty in
              let*? sty' =
                branched_final_stack_type
                  [
                    Ex_init_stack_ty (rest, branch_if_true);
                    Ex_init_stack_ty (rest, branch_if_false);
                  ]
              in
              let k' =
                match sty' with
                | None -> KCons (k, ks)
                | Some sty' -> instrument_cont logger sty' @@ KCons (k, ks)
              in
              let res, stack = stack in
              if accu then
                (step [@ocaml.tailcall]) g gas branch_if_true k' res stack
              else (step [@ocaml.tailcall]) g gas branch_if_false k' res stack
          | ILoop (_, body, k) ->
              let ks =
                instrument_cont logger sty @@ KLoop_in (body, KCons (k, ks))
              in
              (next [@ocaml.tailcall]) g gas ks accu stack
          | ILoop_left (_, bl, br) ->
              let ks =
                instrument_cont logger sty @@ KLoop_in_left (bl, KCons (br, ks))
              in
              (next [@ocaml.tailcall]) g gas ks accu stack
          | IDip (_, b, ty, k) ->
              let (Item_t (_, rest)) = sty in
              let*? rest' = kinstr_final_stack_type rest b in
              let ign = accu in
              let ks =
                match rest' with
                | None -> KUndip (ign, ty, KCons (k, ks))
                | Some rest' ->
                    instrument_cont
                      logger
                      rest'
                      (KUndip (ign, ty, KCons (k, ks)))
              in
              let accu, stack = stack in
              (step [@ocaml.tailcall]) g gas b ks accu stack
          | IExec (_, stack_ty, k) ->
              let (Item_t (_, Item_t (Lambda_t (_, ret, _), _))) = sty in
              let sty' = Item_t (ret, Bot_t) in
              let instrument = instrument_cont logger sty' in
              iexec instrument (Some logger) g gas stack_ty k ks accu stack
          | IFailwith (kloc, tv) ->
              let {ifailwith} = ifailwith in
              (ifailwith [@ocaml.tailcall]) (Some logger) g gas kloc tv accu
          | IDipn (_, _n, n', b, k) ->
              let accu, stack, ks = kundip n' accu stack (KCons (k, ks)) in
              (step [@ocaml.tailcall]) g gas b ks accu stack
          | IView
              (_, (View_signature {output_ty; _} as view_signature), stack_ty, k)
            ->
              let sty' = Item_t (output_ty, Bot_t) in
              let instrument = instrument_cont logger sty' in
              (iview [@ocaml.tailcall])
                instrument
                g
                gas
                view_signature
                stack_ty
                k
                ks
                accu
                stack
          | _ -> (step [@ocaml.tailcall]) g old_gas k ks accu stack)
    [@@inline]

  let klog :
      type a s r f.
      logger ->
      outdated_context * step_constants ->
      local_gas_counter ->
      (a, s) stack_ty ->
      (a, s, r, f) continuation ->
      (a, s, r, f) continuation ->
      a ->
      s ->
      (r * f * outdated_context * local_gas_counter) tzresult Lwt.t =
    let open Lwt_result_syntax in
    fun logger g old_gas stack_ty k0 ks accu stack ->
      let ty_for_logging_unsafe = function
        (* This function is only called when logging is enabled.  If
           that's the case, the elaborator must have been called with
           [logging_enabled] option, which ensures that this will not be
           [None]. Realistically, it can happen that the [logging_enabled]
           option was omitted, resulting in a crash here. But this is
           acceptable, because logging is never enabled during block
           validation, so the layer 1 is safe. *)
        | None -> assert false
        | Some ty -> ty
      in
      (match ks with KLog _ -> () | _ -> log_control ks) ;
      match consume_control old_gas ks with
      | None -> tzfail Gas.Operation_quota_exceeded
      | Some gas -> (
          let*? continuation = log_next_continuation logger stack_ty ks in
          match continuation with
          | KCons (ki, k) -> (step [@ocaml.tailcall]) g gas ki k accu stack
          | KLoop_in (ki, k) ->
              (kloop_in [@ocaml.tailcall]) g gas k0 ki k accu stack
          | KReturn (_, _, _) as k ->
              (next [@ocaml.tailcall]) g old_gas k accu stack
          | KLoop_in_left (ki, k) ->
              (kloop_in_left [@ocaml.tailcall]) g gas k0 ki k accu stack
          | KUndip (_, _, _) as k ->
              (next [@ocaml.tailcall]) g old_gas k accu stack
          | KIter (body, xty, xs, k) ->
              let instrument = instrument_cont logger stack_ty in
              (kiter [@ocaml.tailcall])
                instrument
                g
                gas
                body
                xty
                xs
                k
                accu
                stack
          | KList_enter_body (body, xs, ys, ty_opt, len, k) ->
              let instrument =
                let ty = ty_for_logging_unsafe ty_opt in
                let (List_t (vty, _)) = ty in
                let sty = Item_t (vty, stack_ty) in
                instrument_cont logger sty
              in
              (klist_enter [@ocaml.tailcall])
                instrument
                g
                gas
                body
                xs
                ys
                ty_opt
                len
                k
                accu
                stack
          | KList_exit_body (body, xs, ys, ty_opt, len, k) ->
              let (Item_t (_, rest)) = stack_ty in
              let instrument = instrument_cont logger rest in
              (klist_exit [@ocaml.tailcall])
                instrument
                g
                gas
                body
                xs
                ys
                ty_opt
                len
                k
                accu
                stack
          | KMap_enter_body (body, xs, ys, ty_opt, k) ->
              let instrument =
                let ty = ty_for_logging_unsafe ty_opt in
                let (Map_t (_, vty, _)) = ty in
                let sty = Item_t (vty, stack_ty) in
                instrument_cont logger sty
              in
              (kmap_enter [@ocaml.tailcall])
                instrument
                g
                gas
                body
                xs
                ty_opt
                ys
                k
                accu
                stack
          | KMap_exit_body (body, xs, ys, yk, ty_opt, k) ->
              let (Item_t (_, rest)) = stack_ty in
              let instrument = instrument_cont logger rest in
              (kmap_exit [@ocaml.tailcall])
                instrument
                g
                gas
                body
                xs
                ty_opt
                ys
                yk
                k
                accu
                stack
          | KMap_head (f, k) -> (next [@ocaml.tailcall]) g gas k (f accu) stack
          | KView_exit (scs, k) ->
              (next [@ocaml.tailcall]) (fst g, scs) gas k accu stack
          | KLog _ as k ->
              (* This case should never happen. *)
              (next [@ocaml.tailcall]) g old_gas k accu stack
          | KNil as k -> (next [@ocaml.tailcall]) g old_gas k accu stack)
    [@@inline]
end

let make (module Base : Logger_base) =
  let module Logger = Logger (Base) in
  let open Logger in
  let open Base in
  {log_interp; get_log; log_kinstr; klog; ilog}
back to top