Raw File
interpreter_workload.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2021-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
module Size = Gas_input_size

(* ------------------------------------------------------------------------- *)

type id = string

let pp_id = Format.pp_print_string

let equal_id = String.equal

(* ------------------------------------------------------------------------- *)
(* Names of IR instructions together with sizes of their operands as
   encountered during evaluation. *)

type instruction_name =
  (* stack ops *)
  | N_IDrop
  | N_IDup
  | N_ISwap
  | N_IPush
  | N_IUnit
  (* pairs *)
  | N_ICons_pair
  | N_ICar
  | N_ICdr
  | N_IUnpair
  (* options *)
  | N_ICons_some
  | N_ICons_none
  | N_IIf_none
  | N_IOpt_map_none
  | N_IOpt_map_some
  (* ors *)
  | N_ILeft
  | N_IRight
  | N_IIf_left
  (* lists *)
  | N_ICons_list
  | N_INil
  | N_IIf_cons
  | N_IList_map
  | N_IList_iter
  | N_IIter
  | N_IList_size
  (* sets *)
  | N_IEmpty_set
  | N_ISet_iter
  | N_ISet_mem
  | N_ISet_update
  | N_ISet_size
  (* maps *)
  | N_IEmpty_map
  | N_IMap_map
  | N_IMap_iter
  | N_IMap_mem
  | N_IMap_get
  | N_IMap_update
  | N_IMap_get_and_update
  | N_IMap_size
  (* big maps *)
  | N_IEmpty_big_map
  | N_IBig_map_mem
  | N_IBig_map_get
  | N_IBig_map_update
  | N_IBig_map_get_and_update
  (* string operations *)
  | N_IConcat_string
  | N_IConcat_string_pair
  | N_ISlice_string
  | N_IString_size
  (* bytes operations *)
  | N_IConcat_bytes
  | N_IConcat_bytes_pair
  | N_ISlice_bytes
  | N_IBytes_size
  | N_IOr_bytes
  | N_IAnd_bytes
  | N_IXor_bytes
  | N_INot_bytes
  | N_ILsl_bytes
  | N_ILsr_bytes
  | N_IBytes_nat
  | N_INat_bytes
  | N_IBytes_int
  | N_IInt_bytes
  (* timestamp operations *)
  | N_IAdd_seconds_to_timestamp
  | N_IAdd_timestamp_to_seconds
  | N_ISub_timestamp_seconds
  | N_IDiff_timestamps
  (* currency operations *)
  | N_IAdd_tez
  | N_ISub_tez
  | N_ISub_tez_legacy
  | N_IMul_teznat
  | N_IMul_nattez
  | N_IEdiv_teznat
  | N_IEdiv_tez
  (* boolean operations - assumed O(1) *)
  | N_IOr
  | N_IAnd
  | N_IXor
  | N_INot
  (* integer operations *)
  | N_IIs_nat
  | N_INeg
  | N_IAbs_int
  | N_IInt_nat
  | N_IAdd_int
  | N_IAdd_nat
  | N_ISub_int
  | N_IMul_int
  | N_IMul_nat
  | N_IEdiv_int
  | N_IEdiv_nat
  | N_ILsl_nat
  | N_ILsr_nat
  | N_IOr_nat
  | N_IAnd_nat
  | N_IAnd_int_nat
  | N_IXor_nat
  | N_INot_int
  (* control *)
  | N_IIf
  | N_ILoop_in
  | N_ILoop_out
  | N_ILoop_left_in
  | N_ILoop_left_out
  | N_IDip
  | N_IExec
  | N_IApply
  | N_ILambda_lam
  | N_ILambda_lamrec
  | N_IFailwith
  (* comparison, warning: ad-hoc polymorphic instruction *)
  | N_ICompare
  (* comparators *)
  | N_IEq
  | N_INeq
  | N_ILt
  | N_IGt
  | N_ILe
  | N_IGe
  (* protocol *)
  | N_IAddress
  | N_IContract
  | N_ITransfer_tokens
  | N_IImplicit_account
  | N_ICreate_contract
  | N_ISet_delegate
  (* time *)
  | N_INow
  | N_IMin_block_time
  (* other *)
  | N_IBalance
  | N_ILevel
  | N_IView
  (* We specialize the check-signature instruction for each crypto scheme. *)
  | N_ICheck_signature_ed25519
  | N_ICheck_signature_secp256k1
  | N_ICheck_signature_p256
  | N_ICheck_signature_bls
  | N_IHash_key
  | N_IPack
  | N_IUnpack
  | N_IBlake2b
  | N_ISha256
  | N_ISha512
  | N_ISource
  | N_ISender
  | N_ISelf
  | N_ISelf_address
  | N_IAmount
  | N_ISapling_empty_state
  | N_ISapling_verify_update
  | N_IDig
  | N_IDug
  | N_IDipN
  | N_IDropN
  | N_IChainId
  | N_INever
  | N_IVoting_power
  | N_ITotal_voting_power
  | N_IKeccak
  | N_ISha3
  (* Elliptic curves *)
  | N_IAdd_bls12_381_g1
  | N_IAdd_bls12_381_g2
  | N_IAdd_bls12_381_fr
  | N_IMul_bls12_381_g1
  | N_IMul_bls12_381_g2
  | N_IMul_bls12_381_fr
  | N_INeg_bls12_381_g1
  | N_INeg_bls12_381_g2
  | N_INeg_bls12_381_fr
  | N_IMul_bls12_381_fr_z
  | N_IMul_bls12_381_z_fr
  | N_IInt_bls12_381_z_fr
  | N_IPairing_check_bls12_381
  (* Combs *)
  | N_IComb
  | N_IUncomb
  | N_IComb_get
  | N_IComb_set
  | N_IDupN
  (* Tickets *)
  | N_ITicket
  | N_IRead_ticket
  | N_ISplit_ticket
  | N_IJoin_tickets
  (* Misc *)
  | N_IHalt
  | N_ILog
  (* Timelock*)
  | N_IOpen_chest
  (* Event *)
  | N_IEmit

type continuation_name =
  | N_KNil
  | N_KCons
  | N_KReturn
  | N_KView_exit
  | N_KMap_head
  | N_KUndip
  | N_KLoop_in
  | N_KLoop_in_left
  | N_KIter_empty
  | N_KIter_nonempty
  | N_KList_enter_body
  | N_KList_exit_body
  | N_KMap_enter_body
  | N_KMap_exit_body
  | N_KLog

and instr_or_cont_name =
  | Instr_name of instruction_name
  | Cont_name of continuation_name

(* ------------------------------------------------------------------------- *)
(* Code that ought to be auto-generated *)

