LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_interp_core.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 565 646 87.5 %
Date: 2026-11-11 12:40:43 Functions: 0 0 -

          Line data    Source code
       1             :   /* This is the VM SBPF interpreter core.  The caller unpacks the VM
       2             :      state and then just lets execution continue into this (or jumps to
       3             :      interp_exec) to start running.  The VM will run until it halts or
       4             :      faults.  On normal termination, it will branch to interp_halt to
       5             :      exit.  Each fault has its own exit label to allow the caller to
       6             :      handle individually. */
       7             : 
       8             :   /* FIXME: SIGILLS FOR VARIOUS THINGS THAT HAVE UNNECESSARY BITS IN IMM
       9             :      SET? (LIKE WIDE SHIFTS?) */
      10             : 
      11           0 : # if defined(__GNUC__) /* -Wpedantic rejects labels as values and rejects goto *expr */
      12           0 : # pragma GCC diagnostic push
      13           0 : # pragma GCC diagnostic ignored "-Wpedantic"
      14           0 : # endif
      15             : 
      16           0 : # if defined(__clang__) /* Clang is differently picky about labels as values and goto *expr */
      17           0 : # pragma clang diagnostic push
      18           0 : # pragma clang diagnostic ignored "-Wpedantic"
      19           0 : # pragma clang diagnostic ignored "-Wgnu-label-as-value"
      20           0 : # endif
      21             : 
      22             :   /* Include the jump table */
      23             : 
      24        4231 : # include "fd_vm_interp_jump_table.c"
      25             : 
      26             :   /* Update the jump table based on SBPF version */
      27             : 
      28           0 :   ulong sbpf_version = vm->sbpf_version;
      29             : 
      30             :   /* Unpack the VM state */
      31             : 
      32           0 :   ulong pc        = vm->pc;
      33           0 :   ulong ic        = vm->ic;
      34           0 :   ulong cu        = vm->cu;
      35           0 :   ulong frame_cnt = vm->frame_cnt;
      36             : 
      37           0 :   void const * const * const version_interp_jump_table = interp_jump_table[ sbpf_version ];
      38             : 
      39             :   /* FD_VM_INTERP_INSTR_EXEC loads the first word of the instruction at
      40             :      pc, parses it, fetches the associated register values and then
      41             :      jumps to the code that executes the instruction.  On normal
      42             :      instruction execution, the pc will be updated and
      43             :      FD_VM_INTERP_INSTR_EXEC will be invoked again to do the next
      44             :      instruction.  After a normal halt, this will branch to interp_halt.
      45             :      Otherwise, it will branch to the appropriate normal termination. */
      46             : 
      47           0 :   ulong instr;
      48           0 :   ulong opcode;
      49           0 :   ulong dst;
      50           0 :   ulong src;
      51           0 :   ulong offset; /* offset is 16-bit but always sign extended, so we handle cast once */
      52           0 :   uint  imm;
      53           0 :   ulong reg_dst;
      54           0 :   ulong reg_src;
      55             : 
      56             : /* These mimic the exact Rust semantics for wrapping_shl and wrapping_shr. */
      57             : 
      58             : /* u64::wrapping_shl: a.unchecked_shl(b & (64 - 1))
      59             : 
      60             :    https://doc.rust-lang.org/std/primitive.u64.html#method.wrapping_shl
      61             :  */
      62      304685 : #define FD_RUST_ULONG_WRAPPING_SHL( a, b ) ((a) << ( (b) & ( 63 ) ))
      63             : 
      64             : /* u64::wrapping_shr: a.unchecked_shr(b & (64 - 1))
      65             : 
      66             :    https://doc.rust-lang.org/std/primitive.u64.html#method.wrapping_shr
      67             :  */
      68       69605 : #define FD_RUST_ULONG_WRAPPING_SHR( a, b ) ((a) >> ( (b) & ( 63 ) ))
      69             : 
      70             : /* u32::wrapping_shl: a.unchecked_shl(b & (32 - 1))
      71             : 
      72             :    https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_shl
      73             :  */
      74      126310 : #define FD_RUST_UINT_WRAPPING_SHL( a, b ) ((a) << ( (b) & ( 31 ) ))
      75             : 
      76             : /* u32::wrapping_shr: a.unchecked_shr(b & (32 - 1))
      77             : 
      78             :    https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_shr
      79             :  */
      80         688 : #define FD_RUST_UINT_WRAPPING_SHR( a, b ) ((a) >> ( (b) & ( 31 ) ))
      81             : 
      82             : /* i32::wrapping_shr: a.unchecked_shr(b & (32 - 1))
      83             : 
      84             :    https://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_shr
      85             :  */
      86       23224 : #define FD_RUST_INT_WRAPPING_SHR( a, b ) ((a) >> ( (b) & ( 31 ) ))
      87             : 
      88             : /* i64::wrapping_shr: a.unchecked_shr(b & (64 - 1))
      89             : 
      90             :    https://doc.rust-lang.org/std/primitive.i64.html#method.wrapping_shr
      91             :  */
      92       10707 : #define FD_RUST_LONG_WRAPPING_SHR( a, b ) ((a) >> ( (b) & ( 63 ) ))
      93             : 
      94             : 
      95           0 : # define FD_VM_INTERP_INSTR_EXEC                                                                                    \
      96    13460190 :   if( FD_UNLIKELY( pc>=block_text_limit ) ) goto sigtext_or_sigcost; /* Note: untaken branches don't consume BTB */ \
      97    13460190 :   instr   = text[ pc ];                  /* Guaranteed in-bounds */                                                 \
      98    13459127 :   opcode  = fd_vm_instr_opcode( instr ); /* in [0,256) even if malformed */                                         \
      99    13459127 :   dst     = fd_vm_instr_dst   ( instr ); /* in [0, 16) even if malformed */                                         \
     100    13459127 :   src     = fd_vm_instr_src   ( instr ); /* in [0, 16) even if malformed */                                         \
     101    13459127 :   offset  = fd_vm_instr_offset( instr ); /* in [-2^15,2^15) even if malformed */                                    \
     102    13459127 :   imm     = fd_vm_instr_imm   ( instr ); /* in [0,2^32) even if malformed */                                        \
     103    13459127 :   reg_dst = reg[ dst ];                  /* Guaranteed in-bounds */                                                 \
     104    13459127 :   reg_src = reg[ src ];                  /* Guaranteed in-bounds */                                                 \
     105    13459127 :   goto *version_interp_jump_table[ opcode ]      /* Guaranteed in-bounds */
     106             : 
     107             : /* FD_VM_INTERP_SYSCALL_EXEC
     108             :    (macro to handle the logic of 0x85 pre- and post- SIMD-0178: static syscalls)
     109             : 
     110             :    Setup.
     111             :    Update the vm with the current vm execution state for the
     112             :    syscall.  Note that BRANCH_BEGIN has pc at the syscall and
     113             :    already updated ic and cu to reflect all instructions up to
     114             :    and including the syscall instruction itself.
     115             : 
     116             :    Execution.
     117             :    Do the syscall. The return value is placed into vm->reg[0].
     118             : 
     119             :    Error handling.
     120             :    If we trust syscall implementations to handle the vm state
     121             :    correctly, the below could be implemented as unpacking the vm
     122             :    state and jumping to sigsys on error.  But we provide some
     123             :    extra protection to make various strong guarantees:
     124             : 
     125             :    - We do not let the syscall modify pc currently as nothing
     126             :      requires this and it reduces risk of a syscall bug mucking
     127             :      up the interpreter.  If there ever was a syscall that
     128             :      needed to modify the pc (e.g. a syscall that has execution
     129             :      resume from a different location than the instruction
     130             :      following the syscall), do "pc = vm->pc" below.
     131             : 
     132             :    - We do not let the syscall modify ic currently as nothing
     133             :      requires this and it keeps the ic precise.  If a future
     134             :      syscall needs this, do "ic = vm->ic" below.
     135             : 
     136             :    - We do not let the syscall increase cu as nothing requires
     137             :      this and it guarantees the interpreter will halt in a
     138             :      reasonable finite amount of time.  If a future syscall
     139             :      needs this, do "cu = vm->cu" below.
     140             : 
     141             :    - A syscall that returns SIGCOST is always treated as though
     142             :      it also zerod cu.
     143             : 
     144             :    At this point, vm->cu is whatever the syscall tried to set.
     145             : 
     146             :    Exit
     147             :    At this point, err is clear.
     148             : */
     149             : 
     150           0 : # define FD_VM_INTERP_SYSCALL_EXEC_DUMP                                       \
     151             :   /* Dumping for debugging purposes */                                        \
     152       10055 :   if( FD_UNLIKELY( vm->dump_syscall_to_pb ) ) {                               \
     153           0 :     fd_dump_vm_syscall_to_protobuf( vm, syscall->name );                      \
     154           0 :   }
     155             : 
     156           0 : # define FD_VM_INTERP_SYSCALL_EXEC                                            \
     157             :   /* Setup */                                                                 \
     158       10055 :   vm->pc        = pc;                                                         \
     159       10055 :   vm->ic        = ic;                                                         \
     160       10055 :   vm->cu        = cu;                                                         \
     161       10055 :   vm->frame_cnt = frame_cnt;                                                  \
     162       10055 :   FD_VM_INTERP_SYSCALL_EXEC_DUMP                                              \
     163             :   /* Execution */                                                             \
     164       10055 :   err = syscall->func( vm, reg[1], reg[2], reg[3], reg[4], reg[5] );          \
     165             :   /* Error handling */                                                        \
     166       10055 :   ulong cu_req = vm->cu;                                                      \
     167       10055 :   cu = fd_ulong_min( cu_req, cu );                                            \
     168       10055 :   if( FD_UNLIKELY( err ) ) {                                                  \
     169         951 :     if( err==FD_VM_SYSCALL_ERR_COMPUTE_BUDGET_EXCEEDED ) cu = 0UL; /* cmov */ \
     170         951 :     FD_VM_TEST_ERR_EXISTS( vm );                                              \
     171         951 :     goto sigsyscall;                                                          \
     172         951 :   }                                                                           \
     173             :   /* Exit */
     174             : 
     175             : 
     176             :   /* FD_VM_INTERP_INSTR_BEGIN / FD_VM_INTERP_INSTR_END bracket opcode's
     177             :      implementation for an opcode that does not branch.  On entry, the
     178             :      instruction word has been unpacked into dst / src / offset / imm
     179             :      and reg[dst] / reg[src] has been prefetched into reg_dst / reg_src. */
     180             : 
     181     8233829 : # define FD_VM_INTERP_INSTR_BEGIN(opcode) interp_##opcode:
     182             : 
     183             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~0.3% faster in some benchmarks, slower in others but more code footprint */
     184     8232397 : # define FD_VM_INTERP_INSTR_END pc++; FD_VM_INTERP_INSTR_EXEC
     185             : # else /* Use this version when tracing or optimizing code footprint */
     186           0 : # define FD_VM_INTERP_INSTR_END pc++; goto interp_exec
     187             : # endif
     188             : 
     189             :   /* Instead of doing a lot of compute budget calcs and tests every
     190             :      instruction, we note that the program counter increases
     191             :      monotonically after a branch (or a program start) until the next
     192             :      branch (or program termination).  We save the program counter of
     193             :      the start of such a segment in pc0.  Whenever we encounter a branch
     194             :      (or a program termination) at pc, we know we processed pc-pc0+1
     195             :      text words (including the text word for the branch instruction
     196             :      itself as all branch instructions are single word).
     197             : 
     198             :      Each instruction costs 1 cu (syscalls can cost extra on top of
     199             :      this that is accounted separately in CALL_IMM below).  Since there
     200             :      could have been multiword instructions in this segment, at start of
     201             :      such a segment, we zero out the accumulator ic_correction and have
     202             :      every multiword instruction in the segment accumulate the number of
     203             :      extra text words it has to this variable.  (Sigh ... it would be a
     204             :      lot simpler to bill based on text words processed but this would be
     205             :      very difficult to make this protocol change at this point.)
     206             : 
     207             :      When we encounter a branch at pc, the number of instructions
     208             :      processed (and thus the number of compute units to bill for that
     209             :      segment) is thus:
     210             : 
     211             :        pc - pc0 + 1 - ic_correction */
     212             : 
     213           0 :   ulong pc0           = pc;
     214           0 :   ulong ic_correction = 0UL;
     215             : 
     216             :   /* Compute the text limit for a basic block. This bounds the number
     217             :      of instructions that the basic block can execute linearly.
     218             : 
     219             :      There are two factors: the remaining CU budget as of the start of
     220             :      the basic block, and the text_cnt. The tightest bound is the
     221             :      lesser of the two.
     222             : 
     223             :      We use 2*(the remaining CU budget) because each LDDW instruction
     224             :      costs 1 CU, but occupies two text words, so in the worst case (a
     225             :      block full of LDDW instructions) the block text limit should be
     226             :      doubled.
     227             : 
     228             :      It is safe to not use saturating addition here.  Both addends are
     229             :      bounded far below ULONG_MAX before any block executes:
     230             : 
     231             :        pc0 <= text_cnt <= 1310720 (see fd_sbpf_loader.h)
     232             :        cu  <= 1400000             (FD_MAX_COMPUTE_UNIT_LIMIT)
     233             : 
     234             :        pc0 + cu + cu + 1 < 1310720 + 1400000 + 1400000 + 1 < ULONG_MAX */
     235             : 
     236     5226738 : # define FD_VM_INTERP_BLOCK_TEXT_LIMIT fd_ulong_min( text_cnt, pc0+cu+cu+1UL )
     237             : 
     238        4231 :   ulong block_text_limit = FD_VM_INTERP_BLOCK_TEXT_LIMIT;
     239             : 
     240           0 : # define FD_VM_INTERP_BRANCH_BEGIN(opcode)                                                              \
     241     5225278 :   interp_##opcode:                                                                                      \
     242             :     /* Bill linear text segment and this branch instruction as per the above */                         \
     243     5225278 :     ic_correction = pc - pc0 + 1UL - ic_correction;                                                     \
     244     5225278 :     ic += ic_correction;                                                                                \
     245     5225278 :     if( FD_UNLIKELY( ic_correction>cu ) ) goto sigcost; /* Note: untaken branches don't consume BTB */  \
     246     5225278 :     cu -= ic_correction;                                                                                \
     247             :     /* At this point, cu>=0 */                                                                          \
     248     5225253 :     ic_correction = 0UL;
     249             : 
     250             :   /* FIXME: debatable if it is better to do pc++ here or have the
     251             :      instruction implementations do it in their code path. */
     252             : 
     253             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~4% faster in some benchmarks, slower in others but more code footprint */
     254             : # define FD_VM_INTERP_BRANCH_END                      \
     255     5222507 :     pc++;                                             \
     256     5222507 :     pc0 = pc; /* Start a new linear segment */        \
     257     5222507 :     block_text_limit = FD_VM_INTERP_BLOCK_TEXT_LIMIT; \
     258     5223562 :     FD_VM_INTERP_INSTR_EXEC
     259             : # else /* Use this version when tracing or optimizing code footprint */
     260             : # define FD_VM_INTERP_BRANCH_END                      \
     261           0 :     pc++;                                             \
     262           0 :     pc0 = pc; /* Start a new linear segment */        \
     263           0 :     block_text_limit = FD_VM_INTERP_BLOCK_TEXT_LIMIT; \
     264             :     /* FIXME: TEST sigsplit HERE */                   \
     265           0 :     goto interp_exec
     266             : # endif
     267             : 
     268             :   /* FD_VM_INTERP_STACK_PUSH pushes reg[6:9] onto the shadow stack and
     269             :      advances reg[10] to a new user stack frame.  If there are no more
     270             :      stack frames available, will do a SIGSTACK. */
     271             : 
     272             :   /* FIXME: double check faulting is desired on stack overflow. */
     273             : 
     274             :   /* FIXME: a pre-belt-sanding FIXME implied the TLB should be updated
     275             :      to prevent byte code from accessing the stack outside its current
     276             :      stack frame.  But this would break the common practice of a
     277             :      function passing a pointer to something on its stack into a
     278             :      function that it calls:
     279             : 
     280             :        void foo( ... ) {
     281             :          ...
     282             :          int ret;
     283             :          bar( &ret );
     284             :          ...
     285             :        }
     286             : 
     287             :      So this probably shouldn't be done.  But, if it is in fact
     288             :      necessary, the TLB updates would be here and in pop. */
     289             : 
     290             :   /* FIXME: unvalidated code mucking with r10 */
     291             : 
     292           0 : # define FD_VM_INTERP_STACK_PUSH                                                                            \
     293       47434 :   shadow[ frame_cnt ].r6  = reg[6];                                                                         \
     294       47434 :   shadow[ frame_cnt ].r7  = reg[7];                                                                         \
     295       47434 :   shadow[ frame_cnt ].r8  = reg[8];                                                                         \
     296       47434 :   shadow[ frame_cnt ].r9  = reg[9];                                                                         \
     297       47434 :   shadow[ frame_cnt ].r10 = reg[10];                                                                        \
     298       47434 :   shadow[ frame_cnt ].pc  = pc;                                                                             \
     299       47434 :   if( FD_UNLIKELY( ++frame_cnt>=frame_max ) ) goto sigstack; /* Note: untaken branches don't consume BTB */ \
     300       47434 :   reg[10]                += vm->stack_frame_sz * vm->stack_push_frame_count;                                \
     301             :   /* We subtract the heap cost in the BPF loader */
     302             : 
     303           0 :   goto interp_exec; /* Silly but to avoid unused label warning in some configurations */
     304        4231 : interp_exec:
     305             : 
     306             : # ifdef FD_VM_INTERP_EXE_TRACING_ENABLED
     307             :   /* Note: when tracing or optimizing for code footprint, all
     308             :      instruction execution starts here such that this is only point
     309             :      where exe tracing diagnostics are needed. */
     310           0 :   if( FD_UNLIKELY( pc>=block_text_limit ) ) goto sigtext_or_sigcost;
     311           0 :   fd_vm_trace_event_exe( vm->trace, pc, ic + ( pc - pc0 - ic_correction ), cu, reg, vm->text + pc, vm->text_cnt - pc, ic_correction, frame_cnt );
     312           0 : # endif
     313             : 
     314        4231 :   FD_VM_INTERP_INSTR_EXEC;
     315             : 
     316             :   /* 0x00 - 0x0f ******************************************************/
     317             : 
     318             :   FD_VM_INTERP_INSTR_BEGIN(0x04) /* FD_SBPF_OP_ADD_IMM */
     319          65 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst + (int)imm );
     320          65 :   FD_VM_INTERP_INSTR_END;
     321             : 
     322             :   FD_VM_INTERP_INSTR_BEGIN(0x04depr) /* FD_SBPF_OP_ADD_IMM deprecated SIMD-0174 */
     323      482939 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)imm );
     324      482939 :   FD_VM_INTERP_INSTR_END;
     325             : 
     326             :   FD_VM_INTERP_BRANCH_BEGIN(0x05) /* FD_SBPF_OP_JA */
     327      876281 :     pc += offset;
     328      876281 :   FD_VM_INTERP_BRANCH_END;
     329             : 
     330             :   FD_VM_INTERP_INSTR_BEGIN(0x07) /* FD_SBPF_OP_ADD64_IMM */
     331      491945 :     reg[ dst ] = reg_dst + (ulong)(long)(int)imm;
     332      491945 :   FD_VM_INTERP_INSTR_END;
     333             : 
     334             :   FD_VM_INTERP_INSTR_BEGIN(0x0c) /* FD_SBPF_OP_ADD_REG */
     335          56 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst + (int)reg_src );
     336          56 :   FD_VM_INTERP_INSTR_END;
     337             : 
     338             :   FD_VM_INTERP_INSTR_BEGIN(0x0cdepr) /* FD_SBPF_OP_ADD_REG deprecated SIMD-0174 */
     339       19881 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)reg_src );
     340       19881 :   FD_VM_INTERP_INSTR_END;
     341             : 
     342             :   FD_VM_INTERP_INSTR_BEGIN(0x0f) /* FD_SBPF_OP_ADD64_REG */
     343      238213 :     reg[ dst ] = reg_dst + reg_src;
     344      238213 :   FD_VM_INTERP_INSTR_END;
     345             : 
     346             :   /* 0x10 - 0x1f ******************************************************/
     347             : 
     348             :   FD_VM_INTERP_INSTR_BEGIN(0x14) /* FD_SBPF_OP_SUB_IMM */
     349          95 :     reg[ dst ] = (ulong)(uint)( (int)imm - (int)reg_dst );
     350          95 :   FD_VM_INTERP_INSTR_END;
     351             : 
     352             :   FD_VM_INTERP_INSTR_BEGIN(0x14depr) /* FD_SBPF_OP_SUB_IMM deprecated SIMD-0174 */
     353       20166 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)imm );
     354       20166 :   FD_VM_INTERP_INSTR_END;
     355             : 
     356             :   FD_VM_INTERP_BRANCH_BEGIN(0x15) /* FD_SBPF_OP_JEQ_IMM */
     357      108202 :     pc += fd_ulong_if( reg_dst==(ulong)(long)(int)imm, offset, 0UL );
     358      108202 :   FD_VM_INTERP_BRANCH_END;
     359             : 
     360             :   FD_VM_INTERP_INSTR_BEGIN(0x17) /* FD_SBPF_OP_SUB64_IMM */
     361          89 :     reg[ dst ] = (ulong)(long)(int)imm - reg_dst;
     362          89 :   FD_VM_INTERP_INSTR_END;
     363             : 
     364             :   FD_VM_INTERP_INSTR_BEGIN(0x17depr) /* FD_SBPF_OP_SUB64_IMM deprecated SIMD-0174 */
     365         306 :     reg[ dst ] = reg_dst - (ulong)(long)(int)imm;
     366         306 :   FD_VM_INTERP_INSTR_END;
     367             : 
     368             :   FD_VM_INTERP_INSTR_BEGIN(0x18) /* FD_SBPF_OP_LDQ */
     369      724572 :     pc++;
     370      724572 :     ic_correction++;
     371             :     /* No need to check pc because it's already checked during validation.
     372             :        if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigsplit; // Note: untaken branches don't consume BTB */
     373      724572 :     reg[ dst ] = (ulong)((ulong)imm | ((ulong)fd_vm_instr_imm( text[ pc ] ) << 32));
     374      724572 :   FD_VM_INTERP_INSTR_END;
     375             : 
     376             :   FD_VM_INTERP_INSTR_BEGIN(0x1c) /* FD_SBPF_OP_SUB_REG */
     377          77 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst - (int)reg_src );
     378          77 :   FD_VM_INTERP_INSTR_END;
     379             : 
     380             :   FD_VM_INTERP_INSTR_BEGIN(0x1cdepr) /* FD_SBPF_OP_SUB_REG deprecated SIMD-0174 */
     381      215203 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)reg_src );
     382      215203 :   FD_VM_INTERP_INSTR_END;
     383             : 
     384             :   FD_VM_INTERP_BRANCH_BEGIN(0x1d) /* FD_SBPF_OP_JEQ_REG */
     385       15207 :     pc += fd_ulong_if( reg_dst==reg_src, offset, 0UL );
     386       15207 :   FD_VM_INTERP_BRANCH_END;
     387             : 
     388             :   FD_VM_INTERP_INSTR_BEGIN(0x1f) /* FD_SBPF_OP_SUB64_REG */
     389       22428 :     reg[ dst ] = reg_dst - reg_src;
     390       22428 :   FD_VM_INTERP_INSTR_END;
     391             : 
     392             :   /* 0x20 - 0x2f ******************************************************/
     393             : 
     394             :   FD_VM_INTERP_INSTR_BEGIN(0x24) /* FD_SBPF_OP_MUL_IMM */
     395      216072 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)imm );
     396      216072 :   FD_VM_INTERP_INSTR_END;
     397             : 
     398             :   FD_VM_INTERP_BRANCH_BEGIN(0x25) /* FD_SBPF_OP_JGT_IMM */
     399       30039 :     pc += fd_ulong_if( reg_dst>(ulong)(long)(int)imm, offset, 0UL );
     400       30039 :   FD_VM_INTERP_BRANCH_END;
     401             : 
     402       31695 :   FD_VM_INTERP_INSTR_BEGIN(0x27) { /* FD_SBPF_OP_STB */
     403       31695 :     ulong vaddr = reg_dst + offset;
     404       31695 :     ulong haddr = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL );
     405       31695 :     if( FD_UNLIKELY( !haddr ) ) {
     406           6 :       vm->segv_vaddr       = vaddr;
     407           6 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     408           6 :       vm->segv_access_len  = 1UL;
     409           6 :       goto sigsegv;
     410           6 :     } /* Note: untaken branches don't consume BTB */
     411       31689 :     fd_vm_mem_st_1( haddr, (uchar)imm );
     412       31689 :   }
     413       31689 :   FD_VM_INTERP_INSTR_END;
     414             : 
     415       96675 :   FD_VM_INTERP_INSTR_BEGIN(0x2c) { /* FD_SBPF_OP_LDXB */
     416       96675 :     ulong vaddr = reg_src + offset;
     417       96675 :     ulong haddr = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_ld_sz, 0, 0UL );
     418       96675 :     if( FD_UNLIKELY( !haddr ) ) {
     419           9 :       vm->segv_vaddr       = vaddr;
     420           9 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     421           9 :       vm->segv_access_len  = 1UL;
     422           9 :       goto sigsegv;
     423           9 :     } /* Note: untaken branches don't consume BTB */
     424       96666 :     reg[ dst ] = fd_vm_mem_ld_1( haddr );
     425       96666 :   }
     426       96666 :   FD_VM_INTERP_INSTR_END;
     427             : 
     428             :   FD_VM_INTERP_BRANCH_BEGIN(0x2d) /* FD_SBPF_OP_JGT_REG */
     429      126529 :     pc += fd_ulong_if( reg_dst>reg_src, offset, 0UL );
     430      126529 :   FD_VM_INTERP_BRANCH_END;
     431             : 
     432       89439 :   FD_VM_INTERP_INSTR_BEGIN(0x2f) { /* FD_SBPF_OP_STXB */
     433       89439 :     ulong vaddr = reg_dst + offset;
     434       89439 :     ulong haddr = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL );
     435       89439 :     if( FD_UNLIKELY( !haddr ) ) {
     436          16 :       vm->segv_vaddr       = vaddr;
     437          16 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     438          16 :       vm->segv_access_len  = 1UL;
     439          16 :       goto sigsegv;
     440          16 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigrdonly */
     441       89423 :     fd_vm_mem_st_1( haddr, (uchar)reg_src );
     442       89423 :   }
     443       89423 :   FD_VM_INTERP_INSTR_END;
     444             : 
     445             :   FD_VM_INTERP_INSTR_BEGIN(0x27depr) /* FD_SBPF_OP_MUL64_IMM */
     446       15739 :     reg[ dst ] = (ulong)( (long)reg_dst * (long)(int)imm );
     447       15739 :   FD_VM_INTERP_INSTR_END;
     448             : 
     449             :   FD_VM_INTERP_INSTR_BEGIN(0x2cdepr) /* FD_SBPF_OP_MUL_REG */
     450       58223 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)reg_src );
     451       58223 :   FD_VM_INTERP_INSTR_END;
     452             : 
     453             :   FD_VM_INTERP_INSTR_BEGIN(0x2fdepr) /* FD_SBPF_OP_MUL64_REG */
     454       74805 :     reg[ dst ] = reg_dst * reg_src;
     455       74805 :   FD_VM_INTERP_INSTR_END;
     456             : 
     457             :   /* 0x30 - 0x3f ******************************************************/
     458             : 
     459             :   FD_VM_INTERP_INSTR_BEGIN(0x34) /* FD_SBPF_OP_DIV_IMM */
     460       49994 :     /* FIXME: convert to a multiply at validation time (usually probably
     461       49994 :        not worth it) */
     462       49994 :     reg[ dst ] = (ulong)((uint)reg_dst / imm);
     463       49994 :   FD_VM_INTERP_INSTR_END;
     464             : 
     465             :   FD_VM_INTERP_BRANCH_BEGIN(0x35) /* FD_SBPF_OP_JGE_IMM */
     466         613 :     pc += fd_ulong_if( reg_dst>=(ulong)(long)(int)imm, offset, 0UL );
     467         613 :   FD_VM_INTERP_BRANCH_END;
     468             : 
     469             :   FD_VM_INTERP_INSTR_BEGIN(0x36) /* FD_SBPF_OP_UHMUL64_IMM */
     470          54 :     reg[ dst ] = (ulong)(( (uint128)reg_dst * (uint128)(ulong)imm ) >> 64 );
     471          54 :   FD_VM_INTERP_INSTR_END;
     472             : 
     473       36573 :   FD_VM_INTERP_INSTR_BEGIN(0x37) { /* FD_SBPF_OP_STH */
     474       36573 :     ulong vaddr   = reg_dst + offset;
     475       36573 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL );
     476       36573 :     int   sigsegv = !haddr;
     477       36573 :     if( FD_UNLIKELY( sigsegv ) ) {
     478           4 :       vm->segv_vaddr       = vaddr;
     479           4 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     480           4 :       vm->segv_access_len  = 2UL;
     481           4 :       goto sigsegv;
     482           4 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     483       36569 :     fd_vm_mem_st_2( haddr, (ushort)imm );
     484       36569 :   }
     485       36569 :   FD_VM_INTERP_INSTR_END;
     486             : 
     487        2941 :   FD_VM_INTERP_INSTR_BEGIN(0x3c) { /* FD_SBPF_OP_LDXH */
     488        2941 :     ulong vaddr   = reg_src + offset;
     489        2941 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_ld_sz, 0, 0UL );
     490        2941 :     int   sigsegv = !haddr;
     491        2941 :     if( FD_UNLIKELY( sigsegv ) ) {
     492          10 :       vm->segv_vaddr       = vaddr;
     493          10 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     494          10 :       vm->segv_access_len  = 2UL;
     495          10 :       goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     496          10 :     }
     497        2931 :     reg[ dst ] = fd_vm_mem_ld_2( haddr );
     498        2931 :   }
     499        2931 :   FD_VM_INTERP_INSTR_END;
     500             : 
     501             :   FD_VM_INTERP_BRANCH_BEGIN(0x3d) /* FD_SBPF_OP_JGE_REG */
     502      614403 :     pc += fd_ulong_if( reg_dst>=reg_src, offset, 0UL );
     503      614403 :   FD_VM_INTERP_BRANCH_END;
     504             : 
     505        1935 :   FD_VM_INTERP_INSTR_BEGIN(0x3f) { /* FD_SBPF_OP_STXH */
     506        1935 :     ulong vaddr   = reg_dst + offset;
     507        1935 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL );
     508        1935 :     int   sigsegv = !haddr;
     509        1935 :     if( FD_UNLIKELY( sigsegv ) ) {
     510           5 :       vm->segv_vaddr       = vaddr;
     511           5 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     512           5 :       vm->segv_access_len  = 2UL;
     513           5 :       goto sigsegv;
     514           5 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     515        1930 :     fd_vm_mem_st_2( haddr, (ushort)reg_src );
     516        1930 :   }
     517        1930 :   FD_VM_INTERP_INSTR_END;
     518             : 
     519             :   FD_VM_INTERP_INSTR_BEGIN(0x3e) /* FD_SBPF_OP_UHMUL64_REG */
     520          87 :     reg[ dst ] = (ulong)(( (uint128)reg_dst * (uint128)reg_src ) >> 64 );
     521          87 :   FD_VM_INTERP_INSTR_END;
     522             : 
     523             :   FD_VM_INTERP_INSTR_BEGIN(0x37depr) /* FD_SBPF_OP_DIV64_IMM */
     524       55047 :     reg[ dst ] = reg_dst / (ulong)(long)(int)imm;
     525       55047 :   FD_VM_INTERP_INSTR_END;
     526             : 
     527             :   FD_VM_INTERP_INSTR_BEGIN(0x3cdepr) /* FD_SBPF_OP_DIV_REG */
     528      549341 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     529      549120 :     reg[ dst ] = (ulong)((uint)reg_dst / (uint)reg_src);
     530      549120 :   FD_VM_INTERP_INSTR_END;
     531             : 
     532             :   FD_VM_INTERP_INSTR_BEGIN(0x3fdepr) /* FD_SBPF_OP_DIV64_REG */
     533      156042 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     534      155831 :     reg[ dst ] = reg_dst / reg_src;
     535      155831 :   FD_VM_INTERP_INSTR_END;
     536             : 
     537             :   /* 0x40 - 0x4f ******************************************************/
     538             : 
     539             :   FD_VM_INTERP_INSTR_BEGIN(0x44) /* FD_SBPF_OP_OR_IMM */
     540       45035 :     reg[ dst ] = (ulong)( (uint)reg_dst | imm );
     541       45035 :   FD_VM_INTERP_INSTR_END;
     542             : 
     543             :   FD_VM_INTERP_BRANCH_BEGIN(0x45) /* FD_SBPF_OP_JSET_IMM */
     544      313340 :     pc += fd_ulong_if( !!(reg_dst & (ulong)(long)(int)imm), offset, 0UL );
     545      313340 :   FD_VM_INTERP_BRANCH_END;
     546             : 
     547             :   FD_VM_INTERP_INSTR_BEGIN(0x46) /* FD_SBPF_OP_UDIV32_IMM */
     548          64 :     reg[ dst ] = (ulong)( (uint)reg_dst / (uint)imm );
     549          64 :   FD_VM_INTERP_INSTR_END;
     550             : 
     551             :   FD_VM_INTERP_INSTR_BEGIN(0x47) /* FD_SBPF_OP_OR64_IMM */
     552      111361 :     reg[ dst ] = reg_dst | (ulong)(long)(int)imm;
     553      111361 :   FD_VM_INTERP_INSTR_END;
     554             : 
     555             :   FD_VM_INTERP_INSTR_BEGIN(0x4c) /* FD_SBPF_OP_OR_REG */
     556       36616 :     reg[ dst ] = (ulong)(uint)( reg_dst | reg_src );
     557       36616 :   FD_VM_INTERP_INSTR_END;
     558             : 
     559             :   FD_VM_INTERP_BRANCH_BEGIN(0x4d) /* FD_SBPF_OP_JSET_REG */
     560           7 :     pc += fd_ulong_if( !!(reg_dst & reg_src), offset, 0UL );
     561           7 :   FD_VM_INTERP_BRANCH_END;
     562             : 
     563             :   FD_VM_INTERP_INSTR_BEGIN(0x4e) /* FD_SBPF_OP_UDIV32_REG */
     564          52 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     565          10 :     reg[ dst ] = (ulong)( (uint)reg_dst / (uint)reg_src );
     566          10 :   FD_VM_INTERP_INSTR_END;
     567             : 
     568             :   FD_VM_INTERP_INSTR_BEGIN(0x4f) /* FD_SBPF_OP_OR64_REG */
     569       74994 :     reg[ dst ] = reg_dst | reg_src;
     570       74994 :   FD_VM_INTERP_INSTR_END;
     571             : 
     572             :   /* 0x50 - 0x5f ******************************************************/
     573             : 
     574             :   FD_VM_INTERP_INSTR_BEGIN(0x54) /* FD_SBPF_OP_AND_IMM */
     575       63552 :     reg[ dst ] = (ulong)( (uint)reg_dst & imm );
     576       63552 :   FD_VM_INTERP_INSTR_END;
     577             : 
     578             :   FD_VM_INTERP_BRANCH_BEGIN(0x55) /* FD_SBPF_OP_JNE_IMM */
     579      301659 :     pc += fd_ulong_if( reg_dst!=(ulong)(long)(int)imm, offset, 0UL );
     580      301659 :   FD_VM_INTERP_BRANCH_END;
     581             : 
     582             :   FD_VM_INTERP_INSTR_BEGIN(0x56) /* FD_SBPF_OP_UDIV64_IMM */
     583          60 :     reg[ dst ] = reg_dst / (ulong)imm;
     584          60 :   FD_VM_INTERP_INSTR_END;
     585             : 
     586             :   FD_VM_INTERP_INSTR_BEGIN(0x57) /* FD_SBPF_OP_AND64_IMM */
     587      156670 :     reg[ dst ] = reg_dst & (ulong)(long)(int)imm;
     588      156670 :   FD_VM_INTERP_INSTR_END;
     589             : 
     590             :   FD_VM_INTERP_INSTR_BEGIN(0x5c) /* FD_SBPF_OP_AND_REG */
     591       39150 :     reg[ dst ] = (ulong)(uint)( reg_dst & reg_src );
     592       39150 :   FD_VM_INTERP_INSTR_END;
     593             : 
     594             :   FD_VM_INTERP_BRANCH_BEGIN(0x5d) /* FD_SBPF_OP_JNE_REG */
     595     1130773 :     pc += fd_ulong_if( reg_dst!=reg_src, offset, 0UL );
     596     1130773 :   FD_VM_INTERP_BRANCH_END;
     597             : 
     598             :   FD_VM_INTERP_INSTR_BEGIN(0x5e) /* FD_SBPF_OP_UDIV64_REG */
     599          70 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     600          25 :     reg[ dst ] = reg_dst / reg_src;
     601          25 :   FD_VM_INTERP_INSTR_END;
     602             : 
     603             :   FD_VM_INTERP_INSTR_BEGIN(0x5f) /* FD_SBPF_OP_AND64_REG */
     604       93504 :     reg[ dst ] = reg_dst & reg_src;
     605       93504 :   FD_VM_INTERP_INSTR_END;
     606             : 
     607             :   /* 0x60 - 0x6f ******************************************************/
     608             : 
     609             :   FD_VM_INTERP_INSTR_BEGIN(0x64) /* FD_SBPF_OP_LSH_IMM */
     610         325 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L291 */
     611         325 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, (uint)imm ) );
     612         325 :   FD_VM_INTERP_INSTR_END;
     613             : 
     614             :   FD_VM_INTERP_BRANCH_BEGIN(0x65) /* FD_SBPF_OP_JSGT_IMM */
     615       28115 :     pc += fd_ulong_if( (long)reg_dst>(long)(int)imm, offset, 0UL );
     616       28115 :   FD_VM_INTERP_BRANCH_END;
     617             : 
     618             :   FD_VM_INTERP_INSTR_BEGIN(0x66) /* FD_SBPF_OP_UREM32_IMM */
     619          57 :     reg[ dst ] = (ulong)( (uint)reg_dst % (uint)imm );
     620          57 :   FD_VM_INTERP_INSTR_END;
     621             : 
     622             :   FD_VM_INTERP_INSTR_BEGIN(0x67) /* FD_SBPF_OP_LSH64_IMM */
     623      277437 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L376 */
     624      277437 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, imm );
     625      277437 :   FD_VM_INTERP_INSTR_END;
     626             : 
     627             :   FD_VM_INTERP_INSTR_BEGIN(0x6c) /* FD_SBPF_OP_LSH_REG */
     628      125985 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L292 */
     629      125985 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, reg_src ) );
     630      125985 :   FD_VM_INTERP_INSTR_END;
     631             : 
     632             :   FD_VM_INTERP_BRANCH_BEGIN(0x6d) /* FD_SBPF_OP_JSGT_REG */
     633       12984 :     pc += fd_ulong_if( (long)reg_dst>(long)reg_src, offset, 0UL );
     634       12984 :   FD_VM_INTERP_BRANCH_END;
     635             : 
     636             :   FD_VM_INTERP_INSTR_BEGIN(0x6e) /* FD_SBPF_OP_UREM32_REG */
     637          65 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     638          10 :     reg[ dst ] = (ulong)( (uint)reg_dst % (uint)reg_src );
     639          10 :   FD_VM_INTERP_INSTR_END;
     640             : 
     641             :   FD_VM_INTERP_INSTR_BEGIN(0x6f) /* FD_SBPF_OP_LSH64_REG */
     642       27248 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L377 */
     643       27248 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, reg_src );
     644       27248 :   FD_VM_INTERP_INSTR_END;
     645             : 
     646             :   /* 0x70 - 0x7f ******************************************************/
     647             : 
     648             :   FD_VM_INTERP_INSTR_BEGIN(0x74) /* FD_SBPF_OP_RSH_IMM */
     649         327 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L293 */
     650         327 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, imm ) );
     651         327 :   FD_VM_INTERP_INSTR_END;
     652             : 
     653             :   FD_VM_INTERP_BRANCH_BEGIN(0x75) /* FD_SBPF_OP_JSGE_IMM */
     654         211 :     pc += fd_ulong_if( (long)reg_dst>=(long)(int)imm, offset, 0UL );
     655         211 :   FD_VM_INTERP_BRANCH_END;
     656             : 
     657             :   FD_VM_INTERP_INSTR_BEGIN(0x76) /* FD_SBPF_OP_UREM64_IMM */
     658          61 :     reg[ dst ] = reg_dst % (ulong)imm;
     659          61 :   FD_VM_INTERP_INSTR_END;
     660             : 
     661             :   FD_VM_INTERP_INSTR_BEGIN(0x77) /* FD_SBPF_OP_RSH64_IMM */
     662       62233 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L378 */
     663       62233 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, imm );
     664       62233 :   FD_VM_INTERP_INSTR_END;
     665             : 
     666             :   FD_VM_INTERP_INSTR_BEGIN(0x7c) /* FD_SBPF_OP_RSH_REG */
     667         361 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L294 */
     668         361 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, (uint)reg_src ) );
     669         361 :   FD_VM_INTERP_INSTR_END;
     670             : 
     671             :   FD_VM_INTERP_BRANCH_BEGIN(0x7d) /* FD_SBPF_OP_JSGE_REG */
     672       38750 :     pc += fd_ulong_if( (long)reg_dst>=(long)reg_src, offset, 0UL );
     673       38750 :   FD_VM_INTERP_BRANCH_END;
     674             : 
     675             :   FD_VM_INTERP_INSTR_BEGIN(0x7e) /* FD_SBPF_OP_UREM64_REG */
     676          64 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     677          18 :     reg[ dst ] = reg_dst % reg_src;
     678          18 :   FD_VM_INTERP_INSTR_END;
     679             : 
     680             :   FD_VM_INTERP_INSTR_BEGIN(0x7f) /* FD_SBPF_OP_RSH64_REG */
     681        7372 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L379 */
     682        7372 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, reg_src );
     683        7372 :   FD_VM_INTERP_INSTR_END;
     684             : 
     685             :   /* 0x80-0x8f ********************************************************/
     686             : 
     687             :   FD_VM_INTERP_INSTR_BEGIN(0x84) /* FD_SBPF_OP_NEG */
     688         393 :     reg[ dst ] = (ulong)( -(uint)reg_dst );
     689         393 :   FD_VM_INTERP_INSTR_END;
     690             : 
     691       54916 :   FD_VM_INTERP_BRANCH_BEGIN(0x85) { /* FD_SBPF_OP_CALL_IMM */
     692             : 
     693       54923 :     fd_sbpf_syscalls_t const * syscall = imm!=fd_sbpf_syscalls_key_null() ? fd_sbpf_syscalls_query_const( syscalls, (ulong)imm, NULL ) : NULL;
     694       54916 :     if( FD_UNLIKELY( !syscall ) ) { /* Optimize for the syscall case */
     695             : 
     696             :       /* Note we do the stack push before updating the pc(*). This implies
     697             :        that the call stack frame gets allocated _before_ checking if the
     698             :        call target is valid.  It would be fine to switch the order
     699             :        though such would change the precise faulting semantics of
     700             :        sigtextbr and sigstack.
     701             : 
     702             :        (*)but after checking calldests, see point below. */
     703             : 
     704             :       /* Agave's order of checks
     705             :          (https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/interpreter.rs#L565-L572):
     706             :           1. Lookup imm hash in FunctionRegistry (calldests_test is our equivalent)
     707             :           2. Push stack frame
     708             :           3. Check PC
     709             :           4. Update PC
     710             : 
     711             :           Following this precisely is impossible as our PC check also
     712             :           serves as a bounds check for the calldests_test call. So we
     713             :           have to perform step 3 before step 1. The following
     714             :           is a best-effort implementation that should match the VM state
     715             :           in all ways except error code. */
     716             : 
     717             :       /* Special case to handle entrypoint.
     718             :          ebpf::hash_symbol_name(b"entrypoint") = 0x71e3cf81, and
     719             :          fd_pchash_inverse( 0x71e3cf81U ) = 0xb00c380U */
     720       46147 :       if( FD_UNLIKELY( imm==0x71e3cf81U ) ) {
     721           0 :         FD_VM_INTERP_STACK_PUSH;
     722           0 :         pc = entry_pc - 1;
     723       46147 :       } else {
     724       46147 :         ulong target_pc = (ulong)fd_pchash_inverse( imm );
     725       46147 :         if( FD_UNLIKELY( target_pc>=text_cnt ) ) {
     726          90 :           goto sigillbr; /* different return between 0x85 and 0x8d */
     727          90 :         }
     728       46057 :         if( FD_UNLIKELY( !fd_sbpf_calldests_test( calldests, target_pc ) ) ) {
     729           0 :           goto sigillbr;
     730           0 :         }
     731       92113 :         FD_VM_INTERP_STACK_PUSH;
     732       92113 :         pc = target_pc - 1;
     733       92113 :       }
     734             : 
     735       46147 :     } else {
     736             : 
     737        8769 :       FD_VM_INTERP_SYSCALL_EXEC;
     738             : 
     739        7941 :     }
     740       54916 :   } FD_VM_INTERP_BRANCH_END;
     741             : 
     742             :   /* SIMD-0178: Static syscalls (SBPF V3+)
     743             :      https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/interpreter.rs#L542-L577 */
     744       53997 :   FD_VM_INTERP_BRANCH_BEGIN(0x85_static) { /* FD_SBPF_OP_CALL_IMM (static syscalls) */
     745             : 
     746        1309 :     if( src == 0 ) {
     747             :       /* External syscall
     748             :          https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/interpreter.rs#L545-L553 */
     749        1299 :       fd_sbpf_syscalls_t const * syscall = imm!=fd_sbpf_syscalls_key_null() ? fd_sbpf_syscalls_query_const( syscalls, (ulong)imm, NULL ) : NULL;
     750        1299 :       if( FD_UNLIKELY( !syscall ) ) goto sigillbr;
     751        2449 :       FD_VM_INTERP_SYSCALL_EXEC;
     752        2449 :     } else if( src == 1 ) {
     753             :       /* Internal call
     754             :          https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/interpreter.rs#L555-L563
     755             :          https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L97-L103 */
     756          10 :       long target_pc_l = fd_long_sat_add( (long)pc, fd_long_sat_add( (long)(int)imm, 1L ) );
     757          10 :       if( FD_UNLIKELY( target_pc_l<0L || (ulong)target_pc_l>=text_cnt ) ) goto sigillbr;
     758          20 :       FD_VM_INTERP_STACK_PUSH;
     759          20 :       pc = (ulong)target_pc_l - 1;
     760          20 :     } else {
     761             :       /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/interpreter.rs#L574-L576 */
     762           0 :       goto sigillbr;
     763           0 :     }
     764             : 
     765        1309 :   } FD_VM_INTERP_BRANCH_END;
     766             : 
     767             :   FD_VM_INTERP_INSTR_BEGIN(0x86) /* FD_SBPF_OP_LMUL32_IMM */
     768          59 :     reg[ dst ] = (ulong)( (uint)reg_dst * imm );
     769          59 :   FD_VM_INTERP_INSTR_END;
     770             : 
     771        1559 :   FD_VM_INTERP_INSTR_BEGIN(0x87) { /* FD_SBPF_OP_STW */
     772        1559 :     ulong vaddr   = reg_dst + offset;
     773        1559 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL );
     774        1559 :     int   sigsegv = !haddr;
     775        1559 :     if( FD_UNLIKELY( sigsegv ) ) {
     776           5 :       vm->segv_vaddr       = vaddr;
     777           5 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     778           5 :       vm->segv_access_len  = 4UL;
     779           5 :       goto sigsegv;
     780           5 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     781        1554 :     fd_vm_mem_st_4( haddr, imm );
     782        1554 :   } FD_VM_INTERP_INSTR_END;
     783             : 
     784             :   FD_VM_INTERP_INSTR_BEGIN(0x87depr) /* FD_SBPF_OP_NEG64 deprecated */
     785        6554 :     reg[ dst ] = -reg_dst;
     786        6554 :   FD_VM_INTERP_INSTR_END;
     787             : 
     788        8507 :   FD_VM_INTERP_INSTR_BEGIN(0x8c) { /* FD_SBPF_OP_LDXW */
     789        8507 :     ulong vaddr   = reg_src + offset;
     790        8507 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_ld_sz, 0, 0UL );
     791        8507 :     int   sigsegv = !haddr;
     792        8507 :     if( FD_UNLIKELY( sigsegv ) ) {
     793           7 :       vm->segv_vaddr       = vaddr;
     794           7 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     795           7 :       vm->segv_access_len  = 4UL;
     796           7 :       goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     797           7 :     }
     798        8500 :     reg[ dst ] = fd_vm_mem_ld_4( haddr );
     799        8500 :   }
     800        8500 :   FD_VM_INTERP_INSTR_END;
     801             : 
     802        8500 :   FD_VM_INTERP_BRANCH_BEGIN(0x8d) { /* FD_SBPF_OP_CALL_REG */
     803             : 
     804        1367 :     FD_VM_INTERP_STACK_PUSH;
     805             : 
     806             :     /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/interpreter.rs#L528-L540 */
     807        1367 :     ulong vaddr = fd_sbpf_callx_uses_src_reg_enabled( sbpf_version ) ? reg_src
     808        1367 :                 : fd_sbpf_callx_uses_dst_reg_enabled( sbpf_version ) ? reg[ dst ]
     809        1354 :                 : reg[ imm & 15U ];
     810             : 
     811             :     /* Notes: Agave checks region and target_pc before updating the pc.
     812             :        To match their state, we do the same, even though we could simply
     813             :        update the pc and let BRANCH_END fail.
     814             :        Also, Agave doesn't check alignment. */
     815             : 
     816        1367 :     ulong region = vaddr >> 32;
     817             :     /* ulong align  = vaddr & 7UL; */
     818        1367 :     ulong target_pc = ((vaddr & FD_VM_OFFSET_MASK) - vm->text_off) / 8UL;
     819        1367 :     if( FD_UNLIKELY( (region!=1UL) | (target_pc>=text_cnt) ) ) goto sigtextbr; /* Note: untaken branches don't consume BTB */
     820        1311 :     pc = target_pc - 1;
     821             : 
     822        1311 :   } FD_VM_INTERP_BRANCH_END;
     823             : 
     824             :   FD_VM_INTERP_INSTR_BEGIN(0x8e) /* FD_SBPF_OP_LMUL32_REG */
     825          61 :     reg[ dst ] = (ulong)( (uint)reg_dst * (uint)reg_src );
     826          61 :   FD_VM_INTERP_INSTR_END;
     827             : 
     828       17118 :   FD_VM_INTERP_INSTR_BEGIN(0x8f) { /* FD_SBPF_OP_STXW */
     829       17118 :     ulong vaddr    = reg_dst + offset;
     830       17118 :     ulong haddr    = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL );
     831       17118 :     int   sigsegv  = !haddr;
     832       17118 :     if( FD_UNLIKELY( sigsegv ) ) {
     833          16 :       vm->segv_vaddr       = vaddr;
     834          16 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     835          16 :       vm->segv_access_len  = 4UL;
     836          16 :       goto sigsegv;
     837          16 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     838       17102 :     fd_vm_mem_st_4( haddr, (uint)reg_src );
     839       17102 :   }
     840       17102 :   FD_VM_INTERP_INSTR_END;
     841             : 
     842             :   /* 0x90 - 0x9f ******************************************************/
     843             : 
     844             :   FD_VM_INTERP_INSTR_BEGIN(0x94) /* FD_SBPF_OP_MOD_IMM */
     845        2857 :     reg[ dst ] = (ulong)( (uint)reg_dst % imm );
     846        2857 :   FD_VM_INTERP_INSTR_END;
     847             : 
     848             :   FD_VM_INTERP_BRANCH_BEGIN(0x95) /* FD_SBPF_OP_EXIT */
     849       48121 :       /* Agave JIT VM exit implementation analysis below.
     850       48121 : 
     851       48121 :        Agave references:
     852       48121 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L503-L509
     853       48121 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L697-L702 */
     854       48121 :     if( FD_UNLIKELY( !frame_cnt ) ) goto sigexit; /* Exit program */
     855       46486 :     frame_cnt--;
     856       46486 :     reg[6]   = shadow[ frame_cnt ].r6;
     857       46486 :     reg[7]   = shadow[ frame_cnt ].r7;
     858       46486 :     reg[8]   = shadow[ frame_cnt ].r8;
     859       46486 :     reg[9]   = shadow[ frame_cnt ].r9;
     860       46486 :     reg[10]  = shadow[ frame_cnt ].r10;
     861       46486 :     pc       = shadow[ frame_cnt ].pc;
     862       46486 :   FD_VM_INTERP_BRANCH_END;
     863             : 
     864             :   FD_VM_INTERP_INSTR_BEGIN(0x96) /* FD_SBPF_OP_LMUL64_IMM */
     865          77 :     reg[ dst ] = reg_dst * (ulong)(long)(int)imm;
     866          77 :   FD_VM_INTERP_INSTR_END;
     867             : 
     868        6740 :   FD_VM_INTERP_INSTR_BEGIN(0x97) { /* FD_SBPF_OP_STQ */
     869        6740 :     ulong vaddr   = reg_dst + offset;
     870        6740 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL );
     871        6740 :     int   sigsegv = !haddr;
     872        6740 :     if( FD_UNLIKELY( sigsegv ) ) {
     873           7 :       vm->segv_vaddr       = vaddr;
     874           7 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     875           7 :       vm->segv_access_len  = 8UL;
     876           7 :       goto sigsegv;
     877           7 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     878        6733 :     fd_vm_mem_st_8( haddr, (ulong)(long)(int)imm );
     879        6733 :   }
     880        6733 :   FD_VM_INTERP_INSTR_END;
     881             : 
     882      288713 :   FD_VM_INTERP_INSTR_BEGIN(0x9c) { /* FD_SBPF_OP_LDXQ */
     883      288713 :     ulong vaddr   = reg_src + offset;
     884      288713 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_ld_sz, 0, 0UL );
     885      288713 :     int   sigsegv = !haddr;
     886      288713 :     if( FD_UNLIKELY( sigsegv ) ) {
     887          84 :       vm->segv_vaddr       = vaddr;
     888          84 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     889          84 :       vm->segv_access_len  = 8UL;
     890          84 :       goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     891          84 :     }
     892      288629 :     reg[ dst ] = fd_vm_mem_ld_8( haddr );
     893      288629 :   }
     894      288629 :   FD_VM_INTERP_INSTR_END;
     895             : 
     896             :   FD_VM_INTERP_INSTR_BEGIN(0x9e) /* FD_SBPF_OP_LMUL64_REG */
     897          93 :     reg[ dst ] = reg_dst * reg_src;
     898          93 :   FD_VM_INTERP_INSTR_END;
     899             : 
     900      537160 :   FD_VM_INTERP_INSTR_BEGIN(0x9f) { /* FD_SBPF_OP_STXQ */
     901      537160 :     ulong vaddr   = reg_dst + offset;
     902      537160 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL );
     903      537160 :     int   sigsegv = !haddr;
     904      537160 :     if( FD_UNLIKELY( sigsegv ) ) {
     905          18 :       vm->segv_vaddr       = vaddr;
     906          18 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     907          18 :       vm->segv_access_len  = 8UL;
     908          18 :       goto sigsegv;
     909          18 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     910      537142 :     fd_vm_mem_st_8( haddr, reg_src );
     911      537142 :   }
     912      537142 :   FD_VM_INTERP_INSTR_END;
     913             : 
     914             :   FD_VM_INTERP_INSTR_BEGIN(0x97depr) /* FD_SBPF_OP_MOD64_IMM */
     915       56507 :     reg[ dst ] = reg_dst % (ulong)(long)(int)imm;
     916       56507 :   FD_VM_INTERP_INSTR_END;
     917             : 
     918             :   FD_VM_INTERP_INSTR_BEGIN(0x9cdepr) /* FD_SBPF_OP_MOD_REG */
     919       36034 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     920       35786 :     reg[ dst ] = (ulong)( ((uint)reg_dst % (uint)reg_src) );
     921       35786 :   FD_VM_INTERP_INSTR_END;
     922             : 
     923             :   FD_VM_INTERP_INSTR_BEGIN(0x9fdepr) /* FD_SBPF_OP_MOD64_REG */
     924       36635 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     925       36446 :     reg[ dst ] = reg_dst % reg_src;
     926       36446 :   FD_VM_INTERP_INSTR_END;
     927             : 
     928             :   /* 0xa0 - 0xaf ******************************************************/
     929             : 
     930             :   FD_VM_INTERP_INSTR_BEGIN(0xa4) /* FD_SBPF_OP_XOR_IMM */
     931      120507 :     reg[ dst ] = (ulong)( (uint)reg_dst ^ imm );
     932      120507 :   FD_VM_INTERP_INSTR_END;
     933             : 
     934             :   FD_VM_INTERP_BRANCH_BEGIN(0xa5) /* FD_SBPF_OP_JLT_IMM */
     935      133652 :     pc += fd_ulong_if( reg_dst<(ulong)(long)(int)imm, offset, 0UL );
     936      133652 :   FD_VM_INTERP_BRANCH_END;
     937             : 
     938             :   FD_VM_INTERP_INSTR_BEGIN(0xa7) /* FD_SBPF_OP_XOR64_IMM */
     939        6428 :     reg[ dst ] = reg_dst ^ (ulong)(long)(int)imm;
     940        6428 :   FD_VM_INTERP_INSTR_END;
     941             : 
     942             :   FD_VM_INTERP_INSTR_BEGIN(0xac) /* FD_SBPF_OP_XOR_REG */
     943       25226 :     reg[ dst ] = (ulong)(uint)( reg_dst ^ reg_src );
     944       25226 :   FD_VM_INTERP_INSTR_END;
     945             : 
     946             :   FD_VM_INTERP_BRANCH_BEGIN(0xad) /* FD_SBPF_OP_JLT_REG */
     947       22950 :     pc += fd_ulong_if( reg_dst<reg_src, offset, 0UL );
     948       22950 :   FD_VM_INTERP_BRANCH_END;
     949             : 
     950             :   FD_VM_INTERP_INSTR_BEGIN(0xaf) /* FD_SBPF_OP_XOR64_REG */
     951      287349 :     reg[ dst ] = reg_dst ^ reg_src;
     952      287349 :   FD_VM_INTERP_INSTR_END;
     953             : 
     954             :   /* 0xb0 - 0xbf ******************************************************/
     955             : 
     956             :   FD_VM_INTERP_INSTR_BEGIN(0xb4) /* FD_SBPF_OP_MOV_IMM */
     957      352999 :     reg[ dst ] = (ulong)imm;
     958      352999 :   FD_VM_INTERP_INSTR_END;
     959             : 
     960             :   FD_VM_INTERP_BRANCH_BEGIN(0xb5) /* FD_SBPF_OP_JLE_IMM */
     961      379825 :     pc += fd_ulong_if( reg_dst<=(ulong)(long)(int)imm, offset, 0UL );
     962      379825 :   FD_VM_INTERP_BRANCH_END;
     963             : 
     964             :   FD_VM_INTERP_INSTR_BEGIN(0xb6) /* FD_SBPF_OP_SHMUL64_IMM */
     965          42 :     reg[ dst ] = (ulong)(( (int128)(long)reg_dst * (int128)(long)(int)imm ) >> 64 );
     966          42 :   FD_VM_INTERP_INSTR_END;
     967             : 
     968             :   FD_VM_INTERP_INSTR_BEGIN(0xb7) /* FD_SBPF_OP_MOV64_IMM */
     969      594277 :     reg[ dst ] = (ulong)(long)(int)imm;
     970      594277 :   FD_VM_INTERP_INSTR_END;
     971             : 
     972             :   FD_VM_INTERP_INSTR_BEGIN(0xbc) /* FD_SBPF_OP_MOV_REG */
     973         377 :     reg[ dst ] = (ulong)(long)(int)reg_src;
     974         377 :   FD_VM_INTERP_INSTR_END;
     975             : 
     976             :   FD_VM_INTERP_INSTR_BEGIN(0xbcdepr) /* FD_SBPF_OP_MOV_REG deprecated SIMD-0174 */
     977      228554 :     reg[ dst ] = (ulong)(uint)reg_src;
     978      228554 :   FD_VM_INTERP_INSTR_END;
     979             : 
     980             :   FD_VM_INTERP_BRANCH_BEGIN(0xbd) /* FD_SBPF_OP_JLE_REG */
     981         785 :     pc += fd_ulong_if( reg_dst<=reg_src, offset, 0UL );
     982         785 :   FD_VM_INTERP_BRANCH_END;
     983             : 
     984             :   FD_VM_INTERP_INSTR_BEGIN(0xbe) /* FD_SBPF_OP_SHMUL64_REG */
     985          46 :     reg[ dst ] = (ulong)(( (int128)(long)reg_dst * (int128)(long)reg_src ) >> 64 );
     986          46 :   FD_VM_INTERP_INSTR_END;
     987             : 
     988             :   FD_VM_INTERP_INSTR_BEGIN(0xbf) /* FD_SBPF_OP_MOV64_REG */
     989      405469 :     reg[ dst ] = reg_src;
     990      405469 :   FD_VM_INTERP_INSTR_END;
     991             : 
     992             :   /* 0xc0 - 0xcf ******************************************************/
     993             : 
     994             :   FD_VM_INTERP_INSTR_BEGIN(0xc4) /* FD_SBPF_OP_ARSH_IMM */
     995         375 :     reg[ dst ] = (ulong)(uint)( FD_RUST_INT_WRAPPING_SHR( (int)reg_dst, imm ) );
     996         375 :   FD_VM_INTERP_INSTR_END;
     997             : 
     998             :   FD_VM_INTERP_BRANCH_BEGIN(0xc5) /* FD_SBPF_OP_JSLT_IMM */ /* FIXME: CHECK IMM SIGN EXTENSION */
     999        3394 :     pc += fd_ulong_if( (long)reg_dst<(long)(int)imm, offset, 0UL );
    1000        3394 :   FD_VM_INTERP_BRANCH_END;
    1001             : 
    1002             :   FD_VM_INTERP_INSTR_BEGIN(0xc6) /* FD_SBPF_OP_SDIV32_IMM */
    1003          57 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)imm==-1) ) ) goto sigfpeof;
    1004          57 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst / (int)imm );
    1005          57 :   FD_VM_INTERP_INSTR_END;
    1006             : 
    1007             :   FD_VM_INTERP_INSTR_BEGIN(0xc7) /* FD_SBPF_OP_ARSH64_IMM */
    1008       10345 :     reg[ dst ] = (ulong)( FD_RUST_LONG_WRAPPING_SHR( (long)reg_dst, imm ) );
    1009       10345 :   FD_VM_INTERP_INSTR_END;
    1010             : 
    1011             :   FD_VM_INTERP_INSTR_BEGIN(0xcc) /* FD_SBPF_OP_ARSH_REG */
    1012       22849 :     reg[ dst ] = (ulong)(uint)( FD_RUST_INT_WRAPPING_SHR( (int)reg_dst, (uint)reg_src ) );
    1013       22849 :   FD_VM_INTERP_INSTR_END;
    1014             : 
    1015             :   FD_VM_INTERP_BRANCH_BEGIN(0xcd) /* FD_SBPF_OP_JSLT_REG */
    1016      607162 :     pc += fd_ulong_if( (long)reg_dst<(long)reg_src, offset, 0UL );
    1017      607162 :   FD_VM_INTERP_BRANCH_END;
    1018             : 
    1019             :   FD_VM_INTERP_INSTR_BEGIN(0xce) /* FD_SBPF_OP_SDIV32_REG */
    1020          52 :     if( FD_UNLIKELY( !(int)reg_src ) ) goto sigfpe;
    1021          12 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)reg_src==-1) ) ) goto sigfpeof;
    1022          12 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst / (int)reg_src );
    1023          12 :   FD_VM_INTERP_INSTR_END;
    1024             : 
    1025             :   FD_VM_INTERP_INSTR_BEGIN(0xcf) /* FD_SBPF_OP_ARSH64_REG */
    1026         362 :     reg[ dst ] = (ulong)( FD_RUST_LONG_WRAPPING_SHR( (long)reg_dst, reg_src ) );
    1027         362 :   FD_VM_INTERP_INSTR_END;
    1028             : 
    1029             :   /* 0xd0 - 0xdf ******************************************************/
    1030             : 
    1031             :   FD_VM_INTERP_INSTR_BEGIN(0xd4) /* FD_SBPF_OP_END_LE */
    1032      112422 :     switch( imm ) {
    1033         105 :     case 16U: reg[ dst ] = (ushort)reg_dst; break;
    1034       36371 :     case 32U: reg[ dst ] = (uint)  reg_dst; break;
    1035       75946 :     case 64U:                               break;
    1036           0 :     default: goto siginv;
    1037      112422 :     }
    1038      112422 :   FD_VM_INTERP_INSTR_END;
    1039             : 
    1040             :   FD_VM_INTERP_BRANCH_BEGIN(0xd5) /* FD_SBPF_OP_JSLE_IMM */
    1041        5053 :     pc += fd_ulong_if( (long)reg_dst<=(long)(int)imm, offset, 0UL );
    1042        5053 :   FD_VM_INTERP_BRANCH_END;
    1043             : 
    1044             :   FD_VM_INTERP_INSTR_BEGIN(0xd6) /* FD_SBPF_OP_SDIV64_IMM */
    1045          64 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)(int)imm==-1L) ) ) goto sigfpeof;
    1046          64 :     reg[ dst ] = (ulong)( (long)reg_dst / (long)(int)imm );
    1047          64 :   FD_VM_INTERP_INSTR_END;
    1048             : 
    1049             :   FD_VM_INTERP_INSTR_BEGIN(0xdc) /* FD_SBPF_OP_END_BE */
    1050      144092 :     switch( imm ) {
    1051       33255 :     case 16U: reg[ dst ] = (ulong)fd_ushort_bswap( (ushort)reg_dst ); break;
    1052       66297 :     case 32U: reg[ dst ] = (ulong)fd_uint_bswap  ( (uint)  reg_dst ); break;
    1053       44540 :     case 64U: reg[ dst ] =        fd_ulong_bswap ( (ulong) reg_dst ); break;
    1054           0 :     default: goto siginv;
    1055      144092 :     }
    1056      144092 :   FD_VM_INTERP_INSTR_END;
    1057             : 
    1058             :   FD_VM_INTERP_BRANCH_BEGIN(0xdd) /* FD_SBPF_OP_JSLE_REG */
    1059      369593 :     pc += fd_ulong_if( (long)reg_dst<=(long)reg_src, offset, 0UL );
    1060      369593 :   FD_VM_INTERP_BRANCH_END;
    1061             : 
    1062             :   FD_VM_INTERP_INSTR_BEGIN(0xde) /* FD_SBPF_OP_SDIV64_REG */
    1063          73 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
    1064          16 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)reg_src==-1L) ) ) goto sigfpeof;
    1065          16 :     reg[ dst ] = (ulong)( (long)reg_dst / (long)reg_src );
    1066          16 :   FD_VM_INTERP_INSTR_END;
    1067             : 
    1068             :   /* 0xe0 - 0xef ******************************************************/
    1069             : 
    1070             :   FD_VM_INTERP_INSTR_BEGIN(0xe6) /* FD_SBPF_OP_SREM32_IMM */
    1071          64 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)imm==-1) ) ) goto sigfpeof;
    1072          64 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst % (int)imm );
    1073          64 :   FD_VM_INTERP_INSTR_END;
    1074             : 
    1075             :   FD_VM_INTERP_INSTR_BEGIN(0xee) /* FD_SBPF_OP_SREM32_REG */
    1076          53 :     if( FD_UNLIKELY( !(int)reg_src ) ) goto sigfpe;
    1077           8 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)reg_src==-1) ) ) goto sigfpeof;
    1078           8 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst % (int)reg_src );
    1079           8 :   FD_VM_INTERP_INSTR_END;
    1080             : 
    1081             :   /* 0xf0 - 0xff ******************************************************/
    1082             : 
    1083             :   FD_VM_INTERP_INSTR_BEGIN(0xf6) /* FD_SBPF_OP_SREM64_IMM */
    1084          74 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)(int)imm==-1L) ) ) goto sigfpeof;
    1085          74 :     reg[ dst ] = (ulong)( (long)reg_dst % (long)(int)imm );
    1086          74 :   FD_VM_INTERP_INSTR_END;
    1087             : 
    1088             :   FD_VM_INTERP_INSTR_BEGIN(0xf7) /* FD_SBPF_OP_HOR64 */
    1089      149190 :     reg[ dst ] = reg_dst | (((ulong)imm) << 32);
    1090      149190 :   FD_VM_INTERP_INSTR_END;
    1091             : 
    1092             :   FD_VM_INTERP_INSTR_BEGIN(0xfe) /* FD_SBPF_OP_SREM64_REG */
    1093          56 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
    1094          10 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)reg_src==-1L) ) ) goto sigfpeof;
    1095          10 :     reg[ dst ] = (ulong)( (long)reg_dst % (long)reg_src );
    1096          10 :   FD_VM_INTERP_INSTR_END;
    1097             : 
    1098             :   /* SIMD-0377: JMP32
    1099             :      https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/interpreter.rs#L480-L501
    1100             : 
    1101             :      0x16 - 0xde ******************************************************/
    1102             : 
    1103             :   FD_VM_INTERP_BRANCH_BEGIN(0x16_jmp32) /* FD_SBPF_OP_JEQ32_IMM */
    1104           1 :     pc += fd_ulong_if( (uint)reg_dst==(uint)imm, offset, 0UL );
    1105           1 :   FD_VM_INTERP_BRANCH_END;
    1106             : 
    1107             :   FD_VM_INTERP_BRANCH_BEGIN(0x1e_jmp32) /* FD_SBPF_OP_JEQ32_REG */
    1108           0 :     pc += fd_ulong_if( (uint)reg_dst==(uint)reg_src, offset, 0UL );
    1109           0 :   FD_VM_INTERP_BRANCH_END;
    1110             : 
    1111             :   FD_VM_INTERP_BRANCH_BEGIN(0x26_jmp32) /* FD_SBPF_OP_JGT32_IMM */
    1112           0 :     pc += fd_ulong_if( (uint)reg_dst>(uint)imm, offset, 0UL );
    1113           0 :   FD_VM_INTERP_BRANCH_END;
    1114             : 
    1115             :   FD_VM_INTERP_BRANCH_BEGIN(0x2e_jmp32) /* FD_SBPF_OP_JGT32_REG */
    1116           1 :     pc += fd_ulong_if( (uint)reg_dst>(uint)reg_src, offset, 0UL );
    1117           1 :   FD_VM_INTERP_BRANCH_END;
    1118             : 
    1119             :   FD_VM_INTERP_BRANCH_BEGIN(0x36_jmp32) /* FD_SBPF_OP_JGE32_IMM */
    1120           0 :     pc += fd_ulong_if( (uint)reg_dst>=(uint)imm, offset, 0UL );
    1121           0 :   FD_VM_INTERP_BRANCH_END;
    1122             : 
    1123             :   FD_VM_INTERP_BRANCH_BEGIN(0x3e_jmp32) /* FD_SBPF_OP_JGE32_REG */
    1124           0 :     pc += fd_ulong_if( (uint)reg_dst>=(uint)reg_src, offset, 0UL );
    1125           0 :   FD_VM_INTERP_BRANCH_END;
    1126             : 
    1127             :   FD_VM_INTERP_BRANCH_BEGIN(0x46_jmp32) /* FD_SBPF_OP_JSET32_IMM */
    1128           2 :     pc += fd_ulong_if( !!((uint)reg_dst & (uint)imm), offset, 0UL );
    1129           2 :   FD_VM_INTERP_BRANCH_END;
    1130             : 
    1131             :   FD_VM_INTERP_BRANCH_BEGIN(0x4e_jmp32) /* FD_SBPF_OP_JSET32_REG */
    1132           0 :     pc += fd_ulong_if( !!((uint)reg_dst & (uint)reg_src), offset, 0UL );
    1133           0 :   FD_VM_INTERP_BRANCH_END;
    1134             : 
    1135             :   FD_VM_INTERP_BRANCH_BEGIN(0x56_jmp32) /* FD_SBPF_OP_JNE32_IMM */
    1136           0 :     pc += fd_ulong_if( (uint)reg_dst!=(uint)imm, offset, 0UL );
    1137           0 :   FD_VM_INTERP_BRANCH_END;
    1138             : 
    1139             :   FD_VM_INTERP_BRANCH_BEGIN(0x5e_jmp32) /* FD_SBPF_OP_JNE32_REG */
    1140           0 :     pc += fd_ulong_if( (uint)reg_dst!=(uint)reg_src, offset, 0UL );
    1141           0 :   FD_VM_INTERP_BRANCH_END;
    1142             : 
    1143             :   FD_VM_INTERP_BRANCH_BEGIN(0x66_jmp32) /* FD_SBPF_OP_JSGT32_IMM */
    1144           0 :     pc += fd_ulong_if( (int)reg_dst>(int)imm, offset, 0UL );
    1145           0 :   FD_VM_INTERP_BRANCH_END;
    1146             : 
    1147             :   FD_VM_INTERP_BRANCH_BEGIN(0x6e_jmp32) /* FD_SBPF_OP_JSGT32_REG */
    1148           0 :     pc += fd_ulong_if( (int)reg_dst>(int)reg_src, offset, 0UL );
    1149           0 :   FD_VM_INTERP_BRANCH_END;
    1150             : 
    1151             :   FD_VM_INTERP_BRANCH_BEGIN(0x76_jmp32) /* FD_SBPF_OP_JSGE32_IMM */
    1152           4 :     pc += fd_ulong_if( (int)reg_dst>=(int)imm, offset, 0UL );
    1153           4 :   FD_VM_INTERP_BRANCH_END;
    1154             : 
    1155             :   FD_VM_INTERP_BRANCH_BEGIN(0x7e_jmp32) /* FD_SBPF_OP_JSGE32_REG */
    1156           0 :     pc += fd_ulong_if( (int)reg_dst>=(int)reg_src, offset, 0UL );
    1157           0 :   FD_VM_INTERP_BRANCH_END;
    1158             : 
    1159             :   FD_VM_INTERP_BRANCH_BEGIN(0xa6_jmp32) /* FD_SBPF_OP_JLT32_IMM */
    1160           0 :     pc += fd_ulong_if( (uint)reg_dst<(uint)imm, offset, 0UL );
    1161           0 :   FD_VM_INTERP_BRANCH_END;
    1162             : 
    1163             :   FD_VM_INTERP_BRANCH_BEGIN(0xae_jmp32) /* FD_SBPF_OP_JLT32_REG */
    1164           1 :     pc += fd_ulong_if( (uint)reg_dst<(uint)reg_src, offset, 0UL );
    1165           1 :   FD_VM_INTERP_BRANCH_END;
    1166             : 
    1167             :   FD_VM_INTERP_BRANCH_BEGIN(0xb6_jmp32) /* FD_SBPF_OP_JLE32_IMM */
    1168           0 :     pc += fd_ulong_if( (uint)reg_dst<=(uint)imm, offset, 0UL );
    1169           0 :   FD_VM_INTERP_BRANCH_END;
    1170             : 
    1171             :   FD_VM_INTERP_BRANCH_BEGIN(0xbe_jmp32) /* FD_SBPF_OP_JLE32_REG */
    1172           0 :     pc += fd_ulong_if( (uint)reg_dst<=(uint)reg_src, offset, 0UL );
    1173           0 :   FD_VM_INTERP_BRANCH_END;
    1174             : 
    1175             :   FD_VM_INTERP_BRANCH_BEGIN(0xc6_jmp32) /* FD_SBPF_OP_JSLT32_IMM */
    1176           2 :     pc += fd_ulong_if( (int)reg_dst<(int)imm, offset, 0UL );
    1177           2 :   FD_VM_INTERP_BRANCH_END;
    1178             : 
    1179             :   FD_VM_INTERP_BRANCH_BEGIN(0xce_jmp32) /* FD_SBPF_OP_JSLT32_REG */
    1180           1 :     pc += fd_ulong_if( (int)reg_dst<(int)reg_src, offset, 0UL );
    1181           1 :   FD_VM_INTERP_BRANCH_END;
    1182             : 
    1183             :   FD_VM_INTERP_BRANCH_BEGIN(0xd6_jmp32) /* FD_SBPF_OP_JSLE32_IMM */
    1184           0 :     pc += fd_ulong_if( (int)reg_dst<=(int)imm, offset, 0UL );
    1185           0 :   FD_VM_INTERP_BRANCH_END;
    1186             : 
    1187             :   FD_VM_INTERP_BRANCH_BEGIN(0xde_jmp32) /* FD_SBPF_OP_JSLE32_REG */
    1188           1 :     pc += fd_ulong_if( (int)reg_dst<=(int)reg_src, offset, 0UL );
    1189           1 :   FD_VM_INTERP_BRANCH_END;
    1190             : 
    1191             :   /* FIXME: sigbus/sigrdonly are mapped to sigsegv for simplicity
    1192             :      currently but could be enabled if desired. */
    1193             : 
    1194             :   /* Note: sigtextbr is for sigtext errors that occur on branching
    1195             :      instructions (i.e., prefixed with FD_VM_INTERP_BRANCH_BEGIN).
    1196             :      We skip a repeat ic accumulation in FD_VM_INTERP_FAULT */
    1197             : 
    1198             :   /* FD_VM_INTERP_FAULT accumulates to ic and cu all non-faulting
    1199             :      instructions preceding a fault generated by a non-branching
    1200             :      instruction.  When a non-branching instruction faults, pc is at the
    1201             :      instruction and the number of non-branching instructions that have
    1202             :      not yet been reflected in ic and cu is:
    1203             : 
    1204             :        pc - pc0 + 1 - ic_correction
    1205             : 
    1206             :      as per the accounting described above. +1 to include the faulting
    1207             :      instruction itself.
    1208             : 
    1209             :      Branch faults use separate halt paths (such as sigtextbr) after
    1210             :      branch accounting, and do not invoke this macro. */
    1211             : 
    1212           0 : #define FD_VM_INTERP_FAULT                                                                 \
    1213        1454 :   ic_correction = pc - pc0 + 1UL - ic_correction;                                          \
    1214        1454 :   ic += ic_correction;                                                                     \
    1215        1454 :   if ( FD_UNLIKELY( ic_correction > cu ) ) err = FD_VM_ERR_EBPF_EXCEEDED_MAX_INSTRUCTIONS; \
    1216        1454 :   cu -= fd_ulong_min( ic_correction, cu )
    1217             : 
    1218           8 : sigtext_or_sigcost:
    1219             :   /* If the block text limit is exceeded, sigtext_or_sigcost will be
    1220             :      thrown. This could either be because the pc has exceeded the text
    1221             :      section, or we have exhausted our CU budget. The block text limit
    1222             :      is used to combine both of these checks into one, primarily for
    1223             :      performance reasons. We then disambiguate the scenarios in this
    1224             :      signal handler, as hitting either of these cases is rare. */
    1225           8 :   if( pc<text_cnt ) goto sigcost; else goto sigtext;
    1226           1 : sigtext:     err = FD_VM_ERR_EBPF_EXECUTION_OVERRUN;                                     FD_VM_INTERP_FAULT;                    goto interp_halt;
    1227          56 : sigtextbr:   err = FD_VM_ERR_EBPF_CALL_OUTSIDE_TEXT_SEGMENT;                             /* ic current */     /* cu current */  goto interp_halt;
    1228           1 : sigstack:    err = FD_VM_ERR_EBPF_CALL_DEPTH_EXCEEDED;                                   /* ic current */     /* cu current */  goto interp_halt;
    1229          21 : sigill:      err = FD_VM_ERR_EBPF_UNSUPPORTED_INSTRUCTION;                               FD_VM_INTERP_FAULT;                    goto interp_halt;
    1230         103 : sigillbr:    err = FD_VM_ERR_EBPF_UNSUPPORTED_INSTRUCTION;                               /* ic current */     /* cu current */  goto interp_halt;
    1231           0 : siginv:      err = FD_VM_ERR_EBPF_INVALID_INSTRUCTION;                                   /* ic current */     /* cu current */  goto interp_halt;
    1232         187 : sigsegv:     err = fd_vm_generate_access_violation( vm->segv_vaddr, vm->sbpf_version );  FD_VM_INTERP_FAULT;                    goto interp_halt;
    1233          32 : sigcost:     err = FD_VM_ERR_EBPF_EXCEEDED_MAX_INSTRUCTIONS;                             /* ic current */     cu = 0UL;         goto interp_halt;
    1234         951 : sigsyscall:  err = FD_VM_ERR_EBPF_SYSCALL_ERROR;                                         /* ic current */     /* cu current */  goto interp_halt;
    1235        1245 : sigfpe:      err = FD_VM_ERR_EBPF_DIVIDE_BY_ZERO;                                        FD_VM_INTERP_FAULT;                    goto interp_halt;
    1236           0 : sigfpeof:    err = FD_VM_ERR_EBPF_DIVIDE_OVERFLOW;                                       FD_VM_INTERP_FAULT;                    goto interp_halt;
    1237        1635 : sigexit:     /* err current */                                                           /* ic current */     /* cu current */  goto interp_halt;
    1238             : 
    1239           0 : #undef FD_VM_INTERP_FAULT
    1240             : 
    1241        4232 : interp_halt:
    1242             : 
    1243             :   /* Pack the unpacked execution state into vm to give a precise view of
    1244             :      the execution when the vm halted. */
    1245             : 
    1246        4232 :   vm->pc        = pc;
    1247        4232 :   vm->ic        = ic;
    1248        4232 :   vm->cu        = cu;
    1249        4232 :   vm->frame_cnt = frame_cnt;
    1250             : 
    1251        4232 : # undef FD_VM_INTERP_STACK_PUSH
    1252             : 
    1253        4232 : # undef FD_VM_INTERP_BRANCH_END
    1254        4232 : # undef FD_VM_INTERP_BRANCH_BEGIN
    1255             : 
    1256        4232 : # undef FD_VM_INTERP_BLOCK_TEXT_LIMIT
    1257             : 
    1258        4232 : # undef FD_VM_INTERP_INSTR_END
    1259        4232 : # undef FD_VM_INTERP_INSTR_BEGIN
    1260        4232 : # undef FD_VM_INTERP_INSTR_EXEC
    1261             : 
    1262        4232 : # if defined(__clang__)
    1263        4232 : # pragma clang diagnostic pop
    1264        4232 : # endif
    1265             : 
    1266        4232 : # if defined(__GNUC__)
    1267        4232 : # pragma GCC diagnostic pop
    1268        4232 : # endif
    1269             : 
    1270             : /*   Agave/JIT CU model analysis (and why we are conformant!):
    1271             : 
    1272             :      The Agave JIT employs a similar strategy of accumulating instructions
    1273             :      in a linear run and processing them at the start of a new linear
    1274             :      run/branch (side note: the JIT treats the LDQ instruction as a "branch"
    1275             :      that jumps pc + 2).
    1276             : 
    1277             :      In what is assumed to be an act of register conservation, the JIT
    1278             :      uses a catch-all "instruction meter" (IM) register (REGISTER_INSTRUCTION_METER)
    1279             :      that represents two different interpretations of the question
    1280             :      "how many instructions can I execute?".
    1281             : 
    1282             :      The IM, depending on where we are in the execution, either represents:
    1283             :         1. IM => The number of instructions remaining before exhausting CU
    1284             :         budget. This is analogous to vm->cu in our interpreter.
    1285             :         2. IM' => The first pc you cannot execute in the current linear run
    1286             :         with the remaining CU budget.  Mathematically, IM' = IM + pc0
    1287             :         where pc0, just like our definition, is the start of the linear run.
    1288             : 
    1289             :         Note: IM' can go past the actual basic block/segment. In-fact,
    1290             :         it typically does, and implies we can execute the full block without
    1291             :         exhausting CU budget (reminder that LDQ is treated as a branch).
    1292             : 
    1293             :       By default, the IM' form is used during execution. The IM form is used:
    1294             :         - (transiently) during the processing of a branch instruction
    1295             :         - in post-VM cleanup (updates EbpfVm::previous_instruction_meter).
    1296             : 
    1297             :       When a branch instruction is encountered, the JIT checks
    1298             :       for CU exhaustion with pc >= IM', and throws an exception if so. This is valid,
    1299             :       because as described above, IM' is the first PC beyond the budget.
    1300             : 
    1301             :       If we haven't exhausted our CU limit, it updates IM':
    1302             :         1. IM = IM' - (pc + 1)  # Note that IM' at this point is IM + pc0',
    1303             :                                 # where pc0' is the start of the current linear run.
    1304             :         2. IM' = IM + pc0       # pc0 is the start of the new linear run (typically the target pc)
    1305             : 
    1306             :       Code (that does the above in one ALU instruction):
    1307             :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L891
    1308             : 
    1309             : 
    1310             :       ### How does this relate to our interpreter?
    1311             : 
    1312             :       This process is similar to FD_VM_INTERP_BRANCH_BEGIN.
    1313             :       We just deal with the IM form throughout (with vm->cu and ic_correction).
    1314             :       If we break down step 1 from above with what we know about IM and IM',
    1315             :       we get the following:
    1316             :         1. IM = IM' - (pc + 1)
    1317             :            IM = (IM + pc0') - (pc + 1)
    1318             :            IM = IM + (pc0' - (pc + 1))
    1319             :            IM = IM - ((pc + 1) - pc0')
    1320             :            IM = IM - ic_correction
    1321             :       Here, ((pc + 1) - pc0') is the number of instructions executed in the current
    1322             :       linear run. This is the same as our ic_correction(*) in FD_VM_INTERP_BRANCH_BEGIN.
    1323             : 
    1324             :       If we replace IM with cu, this effectively becomes the
    1325             :            cu -= ic_correction
    1326             :       line in FD_VM_INTERP_BRANCH_BEGIN.
    1327             : 
    1328             :       (*) Note: ic_correction (also) takes two forms. It is either the instruction
    1329             :       accumulator or the number of instructions executed in the current linear run.
    1330             :       It (transiently) takes the latter form during FD_VM_INTERP_BRANCH_BEGIN and
    1331             :       FD_VM_INTERP_FAULT, and the former form otherwise.
    1332             : */
    1333             : 
    1334             : /* (WIP) Precise faulting and the Agave JIT:
    1335             : 
    1336             :    Since the cost model is a part of consensus, we need to conform with the Agave/JIT
    1337             :    cost model 1:1. To achieve that, our faulting model also needs to match precisely. This
    1338             :    section covers the various faults that the respective VMs implement and how they match.
    1339             : 
    1340             :    # Normal VM exit (sigexit):
    1341             :    VM exit instruction entrypoint: https://github.com/solana-labs/rbpf/blob/12237895305ab38514be865ebed6268553e4f589/src/jit.rs#L698-L708
    1342             : 
    1343             :    Pseudocode (with FD semantics):
    1344             :    ```
    1345             :     # pc is at the exit instruction
    1346             :     # pc0 is the start of the current linear run
    1347             :     if (frame_cnt == 0) {
    1348             :         goto sigexit;
    1349             :     }
    1350             :     ...
    1351             : 
    1352             :     sigexit:
    1353             :     if IM' <= pc {
    1354             :       goto sigcost;
    1355             :     } else {
    1356             :       goto interp_halt;
    1357             :     }
    1358             :     ```
    1359             : 
    1360             :     Breaking down the IM' <= pc check:
    1361             :     - IM' = IM + pc0
    1362             :     - pc  = ic + pc0, where (ic + 1) is the number of instructions executed in the current linear run
    1363             : 
    1364             :     IM' <= pc
    1365             :     IM + pc0 <= ic + pc0
    1366             :     IM <= ic
    1367             :     IM <= pc - pc0
    1368             :     IM < pc - pc0 + 1 # all unsigned integers
    1369             :     IM < ic_correction
    1370             : 
    1371             :     This is analogous to the ic_correction>cu check in VM_INTERP_BRANCH_BEGIN.
    1372             : 
    1373             :    # (TODO) Text Overrun (sigtext/sigsplit):
    1374             : 
    1375             : */

Generated by: LCOV version 1.14