let string_of_instruction_name : instruction_name -> string =
 fun ir ->
  match ir with
  | N_IDrop -> "N_IDrop"
  | N_IDup -> "N_IDup"
  | N_ISwap -> "N_ISwap"
  | N_IPush -> "N_IPush"
  | N_IUnit -> "N_IUnit"
  | N_ICons_pair -> "N_ICons_pair"
  | N_ICar -> "N_ICar"
  | N_ICdr -> "N_ICdr"
  | N_ICons_some -> "N_ICons_some"
  | N_ICons_none -> "N_ICons_none"
  | N_IIf_none -> "N_IIf_none"
  | N_IOpt_map_none -> "N_IOpt_map_none"
  | N_IOpt_map_some -> "N_IOpt_map_some"
  | N_ILeft -> "N_ILeft"
  | N_IRight -> "N_IRight"
  | N_IIf_left -> "N_IIf_left"
  | N_ICons_list -> "N_ICons_list"
  | N_INil -> "N_INil"
  | N_IIf_cons -> "N_IIf_cons"
  | N_IList_map -> "N_IList_map"
  | N_IList_iter -> "N_IList_iter"
  | N_IIter -> "N_IIter"
  | N_IList_size -> "N_IList_size"
  | N_IEmpty_set -> "N_IEmpty_set"
  | N_ISet_iter -> "N_ISet_iter"
  | N_ISet_mem -> "N_ISet_mem"
  | N_ISet_update -> "N_ISet_update"
  | N_ISet_size -> "N_ISet_size"
  | N_IEmpty_map -> "N_IEmpty_map"
  | N_IMap_map -> "N_IMap_map"
  | N_IMap_iter -> "N_IMap_iter"
  | N_IMap_mem -> "N_IMap_mem"
  | N_IMap_get -> "N_IMap_get"
  | N_IMap_update -> "N_IMap_update"
  | N_IMap_size -> "N_IMap_size"
  | N_IEmpty_big_map -> "N_IEmpty_big_map"
  | N_IBig_map_mem -> "N_IBig_map_mem"
  | N_IBig_map_get -> "N_IBig_map_get"
  | N_IBig_map_update -> "N_IBig_map_update"
  | N_IConcat_string -> "N_IConcat_string"
  | N_IConcat_string_pair -> "N_IConcat_string_pair"
  | N_ISlice_string -> "N_ISlice_string"
  | N_IString_size -> "N_IString_size"
  | N_IConcat_bytes -> "N_IConcat_bytes"
  | N_IConcat_bytes_pair -> "N_IConcat_bytes_pair"
  | N_ISlice_bytes -> "N_ISlice_bytes"
  | N_IBytes_size -> "N_IBytes_size"
  | N_IOr_bytes -> "N_IOr_bytes"
  | N_IAnd_bytes -> "N_IAnd_bytes"
  | N_IXor_bytes -> "N_IXor_bytes"
  | N_INot_bytes -> "N_INot_bytes"
  | N_ILsl_bytes -> "N_ILsl_bytes"
  | N_ILsr_bytes -> "N_ILsr_bytes"
  | N_IBytes_nat -> "N_IBytes_nat"
  | N_INat_bytes -> "N_INat_bytes"
  | N_IBytes_int -> "N_IBytes_int"
  | N_IInt_bytes -> "N_IInt_bytes"
  | N_IAdd_seconds_to_timestamp -> "N_IAdd_seconds_to_timestamp"
  | N_IAdd_timestamp_to_seconds -> "N_IAdd_timestamp_to_seconds"
  | N_ISub_timestamp_seconds -> "N_ISub_timestamp_seconds"
  | N_IDiff_timestamps -> "N_IDiff_timestamps"
  | N_IAdd_tez -> "N_IAdd_tez"
  | N_ISub_tez -> "N_ISub_tez"
  | N_ISub_tez_legacy -> "N_ISub_tez_legacy"
  | N_IMul_teznat -> "N_IMul_teznat"
  | N_IMul_nattez -> "N_IMul_nattez"
  | N_IEdiv_teznat -> "N_IEdiv_teznat"
  | N_IEdiv_tez -> "N_IEdiv_tez"
  | N_IOr -> "N_IOr"
  | N_IAnd -> "N_IAnd"
  | N_IXor -> "N_IXor"
  | N_INot -> "N_INot"
  | N_IIs_nat -> "N_IIs_nat"
  | N_INeg -> "N_INeg"
  | N_IAbs_int -> "N_IAbs_int"
  | N_IInt_nat -> "N_IInt_nat"
  | N_IAdd_int -> "N_IAdd_int"
  | N_IAdd_nat -> "N_IAdd_nat"
  | N_ISub_int -> "N_ISub_int"
  | N_IMul_int -> "N_IMul_int"
  | N_IMul_nat -> "N_IMul_nat"
  | N_IEdiv_int -> "N_IEdiv_int"
  | N_IEdiv_nat -> "N_IEdiv_nat"
  | N_ILsl_nat -> "N_ILsl_nat"
  | N_ILsr_nat -> "N_ILsr_nat"
  | N_IOr_nat -> "N_IOr_nat"
  | N_IAnd_nat -> "N_IAnd_nat"
  | N_IAnd_int_nat -> "N_IAnd_int_nat"
  | N_IXor_nat -> "N_IXor_nat"
  | N_INot_int -> "N_INot_int"
  | N_IIf -> "N_IIf"
  | N_ILoop_in -> "N_ILoop_in"
  | N_ILoop_out -> "N_ILoop_out"
  | N_ILoop_left_in -> "N_ILoop_left_in"
  | N_ILoop_left_out -> "N_ILoop_left_out"
  | N_IDip -> "N_IDip"
  | N_IExec -> "N_IExec"
  | N_IApply -> "N_IApply"
  | N_ILambda_lam -> "N_ILambda_lam"
  | N_ILambda_lamrec -> "N_ILambda_lamrec"
  | N_IFailwith -> "N_IFailwith"
  | N_ICompare -> "N_ICompare"
  | N_IEq -> "N_IEq"
  | N_INeq -> "N_INeq"
  | N_ILt -> "N_ILt"
  | N_IGt -> "N_IGt"
  | N_ILe -> "N_ILe"
  | N_IGe -> "N_IGe"
  | N_IAddress -> "N_IAddress"
  | N_IContract -> "N_IContract"
  | N_ITransfer_tokens -> "N_ITransfer_tokens"
  | N_IImplicit_account -> "N_IImplicit_account"
  | N_ICreate_contract -> "N_ICreate_contract"
  | N_ISet_delegate -> "N_ISet_delegate"
  | N_INow -> "N_INow"
  | N_IMin_block_time -> "N_IMin_block_time"
  | N_IBalance -> "N_IBalance"
  | N_ICheck_signature_ed25519 -> "N_ICheck_signature_ed25519"
  | N_ICheck_signature_secp256k1 -> "N_ICheck_signature_secp256k1"
  | N_ICheck_signature_p256 -> "N_ICheck_signature_p256"
  | N_ICheck_signature_bls -> "N_ICheck_signature_bls"
  | N_IHash_key -> "N_IHash_key"
  | N_IPack -> "N_IPack"
  | N_IUnpack -> "N_IUnpack"
  | N_IBlake2b -> "N_IBlake2b"
  | N_ISha256 -> "N_ISha256"
  | N_ISha512 -> "N_ISha512"
  | N_ISource -> "N_ISource"
  | N_ISender -> "N_ISender"
  | N_ISelf -> "N_ISelf"
  | N_IAmount -> "N_IAmount"
  | N_IDig -> "N_IDig"
  | N_IDug -> "N_IDug"
  | N_IDipN -> "N_IDipN"
  | N_IDropN -> "N_IDropN"
  | N_IDupN -> "N_IDupN"
  | N_IChainId -> "N_IChainId"
  | N_ILevel -> "N_ILevel"
  | N_IView -> "N_IView"
  | N_ISelf_address -> "N_ISelf_address"
  | N_INever -> "N_INever"
  | N_IUnpair -> "N_IUnpair"
  | N_IVoting_power -> "N_IVoting_power"
  | N_ITotal_voting_power -> "N_ITotal_voting_power"
  | N_IKeccak -> "N_IKeccak"
  | N_ISha3 -> "N_ISha3"
  | N_IAdd_bls12_381_g1 -> "N_IAdd_bls12_381_g1"
  | N_IAdd_bls12_381_g2 -> "N_IAdd_bls12_381_g2"
  | N_IAdd_bls12_381_fr -> "N_IAdd_bls12_381_fr"
  | N_IMul_bls12_381_g1 -> "N_IMul_bls12_381_g1"
  | N_IMul_bls12_381_g2 -> "N_IMul_bls12_381_g2"
  | N_IMul_bls12_381_fr -> "N_IMul_bls12_381_fr"
  | N_INeg_bls12_381_g1 -> "N_INeg_bls12_381_g1"
  | N_INeg_bls12_381_g2 -> "N_INeg_bls12_381_g2"
  | N_INeg_bls12_381_fr -> "N_INeg_bls12_381_fr"
  | N_IPairing_check_bls12_381 -> "N_IPairing_check_bls12_381"
  | N_IMul_bls12_381_fr_z -> "N_IMul_bls12_381_fr_z"
  | N_IMul_bls12_381_z_fr -> "N_IMul_bls12_381_z_fr"
  | N_IInt_bls12_381_z_fr -> "N_IInt_bls12_381_z_fr"
  | N_IComb -> "N_IComb"
  | N_IUncomb -> "N_IUncomb"
  | N_IComb_get -> "N_IComb_get"
  | N_IComb_set -> "N_IComb_set"
  | N_ITicket -> "N_ITicket"
  | N_IRead_ticket -> "N_IRead_ticket"
  | N_ISplit_ticket -> "N_ISplit_ticket"
  | N_IJoin_tickets -> "N_IJoin_tickets"
  | N_ISapling_empty_state -> "N_ISapling_empty_state"
  | N_ISapling_verify_update -> "N_ISapling_verify_update"
  | N_IMap_get_and_update -> "N_IMap_get_and_update"
  | N_IBig_map_get_and_update -> "N_IBig_map_get_and_update"
  | N_IHalt -> "N_IHalt"
  | N_ILog -> "N_ILog"
  | N_IOpen_chest -> "N_IOpen_chest"
  | N_IEmit -> "N_IEmit"

let string_of_continuation_name : continuation_name -> string =
 fun c ->
  match c with
  | N_KNil -> "N_KNil"
  | N_KCons -> "N_KCons"
  | N_KReturn -> "N_KReturn"
  | N_KView_exit -> "N_KView_exit"
  | N_KMap_head -> "N_KMap_head"
  | N_KUndip -> "N_KUndip"
  | N_KLoop_in -> "N_KLoop_in"
  | N_KLoop_in_left -> "N_KLoop_in_left"
  | N_KIter_empty -> "N_KIter_empty"
  | N_KIter_nonempty -> "N_KIter_nonempty"
  | N_KList_enter_body -> "N_KList_enter_body"
  | N_KList_exit_body -> "N_KList_exit_body"
  | N_KMap_enter_body -> "N_KMap_enter_body"
  | N_KMap_exit_body -> "N_KMap_exit_body"
  | N_KLog -> "N_KLog"

let string_of_instr_or_cont name =
  match name with
  | Instr_name instr_name -> string_of_instruction_name instr_name
  | Cont_name cont_name -> string_of_continuation_name cont_name

(* ------------------------------------------------------------------------- *)

type args = arg list

and arg = {name : id; arg : Size.t}

let nullary : args = []

let unary xn x : args = [{name = xn; arg = x}]

let binary xn x yn y : args = {name = xn; arg = x} :: unary yn y

let ternary xn x yn y zn z : args = {name = xn; arg = x} :: binary yn y zn z

let quaternary wn w xn x yn y zn z : args =
  {name = wn; arg = w} :: ternary xn x yn y zn z

let pp_arg fmtr {name; arg} = Format.fprintf fmtr "%s = %a" name Size.pp arg

let pp_args fmtr args =
  Format.pp_print_list
    ~pp_sep:(fun fmtr () -> Format.fprintf fmtr ";")
    pp_arg
    fmtr
    args

type ir_sized_step = {name : instr_or_cont_name; args : args}

type t = ir_sized_step list

let ir_sized_step instr_name args = {name = Instr_name instr_name; args}

let cont_sized_step cont_name args = {name = Cont_name cont_name; args}

(* ------------------------------------------------------------------------- *)

(* Changing the ordering breaks the workload file compatibility *)
let all_instructions =
  [
    N_IDrop;
    N_IDup;
    N_ISwap;
    N_IPush;
    N_ICons_pair;
    N_ICar;
    N_ICdr;
    N_ICons_some;
    N_ICons_none;
    N_IIf_none;
    N_IOpt_map_none;
    N_IOpt_map_some;
    N_ILeft;
    N_IRight;
    N_IIf_left;
    N_ICons_list;
    N_INil;
    N_IIf_cons;
    N_IList_map;
    N_IList_iter;
    N_IIter;
    N_IList_size;
    N_IEmpty_set;
    N_ISet_iter;
    N_ISet_mem;
    N_ISet_update;
    N_ISet_size;
    N_IEmpty_map;
    N_IMap_map;
    N_IMap_iter;
    N_IMap_mem;
    N_IMap_get;
    N_IMap_update;
    N_IMap_size;
    N_IEmpty_big_map;
    N_IBig_map_mem;
    N_IBig_map_get;
    N_IBig_map_update;
    N_IConcat_string;
    N_IConcat_string_pair;
    N_ISlice_string;
    N_IString_size;
    N_IConcat_bytes;
    N_IConcat_bytes_pair;
    N_ISlice_bytes;
    N_IBytes_size;
    N_IBytes_nat;
    N_INat_bytes;
    N_IBytes_int;
    N_IInt_bytes;
    N_IAdd_seconds_to_timestamp;
    N_IAdd_timestamp_to_seconds;
    N_ISub_timestamp_seconds;
    N_IDiff_timestamps;
    N_IAdd_tez;
    N_ISub_tez;
    N_ISub_tez_legacy;
    N_IMul_teznat;
    N_IMul_nattez;
    N_IEdiv_teznat;
    N_IEdiv_tez;
    N_IOr;
    N_IAnd;
    N_IXor;
    N_INot;
    N_IIs_nat;
    N_INeg;
    N_IAbs_int;
    N_IInt_nat;
    N_IAdd_int;
    N_IAdd_nat;
    N_ISub_int;
    N_IMul_int;
    N_IMul_nat;
    N_IEdiv_int;
    N_IEdiv_nat;
    N_ILsl_nat;
    N_ILsr_nat;
    N_IOr_nat;
    N_IAnd_nat;
    N_IAnd_int_nat;
    N_IXor_nat;
    N_INot_int;
    N_IIf;
    N_ILoop_in;
    N_ILoop_out;
    N_ILoop_left_in;
    N_ILoop_left_out;
    N_IDip;
    N_IExec;
    N_IApply;
    N_ILambda_lam;
    N_ILambda_lamrec;
    N_IFailwith;
    N_ICompare;
    N_IEq;
    N_INeq;
    N_ILt;
    N_IGt;
    N_ILe;
    N_IGe;
    N_IAddress;
    N_IContract;
    N_ITransfer_tokens;
    N_IImplicit_account;
    N_ICreate_contract;
    N_ISet_delegate;
    N_INow;
    N_IMin_block_time;
    N_IBalance;
    N_ICheck_signature_ed25519;
    N_ICheck_signature_secp256k1;
    N_ICheck_signature_p256;
    N_ICheck_signature_bls;
    N_IHash_key;
    N_IPack;
    N_IUnpack;
    N_IBlake2b;
    N_ISha256;
    N_ISha512;
    N_ISource;
    N_ISender;
    N_ISelf;
    N_IAmount;
    N_IDig;
    N_IDug;
    N_IDipN;
    N_IDropN;
    N_IDupN;
    N_IChainId;
    N_ILevel;
    N_IView;
    N_ISelf_address;
    N_INever;
    N_IUnpair;
    N_IVoting_power;
    N_ITotal_voting_power;
    N_IKeccak;
    N_ISha3;
    N_IAdd_bls12_381_g1;
    N_IAdd_bls12_381_g2;
    N_IAdd_bls12_381_fr;
    N_IMul_bls12_381_g1;
    N_IMul_bls12_381_g2;
    N_IMul_bls12_381_fr;
    N_INeg_bls12_381_g1;
    N_INeg_bls12_381_g2;
    N_INeg_bls12_381_fr;
    N_IPairing_check_bls12_381;
    N_IMul_bls12_381_fr_z;
    N_IMul_bls12_381_z_fr;
    N_IInt_bls12_381_z_fr;
    N_IComb;
    N_IUncomb;
    N_IComb_get;
    N_IComb_set;
    N_ITicket;
    N_IRead_ticket;
    N_ISplit_ticket;
    N_IJoin_tickets;
    N_ISapling_empty_state;
    N_ISapling_verify_update;
    N_IMap_get_and_update;
    N_IBig_map_get_and_update;
    N_IHalt;
    N_ILog;
    N_IOpen_chest;
    N_IEmit;
    N_ILsl_bytes;
    N_ILsr_bytes;
    N_IOr_bytes;
    N_IAnd_bytes;
    N_IXor_bytes;
    N_INot_bytes;
    N_IUnit;
  ]

(* Changing the ordering breaks the workload file compatibility *)
let all_continuations =
  [
    N_KNil;
    N_KCons;
    N_KReturn;
    N_KView_exit;
    N_KMap_head;
    N_KUndip;
    N_KLoop_in;
    N_KLoop_in_left;
    N_KIter_empty;
    N_KIter_nonempty;
    N_KList_enter_body;
    N_KList_exit_body;
    N_KMap_enter_body;
    N_KMap_exit_body;
    N_KLog;
  ]

let instruction_name_encoding =
  let open Data_encoding in
  def "instruction_name_encoding"
  @@ string_enum
       (List.map
          (fun instr_name ->
            (string_of_instruction_name instr_name, instr_name))
          all_instructions)

let continuation_name_encoding =
  let open Data_encoding in
  def "continuation_name_encoding"
  @@ string_enum
       (List.map
          (fun cont_name -> (string_of_continuation_name cont_name, cont_name))
          all_continuations)

let args_encoding =
  let open Data_encoding in
  def "args_encoding"
  @@ list
       (conv
          (fun {name; arg} -> (name, arg))
          (fun (name, arg) -> {name; arg})
          (tup2 string Size.encoding))

let instr_or_cont_name_encoding =
  let open Data_encoding in
  def "instr_or_cont_name"
  @@ union
       [
         case
           ~title:"instr_name"
           (Tag 0)
           instruction_name_encoding
           (function Instr_name name -> Some name | _ -> None)
           (fun name -> Instr_name name);
         case
           ~title:"cont_name"
           (Tag 1)
           continuation_name_encoding
           (function Cont_name name -> Some name | _ -> None)
           (fun name -> Cont_name name);
       ]

let ir_sized_step_encoding =
  let open Data_encoding in
  def "ir_sized_step_encoding"
  @@ conv
       (fun {name; args} -> (name, args))
       (fun (name, args) -> {name; args})
       (tup2 instr_or_cont_name_encoding args_encoding)

let encoding =
  let open Data_encoding in
  def "interpreter_trace_encoding" @@ list ir_sized_step_encoding

(* ------------------------------------------------------------------------- *)

module Instructions = struct
  let drop = ir_sized_step N_IDrop nullary

  let dup = ir_sized_step N_IDup nullary

  let swap = ir_sized_step N_ISwap nullary

  let push = ir_sized_step N_IPush nullary

  let unit = ir_sized_step N_IUnit nullary

  let cons_pair = ir_sized_step N_ICons_pair nullary

  let car = ir_sized_step N_ICar nullary

  let cdr = ir_sized_step N_ICdr nullary

  let cons_some = ir_sized_step N_ICons_some nullary

  let cons_none = ir_sized_step N_ICons_none nullary

  let if_none = ir_sized_step N_IIf_none nullary

  let opt_map ~is_some =
    if is_some then ir_sized_step N_IOpt_map_some nullary
    else ir_sized_step N_IOpt_map_none nullary

  let left = ir_sized_step N_ILeft nullary

  let right = ir_sized_step N_IRight nullary

  let if_left = ir_sized_step N_IIf_left nullary

  let cons_list = ir_sized_step N_ICons_list nullary

  let nil = ir_sized_step N_INil nullary

  let if_cons = ir_sized_step N_IIf_cons nullary

  let list_map = ir_sized_step N_IList_map nullary

  let list_iter = ir_sized_step N_IList_iter nullary

  let iter = ir_sized_step N_IIter nullary

  let list_size _list = ir_sized_step N_IList_size nullary

  let empty_set = ir_sized_step N_IEmpty_set nullary

  let set_iter set = ir_sized_step N_ISet_iter (unary "set" set)

  let set_mem elt set = ir_sized_step N_ISet_mem (binary "elt" elt "set" set)

  let set_update elt set =
    ir_sized_step N_ISet_update (binary "elt" elt "set" set)

  let set_size _set = ir_sized_step N_ISet_size nullary

  let empty_map = ir_sized_step N_IEmpty_map nullary

  let map_map map = ir_sized_step N_IMap_map (unary "map" map)

  let map_iter map = ir_sized_step N_IMap_iter (unary "map" map)

  let map_mem key map = ir_sized_step N_IMap_mem (binary "key" key "map" map)

  let map_get key map = ir_sized_step N_IMap_get (binary "key" key "map" map)

  let map_update key map =
    ir_sized_step N_IMap_update (binary "key" key "map" map)

  let map_size _map = ir_sized_step N_IMap_size nullary

  let empty_big_map = ir_sized_step N_IEmpty_big_map nullary

  let big_map_mem key big_map =
    ir_sized_step N_IBig_map_mem (binary "key" key "big_map" big_map)

  let big_map_get key big_map =
    ir_sized_step N_IBig_map_get (binary "key" key "big_map" big_map)

  let big_map_update key big_map =
    ir_sized_step N_IBig_map_update (binary "key" key "big_map" big_map)

  let big_map_get_and_update key big_map =
    ir_sized_step N_IBig_map_get_and_update (binary "key" key "big_map" big_map)

  let concat_string total_bytes list =
    ir_sized_step
      N_IConcat_string
      (binary "total_bytes" total_bytes "list" list)

  let concat_string_pair str1 str2 =
    ir_sized_step N_IConcat_string_pair (binary "str1" str1 "str2" str2)

  let slice_string string =
    ir_sized_step N_ISlice_string (unary "string" string)

  let string_size _string = ir_sized_step N_IString_size nullary

  let concat_bytes total_bytes list =
    ir_sized_step N_IConcat_bytes (binary "total_bytes" total_bytes "list" list)

  let concat_bytes_pair str1 str2 =
    ir_sized_step N_IConcat_bytes_pair (binary "str1" str1 "str2" str2)

  let slice_bytes bytes = ir_sized_step N_ISlice_bytes (unary "bytes" bytes)

  let bytes_size = ir_sized_step N_IBytes_size nullary

  let lsl_bytes bytes shift =
    ir_sized_step N_ILsl_bytes (binary "bytes" bytes "shift" shift)

  let lsr_bytes bytes shift =
    ir_sized_step N_ILsr_bytes (binary "bytes" bytes "shift" shift)

  let or_bytes bytes1 bytes2 =
    ir_sized_step N_IOr_bytes (binary "bytes1" bytes1 "bytes2" bytes2)

  let and_bytes bytes1 bytes2 =
    ir_sized_step N_IAnd_bytes (binary "bytes1" bytes1 "bytes2" bytes2)

  let xor_bytes bytes1 bytes2 =
    ir_sized_step N_IXor_bytes (binary "bytes1" bytes1 "bytes2" bytes2)

  let not_bytes bytes = ir_sized_step N_INot_bytes (unary "bytes" bytes)

  let bytes_nat nat = ir_sized_step N_IBytes_nat (unary "nat" nat)

  let nat_bytes bytes = ir_sized_step N_INat_bytes (unary "bytes" bytes)

  let bytes_int int = ir_sized_step N_IBytes_int (unary "int" int)

  let int_bytes bytes = ir_sized_step N_IInt_bytes (unary "bytes" bytes)

  let add_seconds_to_timestamp seconds tstamp =
    ir_sized_step
      N_IAdd_seconds_to_timestamp
      (binary "seconds" seconds "tstamp" tstamp)

  let add_timestamp_to_seconds tstamp seconds =
    ir_sized_step
      N_IAdd_timestamp_to_seconds
      (binary "tstamp" tstamp "seconds" seconds)

  let sub_timestamp_seconds tstamp seconds =
    ir_sized_step
      N_ISub_timestamp_seconds
      (binary "tstamp" tstamp "seconds" seconds)

  let diff_timestamps tstamp1 tstamp2 =
    ir_sized_step
      N_IDiff_timestamps
      (binary "tstamp1" tstamp1 "tstamp2" tstamp2)

  let add_tez _tez1 _tez2 = ir_sized_step N_IAdd_tez nullary

  let sub_tez _tez1 _tez2 = ir_sized_step N_ISub_tez nullary

  let sub_tez_legacy _tez1 _tez2 = ir_sized_step N_ISub_tez_legacy nullary

  let mul_teznat _tez _nat = ir_sized_step N_IMul_teznat nullary

  let mul_nattez _nat _tez = ir_sized_step N_IMul_nattez nullary

  let ediv_teznat _tez _nat = ir_sized_step N_IEdiv_teznat nullary

  let ediv_tez _tez1 _tez2 = ir_sized_step N_IEdiv_tez nullary

  let or_ = ir_sized_step N_IOr nullary

  let and_ = ir_sized_step N_IAnd nullary

  let xor_ = ir_sized_step N_IXor nullary

  let not_ = ir_sized_step N_INot nullary

  let is_nat _int = ir_sized_step N_IIs_nat nullary

  let neg int = ir_sized_step N_INeg (unary "int" int)

  let abs_int int = ir_sized_step N_IAbs_int (unary "int" int)

  let int_nat _nat = ir_sized_step N_IInt_nat nullary

  let add_int int1 int2 =
    ir_sized_step N_IAdd_int (binary "int1" int1 "int2" int2)

  let add_nat nat1 nat2 =
    ir_sized_step N_IAdd_nat (binary "nat1" nat1 "nat2" nat2)

  let sub_int int1 int2 =
    ir_sized_step N_ISub_int (binary "int1" int1 "int2" int2)

  let mul_int int1 int2 =
    ir_sized_step N_IMul_int (binary "int1" int1 "int2" int2)

  let mul_nat nat int = ir_sized_step N_IMul_nat (binary "nat" nat "int" int)

  let ediv_int int1 int2 =
    ir_sized_step N_IEdiv_int (binary "int1" int1 "int2" int2)

  let ediv_nat nat int = ir_sized_step N_IEdiv_nat (binary "nat" nat "int" int)

  let lsl_nat nat1 _shift = ir_sized_step N_ILsl_nat (unary "nat" nat1)

  let lsr_nat nat1 _shift = ir_sized_step N_ILsr_nat (unary "nat" nat1)

  let or_nat nat1 nat2 =
    ir_sized_step N_IOr_nat (binary "nat1" nat1 "nat2" nat2)

  let and_nat nat1 nat2 =
    ir_sized_step N_IAnd_nat (binary "nat1" nat1 "nat2" nat2)

  let and_int_nat int nat =
    ir_sized_step N_IAnd_int_nat (binary "int" int "nat" nat)

  let xor_nat nat1 nat2 =
    ir_sized_step N_IXor_nat (binary "nat1" nat1 "nat2" nat2)

  let not_int int = ir_sized_step N_INot_int (unary "int" int)

  let if_ = ir_sized_step N_IIf nullary

  let loop bool =
    if bool then ir_sized_step N_ILoop_in nullary
    else ir_sized_step N_ILoop_out nullary

  let loop_left or_ =
    if or_ then ir_sized_step N_ILoop_left_in nullary
    else ir_sized_step N_ILoop_left_out nullary

  let dip = ir_sized_step N_IDip nullary

  let exec = ir_sized_step N_IExec nullary

  let apply ~(rec_flag : bool) =
    ir_sized_step N_IApply (unary "rec" (if rec_flag then 1 else 0))

  let lambda ~(rec_flag : bool) =
    if rec_flag then ir_sized_step N_ILambda_lamrec nullary
    else ir_sized_step N_ILambda_lam nullary

  let failwith_ = ir_sized_step N_IFailwith nullary

  let compare arg1 arg2 =
    ir_sized_step N_ICompare (binary "arg1" arg1 "arg2" arg2)

  let eq = ir_sized_step N_IEq nullary

  let neq = ir_sized_step N_INeq nullary

  let lt = ir_sized_step N_ILt nullary

  let gt = ir_sized_step N_IGt nullary

  let le = ir_sized_step N_ILe nullary

  let ge = ir_sized_step N_IGe nullary

  let address = ir_sized_step N_IAddress nullary

  let contract = ir_sized_step N_IContract nullary

  let transfer_tokens = ir_sized_step N_ITransfer_tokens nullary

  let implicit_account = ir_sized_step N_IImplicit_account nullary

  let create_contract = ir_sized_step N_ICreate_contract nullary

  let set_delegate = ir_sized_step N_ISet_delegate nullary

  let now = ir_sized_step N_INow nullary

  let min_block_time = ir_sized_step N_IMin_block_time nullary

  let balance = ir_sized_step N_IBalance nullary

  let check_signature_ed25519 _pk _signature message =
    ir_sized_step N_ICheck_signature_ed25519 (unary "message" message)

  let check_signature_secp256k1 _pk _signature message =
    ir_sized_step N_ICheck_signature_secp256k1 (unary "message" message)

  let check_signature_p256 _pk _signature message =
    ir_sized_step N_ICheck_signature_p256 (unary "message" message)

  let check_signature_bls _pk _signature message =
    ir_sized_step N_ICheck_signature_bls (unary "message" message)

  let hash_key = ir_sized_step N_IHash_key nullary

  let pack (micheline_size : Size.micheline_size) =
    ir_sized_step
      N_IPack
      (ternary
         "micheline_nodes"
         micheline_size.traversal
         "micheline_int_bytes"
         micheline_size.int_bytes
         "micheline_string_bytes"
         micheline_size.string_bytes)

  let unpack = ir_sized_step N_IUnpack nullary

  let blake2b bytes = ir_sized_step N_IBlake2b (unary "bytes" bytes)

  let sha256 bytes = ir_sized_step N_ISha256 (unary "bytes" bytes)

  let sha512 bytes = ir_sized_step N_ISha512 (unary "bytes" bytes)

  let source = ir_sized_step N_ISource nullary

  let sender = ir_sized_step N_ISender nullary

  let self = ir_sized_step N_ISelf nullary

  let amount = ir_sized_step N_IAmount nullary

  let dig depth = ir_sized_step N_IDig (unary "depth" depth)

  let dug depth = ir_sized_step N_IDug (unary "depth" depth)

  let dipn depth = ir_sized_step N_IDipN (unary "depth" depth)

  let dropn depth = ir_sized_step N_IDropN (unary "depth" depth)

  let dupn depth = ir_sized_step N_IDupN (unary "depth" depth)

  let chain_id = ir_sized_step N_IChainId nullary

  let level = ir_sized_step N_ILevel nullary

  let view = ir_sized_step N_IView nullary

  let self_address = ir_sized_step N_ISelf_address nullary

  let never = ir_sized_step N_INever nullary

  let unpair = ir_sized_step N_IUnpair nullary

  let voting_power = ir_sized_step N_IVoting_power nullary

  let total_voting_power = ir_sized_step N_ITotal_voting_power nullary

  let keccak bytes = ir_sized_step N_IKeccak (unary "bytes" bytes)

  let sha3 bytes = ir_sized_step N_ISha3 (unary "bytes" bytes)

  let add_bls12_381_g1 = ir_sized_step N_IAdd_bls12_381_g1 nullary

  let add_bls12_381_g2 = ir_sized_step N_IAdd_bls12_381_g2 nullary

  let add_bls12_381_fr = ir_sized_step N_IAdd_bls12_381_fr nullary

  let mul_bls12_381_g1 = ir_sized_step N_IMul_bls12_381_g1 nullary

  let mul_bls12_381_g2 = ir_sized_step N_IMul_bls12_381_g2 nullary

  let mul_bls12_381_fr = ir_sized_step N_IMul_bls12_381_fr nullary

  let neg_bls12_381_g1 = ir_sized_step N_INeg_bls12_381_g1 nullary

  let neg_bls12_381_g2 = ir_sized_step N_INeg_bls12_381_g2 nullary

  let neg_bls12_381_fr = ir_sized_step N_INeg_bls12_381_fr nullary

  let pairing_check_bls12_381 length =
    ir_sized_step N_IPairing_check_bls12_381 (unary "length" length)

  let mul_bls12_381_fr_z nat =
    ir_sized_step N_IMul_bls12_381_fr_z (unary "nat" nat)

  let mul_bls12_381_z_fr nat =
    ir_sized_step N_IMul_bls12_381_z_fr (unary "nat" nat)

  let int_bls12_381_z_fr = ir_sized_step N_IInt_bls12_381_z_fr nullary

  let comb depth = ir_sized_step N_IComb (unary "depth" depth)

  let uncomb depth = ir_sized_step N_IUncomb (unary "depth" depth)

  let comb_get key = ir_sized_step N_IComb_get (unary "key" key)

  let comb_set key = ir_sized_step N_IComb_set (unary "key" key)

  let ticket = ir_sized_step N_ITicket nullary

  let read_ticket = ir_sized_step N_IRead_ticket nullary

  let split_ticket nat1 nat2 =
    ir_sized_step N_ISplit_ticket (binary "nat1" nat1 "nat2" nat2)

  let join_tickets size1 size2 size3 size4 =
    ir_sized_step
      N_IJoin_tickets
      (quaternary
         "contents1"
         size1
         "contents2"
         size2
         "amount1"
         size3
         "amount2"
         size4)

  let sapling_empty_state = ir_sized_step N_ISapling_empty_state nullary

  let sapling_verify_update inputs outputs _bound_data _state =
    ir_sized_step
      N_ISapling_verify_update
      (binary "inputs" inputs "outputs" outputs)

  let map_get_and_update key_size map_size =
    ir_sized_step
      N_IMap_get_and_update
      (binary "key_size" key_size "map_size" map_size)

  let halt = ir_sized_step N_IHalt nullary

  let log = ir_sized_step N_ILog nullary

  let open_chest log_time size =
    ir_sized_step N_IOpen_chest (binary "log_time" log_time "size" size)

  (** cost model for the EMIT instruction *)
  let emit = ir_sized_step N_IEmit nullary
end

module Control = struct
  let nil = cont_sized_step N_KNil nullary

  let cons = cont_sized_step N_KCons nullary

  let return = cont_sized_step N_KReturn nullary

  let view_exit = cont_sized_step N_KView_exit nullary

  let map_head = cont_sized_step N_KMap_head nullary

  let undip = cont_sized_step N_KUndip nullary

  let loop_in = cont_sized_step N_KLoop_in nullary

  let loop_in_left = cont_sized_step N_KLoop_in_left nullary

  let iter size =
    if size = 0 then cont_sized_step N_KIter_empty nullary
    else cont_sized_step N_KIter_nonempty nullary

  let list_enter_body xs_size ys_size =
    cont_sized_step
      N_KList_enter_body
      (binary "xs_size" xs_size "ys_size" ys_size)

  let list_exit_body = cont_sized_step N_KList_exit_body nullary

  let map_enter_body size =
    cont_sized_step N_KMap_enter_body (unary "size" size)

  let map_exit_body key_size map_size =
    cont_sized_step N_KMap_exit_body (binary "key" key_size "map" map_size)

  let log = cont_sized_step N_KLog nullary
end

(* ------------------------------------------------------------------------- *)

open Script_typed_ir

let extract_compare_sized_step :
    type a. a comparable_ty -> a -> a -> ir_sized_step =
 fun comparable_ty x y ->
  Instructions.compare
    (Size.size_of_comparable_value comparable_ty x)
    (Size.size_of_comparable_value comparable_ty y)

let extract_ir_sized_step :
    type bef_top bef res_top res.
    Alpha_context.t ->
    (bef_top, bef, res_top, res) Script_typed_ir.kinstr ->
    bef_top * bef ->
    ir_sized_step =
 fun ctxt instr stack ->
  let open Script_typed_ir in
  match (instr, stack) with
  | IDrop (_, _), _ -> Instructions.drop
  | IDup (_, _), _ -> Instructions.dup
  | ISwap (_, _), _ -> Instructions.swap
  | IPush (_, _, _, _), _ -> Instructions.push
  | IUnit (_, _), _ -> Instructions.unit
  | ICons_pair (_, _), _ -> Instructions.cons_pair
  | ICar (_, _), _ -> Instructions.car
  | ICdr (_, _), _ -> Instructions.cdr
  | IUnpair (_, _), _ -> Instructions.unpair
  | ICons_some (_, _), _ -> Instructions.cons_some
  | ICons_none (_, _, _), _ -> Instructions.cons_none
  | IIf_none _, _ -> Instructions.if_none
  | IOpt_map _, (opt, _) ->
      let is_some = match opt with None -> false | Some _ -> true in
      Instructions.opt_map ~is_some
  | ICons_left (_, _, _), _ -> Instructions.left
  | ICons_right (_, _, _), _ -> Instructions.right
  | IIf_left _, _ -> Instructions.if_left
  | ICons_list (_, _), _ -> Instructions.cons_list
  | INil (_, _, _), _ -> Instructions.nil
  | IIf_cons _, _ -> Instructions.if_cons
  | IList_iter (_, _, _, _), _ -> Instructions.list_iter
  | IList_map (_, _, _, _), _ -> Instructions.list_map
  | IList_size (_, _), (list, _) -> Instructions.list_size (Size.list list)
  | IEmpty_set (_, _, _), _ -> Instructions.empty_set
  | ISet_iter _, (set, _) -> Instructions.set_iter (Size.set set)
  | ISet_mem (_, _), (v, (set, _)) ->
      let (module S) = Script_set.get set in
      let sz = S.OPS.elt_size v in
      Instructions.set_mem sz (Size.set set)
  | ISet_update (_, _), (v, (_flag, (set, _))) ->
      let (module S) = Script_set.get set in
      let sz = S.OPS.elt_size v in
      Instructions.set_update sz (Size.set set)
  | ISet_size (_, _), (set, _) -> Instructions.set_size (Size.set set)
  | IEmpty_map (_, _, _, _), _ -> Instructions.empty_map
  | IMap_map _, (map, _) -> Instructions.map_map (Size.map map)
  | IMap_iter _, (map, _) -> Instructions.map_iter (Size.map map)
  | IMap_mem (_, _), (v, (map, _)) ->
      let (module Map) = Script_map.get_module map in
      let key_size = Map.OPS.key_size v in
      Instructions.map_mem key_size (Size.map map)
  | IMap_get (_, _), (v, (map, _)) ->
      let (module Map) = Script_map.get_module map in
      let key_size = Map.OPS.key_size v in
      Instructions.map_get key_size (Size.map map)
  | IMap_update (_, _), (v, (_elt_opt, (map, _))) ->
      let (module Map) = Script_map.get_module map in
      let key_size = Map.OPS.key_size v in
      Instructions.map_update key_size (Size.map map)
  | IMap_get_and_update (_, _), (v, (_elt_opt, (map, _))) ->
      let (module Map) = Script_map.get_module map in
      let key_size = Map.OPS.key_size v in
      Instructions.map_get_and_update key_size (Size.map map)
  | IMap_size (_, _), (map, _) -> Instructions.map_size (Size.map map)
  | IEmpty_big_map (_, _, _, _), _ -> Instructions.empty_big_map
  | IBig_map_mem (_, _), (v, (Big_map {diff = {size; _}; key_type; _}, _)) ->
      let key_size = Size.size_of_comparable_value key_type v in
      Instructions.big_map_mem key_size (Size.of_int size)
  | IBig_map_get (_, _), (v, (Big_map {diff = {size; _}; key_type; _}, _)) ->
      let key_size = Size.size_of_comparable_value key_type v in
      Instructions.big_map_get key_size (Size.of_int size)
  | ( IBig_map_update (_, _),
      (v, (_, (Big_map {diff = {size; _}; key_type; _}, _))) ) ->
      let key_size = Size.size_of_comparable_value key_type v in
      Instructions.big_map_update key_size (Size.of_int size)
  | ( IBig_map_get_and_update (_, _),
      (v, (_, (Big_map {diff = {size; _}; key_type; _}, _))) ) ->
      let key_size = Size.size_of_comparable_value key_type v in
      Instructions.big_map_get_and_update key_size (Size.of_int size)
  | IConcat_string (_, _), (ss, _) ->
      let list_size = Size.list ss in
      let total_bytes =
        List.fold_left
          (fun x s -> Size.(add x (script_string s)))
          Size.zero
          ss.elements
      in
      Instructions.concat_string list_size total_bytes
  | IConcat_string_pair (_, _), (s1, (s2, _)) ->
      Instructions.concat_string_pair
        (Size.script_string s1)
        (Size.script_string s2)
  | ISlice_string (_, _), (_off, (_len, (s, _))) ->
      Instructions.slice_string (Size.script_string s)
  | IString_size (_, _), (s, _) ->
      Instructions.string_size (Size.script_string s)
  | IConcat_bytes (_, _), (ss, _) ->
      let list_size = Size.list ss in
      let total_bytes =
        List.fold_left (fun x s -> Size.(add x (bytes s))) Size.zero ss.elements
      in
      Instructions.concat_bytes list_size total_bytes
  | IConcat_bytes_pair (_, _), (s1, (s2, _)) ->
      Instructions.concat_bytes_pair (Size.bytes s1) (Size.bytes s2)
  | ISlice_bytes (_, _), (_off, (_len, (s, _))) ->
      Instructions.slice_bytes (Size.bytes s)
  | IBytes_size (_, _), _ -> Instructions.bytes_size
  | IBytes_nat (_, _), (n, _) -> Instructions.bytes_nat (Size.integer n)
  | INat_bytes (_, _), (b, _) -> Instructions.nat_bytes (Size.bytes b)
  | IBytes_int (_, _), (n, _) -> Instructions.bytes_int (Size.integer n)
  | IInt_bytes (_, _), (b, _) -> Instructions.int_bytes (Size.bytes b)
  | IAdd_seconds_to_timestamp (_, _), (s, (t, _)) ->
      Instructions.add_seconds_to_timestamp (Size.timestamp t) (Size.integer s)
  | IAdd_timestamp_to_seconds (_, _), (t, (s, _)) ->
      Instructions.add_timestamp_to_seconds (Size.timestamp t) (Size.integer s)
  | ISub_timestamp_seconds (_, _), (t, (s, _)) ->
      Instructions.sub_timestamp_seconds (Size.timestamp t) (Size.integer s)
  | IDiff_timestamps (_, _), (t1, (t2, _)) ->
      Instructions.diff_timestamps (Size.timestamp t1) (Size.timestamp t2)
  | IAdd_tez (_, _), (x, (y, _)) ->
      Instructions.add_tez (Size.mutez x) (Size.mutez y)
  | ISub_tez (_, _), (x, (y, _)) ->
      Instructions.sub_tez (Size.mutez x) (Size.mutez y)
  | ISub_tez_legacy (_, _), (x, (y, _)) ->
      Instructions.sub_tez_legacy (Size.mutez x) (Size.mutez y)
  | IMul_teznat (_, _), (x, (y, _)) ->
      Instructions.mul_teznat (Size.mutez x) (Size.integer y)
  | IMul_nattez (_, _), (x, (y, _)) ->
      Instructions.mul_nattez (Size.integer x) (Size.mutez y)
  | IEdiv_teznat (_, _), (x, (y, _)) ->
      Instructions.ediv_teznat (Size.mutez x) (Size.integer y)
  | IEdiv_tez (_, _), (x, (y, _)) ->
      Instructions.ediv_tez (Size.mutez x) (Size.mutez y)
  | IOr (_, _), _ -> Instructions.or_
  | IAnd (_, _), _ -> Instructions.and_
  | IXor (_, _), _ -> Instructions.xor_
  | INot (_, _), _ -> Instructions.not_
  | IIs_nat (_, _), (x, _) -> Instructions.is_nat (Size.integer x)
  | INeg (_, _), (x, _) -> Instructions.neg (Size.integer x)
  | IAbs_int (_, _), (x, _) -> Instructions.abs_int (Size.integer x)
  | IInt_nat (_, _), (x, _) -> Instructions.int_nat (Size.integer x)
  | IAdd_int (_, _), (x, (y, _)) ->
      Instructions.add_int (Size.integer x) (Size.integer y)
  | IAdd_nat (_, _), (x, (y, _)) ->
      Instructions.add_nat (Size.integer x) (Size.integer y)
  | ISub_int (_, _), (x, (y, _)) ->
      Instructions.sub_int (Size.integer x) (Size.integer y)
  | IMul_int (_, _), (x, (y, _)) ->
      Instructions.mul_int (Size.integer x) (Size.integer y)
  | IMul_nat (_, _), (x, (y, _)) ->
      Instructions.mul_nat (Size.integer x) (Size.integer y)
  | IEdiv_int (_, _), (x, (y, _)) ->
      Instructions.ediv_int (Size.integer x) (Size.integer y)
  | IEdiv_nat (_, _), (x, (y, _)) ->
      Instructions.ediv_nat (Size.integer x) (Size.integer y)
  | ILsl_nat (_, _), (x, (y, _)) ->
      Instructions.lsl_nat (Size.integer x) (Size.integer y)
  | ILsr_nat (_, _), (x, (y, _)) ->
      Instructions.lsr_nat (Size.integer x) (Size.integer y)
  | IOr_nat (_, _), (x, (y, _)) ->
      Instructions.or_nat (Size.integer x) (Size.integer y)
  | IAnd_nat (_, _), (x, (y, _)) ->
      Instructions.and_nat (Size.integer x) (Size.integer y)
  | IAnd_int_nat (_, _), (x, (y, _)) ->
      Instructions.and_int_nat (Size.integer x) (Size.integer y)
  | IXor_nat (_, _), (x, (y, _)) ->
      Instructions.xor_nat (Size.integer x) (Size.integer y)
  | INot_int (_, _), (x, _) -> Instructions.not_int (Size.integer x)
  | IIf _, _ -> Instructions.if_
  | ILoop (_, _, _), (b, _) -> Instructions.loop b
  | ILoop_left (_, _, _), (or_, _) ->
      let or_ = match or_ with L _ -> true | R _ -> false in
      Instructions.loop_left or_
  | IDip (_, _, _, _), _ -> Instructions.dip
  | IExec (_, _, _), _ -> Instructions.exec
  | IApply (_, _, _), (_, (l, _)) ->
      let rec_flag = match l with Lam _ -> false | LamRec _ -> true in
      Instructions.apply ~rec_flag
  | ILambda (_, l, _), _ ->
      let rec_flag = match l with Lam _ -> false | LamRec _ -> true in
      Instructions.lambda ~rec_flag
  | IFailwith (_, _), _ -> Instructions.failwith_
  | ICompare (_, cmp_ty, _), (a, (b, _)) ->
      extract_compare_sized_step cmp_ty a b
  | IEq (_, _), _ -> Instructions.eq
  | INeq (_, _), _ -> Instructions.neq
  | ILt (_, _), _ -> Instructions.lt
  | IGt (_, _), _ -> Instructions.gt
  | ILe (_, _), _ -> Instructions.le
  | IGe (_, _), _ -> Instructions.ge
  | IAddress (_, _), _ -> Instructions.address
  | IContract (_, _, _, _), _ -> Instructions.contract
  | ITransfer_tokens (_, _), _ -> Instructions.transfer_tokens
  | IView (_, _, _, _), _ -> Instructions.view
  | IImplicit_account (_, _), _ -> Instructions.implicit_account
  | ICreate_contract _, _ -> Instructions.create_contract
  | ISet_delegate (_, _), _ -> Instructions.set_delegate
  | INow (_, _), _ -> Instructions.now
  | IBalance (_, _), _ -> Instructions.balance
  | ILevel (_, _), _ -> Instructions.level
  | ICheck_signature (_, _), (public_key, (_signature, (message, _))) -> (
      match public_key with
      | Signature.Ed25519 pk ->
          let pk = Size.of_int (Signature.Ed25519.Public_key.size pk) in
          let signature = Size.of_int Signature.Ed25519.size in
          let message = Size.bytes message in
          Instructions.check_signature_ed25519 pk signature message
      | Signature.Secp256k1 pk ->
          let pk = Size.of_int (Signature.Secp256k1.Public_key.size pk) in
          let signature = Size.of_int Signature.Secp256k1.size in
          let message = Size.bytes message in
          Instructions.check_signature_secp256k1 pk signature message
      | Signature.P256 pk ->
          let pk = Size.of_int (Signature.P256.Public_key.size pk) in
          let signature = Size.of_int Signature.P256.size in
          let message = Size.bytes message in
          Instructions.check_signature_p256 pk signature message
      | Signature.Bls pk ->
          let pk = Size.of_int (Signature.Bls.Public_key.size pk) in
          let signature = Size.of_int Signature.Bls.size in
          let message = Size.bytes message in
          Instructions.check_signature_bls pk signature message)
  | IHash_key (_, _), _ -> Instructions.hash_key
  | IPack (_, ty, _), (v, _) -> (
      let script_res =
        Lwt_main.run (Script_ir_translator.unparse_data ctxt Optimized ty v)
      in
      match script_res with
      | Ok (node, _ctxt) ->
          Instructions.pack (Size.of_micheline (Micheline.root node))
      | Error _ -> Stdlib.failwith "IPack workload: could not unparse")
  | IUnpack (_, _, _), _ -> Instructions.unpack
  | IBlake2b (_, _), (bytes, _) -> Instructions.blake2b (Size.bytes bytes)
  | ISha256 (_, _), (bytes, _) -> Instructions.sha256 (Size.bytes bytes)
  | ISha512 (_, _), (bytes, _) -> Instructions.sha512 (Size.bytes bytes)
  | ISource (_, _), _ -> Instructions.source
  | ISender (_, _), _ -> Instructions.sender
  | ISelf (_, _, _, _), _ -> Instructions.self
  | ISelf_address (_, _), _ -> Instructions.self_address
  | IAmount (_, _), _ -> Instructions.amount
  | ISapling_empty_state (_, _, _), _ -> Instructions.sapling_empty_state
  | ISapling_verify_update (_, _), (transaction, (_state, _)) ->
      let inputs = Size.sapling_transaction_inputs transaction in
      let outputs = Size.sapling_transaction_outputs transaction in
      let bound_data = Size.sapling_transaction_bound_data transaction in
      let state = Size.zero in
      Instructions.sapling_verify_update inputs outputs bound_data state
  | ISapling_verify_update_deprecated (_, _), (transaction, (_state, _)) ->
      let inputs = List.length transaction.inputs in
      let outputs = List.length transaction.outputs in
      let bound_data = Size.zero in
      let state = Size.zero in
      Instructions.sapling_verify_update inputs outputs bound_data state
  | IDig (_, n, _, _), _ -> Instructions.dig (Size.of_int n)
  | IDug (_, n, _, _), _ -> Instructions.dug (Size.of_int n)
  | IDipn (_, n, _, _, _), _ -> Instructions.dipn (Size.of_int n)
  | IDropn (_, n, _, _), _ -> Instructions.dropn (Size.of_int n)
  | IChainId (_, _), _ -> Instructions.chain_id
  | INever _, _ -> .
  | IVoting_power (_, _), _ -> Instructions.voting_power
  | ITotal_voting_power (_, _), _ -> Instructions.total_voting_power
  | IKeccak (_, _), (bytes, _) -> Instructions.keccak (Size.bytes bytes)
  | ISha3 (_, _), (bytes, _) -> Instructions.sha3 (Size.bytes bytes)
  | IAdd_bls12_381_g1 (_, _), _ -> Instructions.add_bls12_381_g1
  | IAdd_bls12_381_g2 (_, _), _ -> Instructions.add_bls12_381_g2
  | IAdd_bls12_381_fr (_, _), _ -> Instructions.add_bls12_381_fr
  | IMul_bls12_381_g1 (_, _), _ -> Instructions.mul_bls12_381_g1
  | IMul_bls12_381_g2 (_, _), _ -> Instructions.mul_bls12_381_g2
  | IMul_bls12_381_fr (_, _), _ -> Instructions.mul_bls12_381_fr
  | IMul_bls12_381_z_fr (_, _), (_fr, (z, _)) ->
      Instructions.mul_bls12_381_z_fr (Size.integer z)
  | IMul_bls12_381_fr_z (_, _), (z, _) ->
      Instructions.mul_bls12_381_fr_z (Size.integer z)
  | IInt_bls12_381_fr (_, _), _ -> Instructions.int_bls12_381_z_fr
  | INeg_bls12_381_g1 (_, _), _ -> Instructions.neg_bls12_381_g1
  | INeg_bls12_381_g2 (_, _), _ -> Instructions.neg_bls12_381_g2
  | INeg_bls12_381_fr (_, _), _ -> Instructions.neg_bls12_381_fr
  | IPairing_check_bls12_381 (_, _), (list, _) ->
      Instructions.pairing_check_bls12_381 (Size.list list)
  | IComb (_, n, _, _), _ -> Instructions.comb (Size.of_int n)
  | IUncomb (_, n, _, _), _ -> Instructions.uncomb (Size.of_int n)
  | IComb_get (_, n, _, _), _ -> Instructions.comb_get (Size.of_int n)
  | IComb_set (_, n, _, _), _ -> Instructions.comb_set (Size.of_int n)
  | IDup_n (_, n, _, _), _ -> Instructions.dupn (Size.of_int n)
  | ITicket (_, _, _), _ | ITicket_deprecated (_, _, _), _ ->
      Instructions.ticket
  | IRead_ticket (_, _, _), _ -> Instructions.read_ticket
  | ISplit_ticket (_, _), (_ticket, ((amount_a, amount_b), _)) ->
      Instructions.split_ticket (Size.integer amount_a) (Size.integer amount_b)
  | IJoin_tickets (_, cmp_ty, _), ((ticket1, ticket2), _) ->
      let size1 = Size.size_of_comparable_value cmp_ty ticket1.contents in
      let size2 = Size.size_of_comparable_value cmp_ty ticket2.contents in
      let tez1 = Size.integer (ticket1.amount :> Script_int.n Script_int.num) in
      let tez2 = Size.integer (ticket2.amount :> Script_int.n Script_int.num) in
      Instructions.join_tickets size1 size2 tez1 tez2
  | IHalt _, _ -> Instructions.halt
  | ILog _, _ -> Instructions.log
  | IOpen_chest (_, _), (_, (chest, (time, _))) ->
      let plaintext_size =
        Script_timelock.get_plaintext_size chest - 1 |> Size.of_int
      in
      let log_time = Z.log2 Z.(one + Script_int.to_zint time) |> Size.of_int in
      Instructions.open_chest log_time plaintext_size
  | IMin_block_time _, _ -> Instructions.min_block_time
  | IEmit _, _ -> Instructions.emit
  | ILsl_bytes (_, _), (x, (y, _)) ->
      let y =
        match Script_int.to_int y with
        | Some y -> y
        | None -> (* overflow *) assert false
      in
      Instructions.lsl_bytes (Size.bytes x) y
  | ILsr_bytes (_, _), (x, (y, _)) ->
      let y =
        match Script_int.to_int y with
        | Some y -> y
        | None -> (* overflow *) assert false
      in
      Instructions.lsr_bytes (Size.bytes x) y
  | IOr_bytes (_, _), (x, (y, _)) ->
      Instructions.or_bytes (Size.bytes x) (Size.bytes y)
  | IAnd_bytes (_, _), (x, (y, _)) ->
      Instructions.and_bytes (Size.bytes x) (Size.bytes y)
  | IXor_bytes (_, _), (x, (y, _)) ->
      Instructions.xor_bytes (Size.bytes x) (Size.bytes y)
  | INot_bytes (_, _), (x, _) -> Instructions.not_bytes (Size.bytes x)

let extract_control_trace (type bef_top bef aft_top aft)
    (cont : (bef_top, bef, aft_top, aft) Script_typed_ir.continuation) =
  match cont with
  | KNil -> Control.nil
  | KCons _ -> Control.cons
  | KReturn _ -> Control.return
  | KMap_head (_, _) -> Control.map_head
  | KUndip _ -> Control.undip
  | KLoop_in _ -> Control.loop_in
  | KLoop_in_left _ -> Control.loop_in_left
  | KIter (_, _, xs, _) -> Control.iter (Size.of_int (List.length xs))
  | KList_enter_body (_, xs, ys, _, _, _) ->
      Control.list_enter_body
        (Size.of_int (List.length xs))
        (Size.of_int (Script_list.length ys))
  | KList_exit_body (_, _, _, _, _, _) -> Control.list_exit_body
  | KMap_enter_body (_, xs, _, _, _) ->
      Control.map_enter_body (Size.of_int (List.length xs))
  | KMap_exit_body (_, _, map, k, _, _) ->
      let (module Map) = Script_map.get_module map in
      let key_size = Map.OPS.key_size k in
      Control.map_exit_body key_size (Size.map map)
  | KView_exit _ -> Control.view_exit
  | KLog _ -> Control.log

(** [Stop_bench] gets raised when a [IFailwith] would be the next instruction.
    This allows us to recover the full execution trace, including the trace of
    the [IFailwith].

    The actual benchmark will follow the same execution branch, but instead will
    raise an [error] which will be ignored. Thus it is safe to end a benchmark
    with [IFailwith], but timings are expected to be different from ending with
    [IHalt]. This means that, if we choose to include this behavior in any
    benchmark, [IFailwith] must be benched. *)
exception Stop_bench

let extract_deps (type bef_top bef aft_top aft) ctxt step_constants
    (sty : (bef_top, bef) Script_typed_ir.stack_ty)
    (kinstr : (bef_top, bef, aft_top, aft) Script_typed_ir.kinstr)
    (stack : bef_top * bef) =
  let trace = ref [] in
  (* Logger definition *)
  let logger =
    Script_interpreter_logging.make
      (module struct
        let log_interp _instr _ctxt _log _stack_ty _stack = ()

        let log_entry :
            type a s b f. (a, s, b, f, a, s) Script_typed_ir.logging_function =
         fun kinstr ctxt _loc _stack_ty stack ->
          trace := extract_ir_sized_step ctxt kinstr stack :: !trace ;
          match kinstr with IFailwith _ -> raise Stop_bench | _ -> ()

        let log_control kont = trace := extract_control_trace kont :: !trace

        let log_exit _instr _ctxt _log _stack_ty _stack = ()

        let get_log () = Environment.Error_monad.return_none
      end)
  in
  try
    let res =
      Lwt_main.run
        (Script_interpreter.Internals.kstep
           (Some logger)
           ctxt
           step_constants
           sty
           kinstr
           (fst stack)
           (snd stack))
    in
    match Environment.wrap_tzresult res with
    | Error errs ->
        Format.eprintf "%a@." Error_monad.pp_print_trace errs ;
        raise (Failure "Interpreter_workload.extract_deps: error in step")
    | Ok (_aft_top, _aft, _ctxt) ->
        (* ((aft_top, aft), List.rev !trace, ctxt) *)
        List.rev !trace
  with Stop_bench -> List.rev !trace

let extract_deps_continuation (type bef_top bef aft_top aft) ctxt step_constants
    (stack_type : (bef_top, bef) stack_ty)
    (cont : (bef_top, bef, aft_top, aft) Script_typed_ir.continuation)
    (stack : bef_top * bef) =
  let trace = ref [] in
  (* Logger definition *)
  let logger =
    Script_interpreter_logging.make
      (module struct
        let log_interp _instr _ctxt _log _stack_ty _stack = ()

        let log_entry :
            type a s b f. (a, s, b, f, a, s) Script_typed_ir.logging_function =
         fun kinstr ctxt _loc _stack_ty stack ->
          trace := extract_ir_sized_step ctxt kinstr stack :: !trace ;
          match kinstr with IFailwith _ -> raise Stop_bench | _ -> ()

        let log_control kont = trace := extract_control_trace kont :: !trace

        let log_exit _instr _ctxt _log _stack_ty _stack = ()

        let get_log () = Environment.Error_monad.return_none
      end)
  in
  try
    let res =
      let _gas_counter, outdated_ctxt =
        Local_gas_counter.local_gas_counter_and_outdated_context ctxt
      in
      Lwt_main.run
        (Script_interpreter.Internals.next
           (Some logger)
           (outdated_ctxt, step_constants)
           (Local_gas_counter 0xFF_FF_FF_FF)
           stack_type
           cont
           (fst stack)
           (snd stack))
    in
    match Environment.wrap_tzresult res with
    | Error errs ->
        Format.eprintf "%a@." Error_monad.pp_print_trace errs ;
        raise (Failure "Interpreter_workload.extract_deps: error in step")
    | Ok (_aft_top, _aft, _outdated_ctxt, _gas) ->
        (* ((aft_top, aft), List.rev !trace, outdated_ctxt, gas) *)
        List.rev !trace
  with Stop_bench -> List.rev !trace

let sized_step_to_sparse_vec {name; args} =
  let s = string_of_instr_or_cont name in
  match args with
  | [] -> Sparse_vec.String.of_list [(s, float_of_int 1)]
  | _ ->
      List.fold_left
        (fun acc {name; arg} ->
          Sparse_vec.String.(
            add acc (of_list [(s ^ "_" ^ name, float_of_int (Size.to_int arg))])))
        Sparse_vec.String.zero
        args

let trace_to_sparse_vec trace =
  List.fold_left
    (fun acc step -> Sparse_vec.String.add acc (sized_step_to_sparse_vec step))
    Sparse_vec.String.zero
    trace
back to top