LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_interp_core.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 591 606 97.5 %
Date: 2025-01-08 12:08:44 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      115653 : # if defined(__GNUC__) /* -Wpedantic rejects labels as values and rejects goto *expr */
      12      115653 : # pragma GCC diagnostic push
      13      115653 : # pragma GCC diagnostic ignored "-Wpedantic"
      14      115653 : # endif
      15             : 
      16      115653 : # if defined(__clang__) /* Clang is differently picky about labels as values and goto *expr */
      17      115653 : # pragma clang diagnostic push
      18      115653 : # pragma clang diagnostic ignored "-Wpedantic"
      19      115653 : # pragma clang diagnostic ignored "-Wgnu-label-as-value"
      20      115653 : # endif
      21             : 
      22             :   /* Include the jump table */
      23             : 
      24      115653 : # include "fd_vm_interp_jump_table.c"
      25             : 
      26             :   /* Update the jump table based on SBPF version */
      27             : 
      28      115653 :   ulong sbpf_version = vm->sbpf_version;
      29             : 
      30             :   /* SIMD-0173: LDDW */
      31      115653 :   interp_jump_table[ 0x18 ] = FD_VM_SBPF_ENABLE_LDDW(sbpf_version) ? &&interp_0x18 : &&sigill;
      32      115653 :   interp_jump_table[ 0xf7 ] = FD_VM_SBPF_ENABLE_LDDW(sbpf_version) ? &&sigill : &&interp_0xf7; /* HOR64 */
      33             : 
      34             :   /* SIMD-0173: LE */
      35      115653 :   interp_jump_table[ 0xd4 ] = FD_VM_SBPF_ENABLE_LE  (sbpf_version) ? &&interp_0xd4 : &&sigill;
      36             : 
      37             :   /* SIMD-0173: LDXW, STW, STXW */
      38      115653 :   interp_jump_table[ 0x61 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x8c;
      39      115653 :   interp_jump_table[ 0x62 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x87;
      40      115653 :   interp_jump_table[ 0x63 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x8f;
      41      115653 :   interp_jump_table[ 0x8c ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x8c : &&sigill;
      42      115653 :   interp_jump_table[ 0x87 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x87 : &&interp_0x87depr;
      43      115653 :   interp_jump_table[ 0x8f ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x8f : &&sigill;
      44             : 
      45             :   /* SIMD-0173: LDXH, STH, STXH */
      46      115653 :   interp_jump_table[ 0x69 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x3c;
      47      115653 :   interp_jump_table[ 0x6a ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x37;
      48      115653 :   interp_jump_table[ 0x6b ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x3f;
      49      115653 :   interp_jump_table[ 0x3c ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x3c : &&interp_0x3cdepr;
      50      115653 :   interp_jump_table[ 0x37 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x37 : &&interp_0x37depr;
      51      115653 :   interp_jump_table[ 0x3f ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x3f : &&interp_0x3fdepr;
      52             : 
      53             :   /* SIMD-0173: LDXB, STB, STXB */
      54      115653 :   interp_jump_table[ 0x71 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x2c;
      55      115653 :   interp_jump_table[ 0x72 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x27;
      56      115653 :   interp_jump_table[ 0x73 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x2f;
      57      115653 :   interp_jump_table[ 0x2c ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x2c : &&interp_0x2cdepr;
      58      115653 :   interp_jump_table[ 0x27 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x27 : &&interp_0x27depr;
      59      115653 :   interp_jump_table[ 0x2f ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x2f : &&interp_0x2fdepr;
      60             : 
      61             :   /* SIMD-0173: LDXDW, STDW, STXDW */
      62      115653 :   interp_jump_table[ 0x79 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x9c;
      63      115653 :   interp_jump_table[ 0x7a ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x97;
      64      115653 :   interp_jump_table[ 0x7b ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&sigill : &&interp_0x9f;
      65      115653 :   interp_jump_table[ 0x9c ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x9c : &&interp_0x9cdepr;
      66      115653 :   interp_jump_table[ 0x97 ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x97 : &&interp_0x97depr;
      67      115653 :   interp_jump_table[ 0x9f ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? &&interp_0x9f : &&interp_0x9fdepr;
      68             : 
      69             :   /* SIMD-0173: CALLX */
      70      115653 :   interp_jump_table[ 0x8d ] = FD_VM_SBPF_CALLX_USES_SRC_REG(sbpf_version) ? &&interp_0x8d : &&interp_0x8ddepr;
      71             : 
      72             :   /* SIMD-0174: PQR */
      73      115653 :   interp_jump_table[ 0x36 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x36 : &&sigill;
      74      115653 :   interp_jump_table[ 0x3e ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x3e : &&sigill;
      75             : 
      76      115653 :   interp_jump_table[ 0x46 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x46 : &&sigill;
      77      115653 :   interp_jump_table[ 0x4e ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x4e : &&sigill;
      78      115653 :   interp_jump_table[ 0x56 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x56 : &&sigill;
      79      115653 :   interp_jump_table[ 0x5e ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x5e : &&sigill;
      80      115653 :   interp_jump_table[ 0x66 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x66 : &&sigill;
      81      115653 :   interp_jump_table[ 0x6e ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x6e : &&sigill;
      82      115653 :   interp_jump_table[ 0x76 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x76 : &&sigill;
      83      115653 :   interp_jump_table[ 0x7e ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x7e : &&sigill;
      84             : 
      85      115653 :   interp_jump_table[ 0x86 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x86 : &&sigill;
      86      115653 :   interp_jump_table[ 0x8e ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x8e : &&sigill;
      87      115653 :   interp_jump_table[ 0x96 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x96 : &&sigill;
      88      115653 :   interp_jump_table[ 0x9e ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0x9e : &&sigill;
      89      115653 :   interp_jump_table[ 0xb6 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xb6 : &&sigill;
      90      115653 :   interp_jump_table[ 0xbe ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xbe : &&sigill;
      91             : 
      92      115653 :   interp_jump_table[ 0xc6 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xc6 : &&sigill;
      93      115653 :   interp_jump_table[ 0xce ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xce : &&sigill;
      94      115653 :   interp_jump_table[ 0xd6 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xd6 : &&sigill;
      95      115653 :   interp_jump_table[ 0xde ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xde : &&sigill;
      96      115653 :   interp_jump_table[ 0xe6 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xe6 : &&sigill;
      97      115653 :   interp_jump_table[ 0xee ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xee : &&sigill;
      98      115653 :   interp_jump_table[ 0xf6 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xf6 : &&sigill;
      99      115653 :   interp_jump_table[ 0xfe ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&interp_0xfe : &&sigill;
     100             : 
     101             :   /* SIMD-0174: disable MUL, DIV, MOD */
     102      115653 :   interp_jump_table[ 0x24 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&sigill : &&interp_0x24;
     103      115653 :   interp_jump_table[ 0x34 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&sigill : &&interp_0x34;
     104      115653 :   interp_jump_table[ 0x94 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? &&sigill : &&interp_0x94;
     105             : 
     106             :   /* SIMD-0174: NEG */
     107      115653 :   interp_jump_table[ 0x84 ] = FD_VM_SBPF_ENABLE_NEG (sbpf_version) ? &&interp_0x84 : &&sigill;
     108             :   /* note: 0x87 should not be overwritten because it was NEG64 and it becomes STW */
     109             : 
     110             :   /* SIMD-0174: Explicit Sign Extension + Register Immediate Subtraction. 
     111             :      Note: 0x14 is affected by both. */
     112      115653 :   interp_jump_table[ 0x04 ] = FD_VM_SBPF_EXPLICIT_SIGN_EXT        (sbpf_version) ? &&interp_0x04 : &&interp_0x04depr;
     113      115653 :   interp_jump_table[ 0x0c ] = FD_VM_SBPF_EXPLICIT_SIGN_EXT        (sbpf_version) ? &&interp_0x0c : &&interp_0x0cdepr;
     114      115653 :   interp_jump_table[ 0x1c ] = FD_VM_SBPF_EXPLICIT_SIGN_EXT        (sbpf_version) ? &&interp_0x1c : &&interp_0x1cdepr;
     115      115653 :   interp_jump_table[ 0xbc ] = FD_VM_SBPF_EXPLICIT_SIGN_EXT        (sbpf_version) ? &&interp_0xbc : &&interp_0xbcdepr;
     116      115653 :   interp_jump_table[ 0x14 ] = FD_VM_SBPF_SWAP_SUB_REG_IMM_OPERANDS(sbpf_version) ? &&interp_0x14 : &&interp_0x14depr;
     117      115653 :   interp_jump_table[ 0x17 ] = FD_VM_SBPF_SWAP_SUB_REG_IMM_OPERANDS(sbpf_version) ? &&interp_0x17 : &&interp_0x17depr;
     118             : 
     119             :   /* SIMD-0178: static syscalls */
     120      115653 :   interp_jump_table[ 0x85 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? &&interp_0x85 : &&interp_0x85depr;
     121      115653 :   interp_jump_table[ 0x95 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? &&interp_0x95 : &&interp_0x9d;
     122      115653 :   interp_jump_table[ 0x9d ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? &&interp_0x9d : &&sigill;
     123             : 
     124             :   /* Unpack the VM state */
     125             : 
     126      115653 :   ulong pc        = vm->pc;
     127      115653 :   ulong ic        = vm->ic;
     128      115653 :   ulong cu        = vm->cu;
     129      115653 :   ulong frame_cnt = vm->frame_cnt;
     130             : 
     131             :   /* FD_VM_INTERP_INSTR_EXEC loads the first word of the instruction at
     132             :      pc, parses it, fetches the associated register values and then
     133             :      jumps to the code that executes the instruction.  On normal
     134             :      instruction execution, the pc will be updated and
     135             :      FD_VM_INTERP_INSTR_EXEC will be invoked again to do the next
     136             :      instruction.  After a normal halt, this will branch to interp_halt.
     137             :      Otherwise, it will branch to the appropriate normal termination. */
     138             : 
     139      115653 :   ulong instr;
     140      115653 :   ulong opcode;
     141      115653 :   ulong dst;
     142      115653 :   ulong src;
     143      115653 :   ulong offset; /* offset is 16-bit but always sign extended, so we handle cast once */
     144      115653 :   uint  imm;
     145      115653 :   ulong reg_dst;
     146      115653 :   ulong reg_src;
     147             : 
     148             : /* These mimic the exact Rust semantics for wrapping_shl and wrapping_shr. */
     149             : 
     150             : /* u64::wrapping_shl: a.unchecked_shl(b & (64 - 1))
     151             :    
     152             :    https://doc.rust-lang.org/std/primitive.u64.html#method.wrapping_shl
     153             :  */
     154      143331 : #define FD_RUST_ULONG_WRAPPING_SHL( a, b ) (a << ( b & ( 63 ) ))
     155             : 
     156             : /* u64::wrapping_shr: a.unchecked_shr(b & (64 - 1))
     157             :    
     158             :    https://doc.rust-lang.org/std/primitive.u64.html#method.wrapping_shr
     159             :  */
     160      206673 : #define FD_RUST_ULONG_WRAPPING_SHR( a, b ) (a >> ( b & ( 63 ) ))
     161             : 
     162             : /* u32::wrapping_shl: a.unchecked_shl(b & (32 - 1))
     163             :    
     164             :    https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_shl
     165             :  */
     166      115653 : #define FD_RUST_UINT_WRAPPING_SHL( a, b ) (a << ( b & ( 31 ) ))
     167             : 
     168             : /* u32::wrapping_shr: a.unchecked_shr(b & (32 - 1))
     169             :    
     170             :    https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_shr
     171             :  */
     172      115653 : #define FD_RUST_UINT_WRAPPING_SHR( a, b ) (a >> ( b & ( 31 ) ))
     173             : 
     174             : 
     175      115653 : # define FD_VM_INTERP_INSTR_EXEC                                                                 \
     176     6036312 :   if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigtext; /* Note: untaken branches don't consume BTB */ \
     177     6036312 :   instr   = text[ pc ];                  /* Guaranteed in-bounds */                              \
     178     6034650 :   opcode  = fd_vm_instr_opcode( instr ); /* in [0,256) even if malformed */                      \
     179     6034650 :   dst     = fd_vm_instr_dst   ( instr ); /* in [0, 16) even if malformed */                      \
     180     6034650 :   src     = fd_vm_instr_src   ( instr ); /* in [0, 16) even if malformed */                      \
     181     6034650 :   offset  = fd_vm_instr_offset( instr ); /* in [-2^15,2^15) even if malformed */                 \
     182     6034650 :   imm     = fd_vm_instr_imm   ( instr ); /* in [0,2^32) even if malformed */                     \
     183     6034650 :   reg_dst = reg[ dst ];                  /* Guaranteed in-bounds */                              \
     184     6034650 :   reg_src = reg[ src ];                  /* Guaranteed in-bounds */                              \
     185     6034650 :   goto *interp_jump_table[ opcode ]      /* Guaranteed in-bounds */
     186             : 
     187             : /* FD_VM_INTERP_SYSCALL_EXEC
     188             :    (macro to handle the logic of 0x85 pre- and post- SIMD-0178: static syscalls)
     189             : 
     190             :    Setup.
     191             :    Update the vm with the current vm execution state for the
     192             :    syscall.  Note that BRANCH_BEGIN has pc at the syscall and
     193             :    already updated ic and cu to reflect all instructions up to
     194             :    and including the syscall instruction itself.
     195             : 
     196             :    Execution.
     197             :    Do the syscall.  We use ret reduce the risk of the syscall
     198             :    accidentally modifying other registers (note however since a
     199             :    syscall has the vm handle it still do arbitrary modifications
     200             :    to the vm state) and the risk of a pointer escape on reg from
     201             :    inhibiting compiler optimizations (this risk is likely low in
     202             :    as this is the only point in the whole interpreter core that
     203             :    calls outside this translation unit).
     204             :    At this point, vm->cu is positive.
     205             : 
     206             :    Error handling.
     207             :    If we trust syscall implementations to handle the vm state
     208             :    correctly, the below could be implemented as unpacking the vm
     209             :    state and jumping to sigsys on error.  But we provide some
     210             :    extra protection to make various strong guarantees:
     211             : 
     212             :    - We do not let the syscall modify pc currently as nothing
     213             :      requires this and it reduces risk of a syscall bug mucking
     214             :      up the interpreter.  If there ever was a syscall that
     215             :      needed to modify the pc (e.g. a syscall that has execution
     216             :      resume from a different location than the instruction
     217             :      following the syscall), do "pc = vm->pc" below.
     218             : 
     219             :    - We do not let the syscall modify ic currently as nothing
     220             :      requires this and it keeps the ic precise.  If a future
     221             :      syscall needs this, do "ic = vm->ic" below.
     222             : 
     223             :    - We do not let the syscall increase cu as nothing requires
     224             :      this and it guarantees the interpreter will halt in a
     225             :      reasonable finite amount of time.  If a future syscall
     226             :      needs this, do "cu = vm->cu" below.
     227             : 
     228             :    - A syscall that returns SIGCOST is always treated as though
     229             :      it also zerod cu.
     230             : 
     231             :    At this point, vm->cu is whatever the syscall tried to set
     232             :    and cu is positive.
     233             : 
     234             :    Exit
     235             :    At this point, cu is positive and err is clear.
     236             : */
     237      115653 : # define FD_VM_INTERP_SYSCALL_EXEC                                          \
     238             :   /* Setup */                                                               \
     239      115653 :   vm->pc        = pc;                                                       \
     240        7809 :   vm->ic        = ic;                                                       \
     241        7809 :   vm->cu        = cu;                                                       \
     242        7809 :   vm->frame_cnt = frame_cnt;                                                \
     243             :   /* Execution */                                                           \
     244        7809 :   ulong ret[1];                                                             \
     245        7809 :   err = syscall->func( vm, reg[1], reg[2], reg[3], reg[4], reg[5], ret );   \
     246        7809 :   reg[0] = ret[0];                                                          \
     247             :   /* Error handling */                                                      \
     248        7809 :   ulong cu_req = vm->cu;                                                    \
     249        7809 :   cu = fd_ulong_min( cu_req, cu );                                          \
     250        7809 :   if( FD_UNLIKELY( err ) ) {                                                \
     251         108 :     if( err==FD_VM_ERR_SIGCOST ) cu = 0UL; /* cmov */                       \
     252         108 :     goto sigsyscall;                                                        \
     253         108 :   }                                                                         \
     254             :   /* Exit */
     255             : 
     256             : 
     257             :   /* FD_VM_INTERP_INSTR_BEGIN / FD_VM_INTERP_INSTR_END bracket opcode's
     258             :      implementation for an opcode that does not branch.  On entry, the
     259             :      instruction word has been unpacked into dst / src / offset / imm
     260             :      and reg[dst] / reg[src] has been prefetched into reg_dst / reg_src. */
     261             : 
     262     4846299 : # define FD_VM_INTERP_INSTR_BEGIN(opcode) interp_##opcode:
     263             : 
     264             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~0.3% faster in some benchmarks, slower in others but more code footprint */
     265     4823697 : # define FD_VM_INTERP_INSTR_END pc++; FD_VM_INTERP_INSTR_EXEC
     266             : # else /* Use this version when tracing or optimizing code footprint */
     267           0 : # define FD_VM_INTERP_INSTR_END pc++; goto interp_exec
     268             : # endif
     269             : 
     270             :   /* Instead of doing a lot of compute budget calcs and tests every
     271             :      instruction, we note that the program counter increases
     272             :      monotonically after a branch (or a program start) until the next
     273             :      branch (or program termination).  We save the program counter of
     274             :      the start of such a segment in pc0.  Whenever we encounter a branch
     275             :      (or a program termination) at pc, we know we processed pc-pc0+1
     276             :      text words (including the text word for the branch instruction
     277             :      itself as all branch instructions are single word).
     278             : 
     279             :      Each instruction costs 1 cu (syscalls can cost extra on top of
     280             :      this that is accounted separately in CALL_IMM below).  Since there
     281             :      could have been multiword instructions in this segment, at start of
     282             :      such a segment, we zero out the accumulator ic_correction and have
     283             :      every multiword instruction in the segment accumulate the number of
     284             :      extra text words it has to this variable.  (Sigh ... it would be a
     285             :      lot simpler to bill based on text words processed but this would be
     286             :      very difficult to make this protocol change at this point.)
     287             : 
     288             :      When we encounter a branch at pc, the number of instructions
     289             :      processed (and thus the number of compute units to bill for that
     290             :      segment) is thus:
     291             : 
     292             :        pc - pc0 + 1 - ic_correction
     293             : 
     294             :      IMPORTANT SAFETY TIP!  This implies the worst case interval before
     295             :      checking the cu budget is the worst case text_cnt.  But since all
     296             :      such instructions are cheap 1 cu instructions and processed fast
     297             :      and text max is limited in size, this should be acceptable in
     298             :      practice.  FIXME: DOUBLE CHECK THE MATH ABOVE AGAINST PROTOCOL
     299             :      LIMITS. */
     300             : 
     301      115653 :   ulong pc0           = pc;
     302      115653 :   ulong ic_correction = 0UL;
     303             : 
     304      115653 : # define FD_VM_INTERP_BRANCH_BEGIN(opcode)                                                              \
     305     1187172 :   interp_##opcode:                                                                                      \
     306             :     /* Bill linear text segment and this branch instruction as per the above */                         \
     307     1187172 :     ic_correction = pc - pc0 + 1UL - ic_correction;                                                     \
     308     1187172 :     ic += ic_correction;                                                                                \
     309     1187172 :     if( FD_UNLIKELY( ic_correction>cu ) ) goto sigcost; /* Note: untaken branches don't consume BTB */  \
     310     1187172 :     cu -= ic_correction;                                                                                \
     311             :     /* At this point, cu>=0 */                                                                          \
     312     1186488 :     ic_correction = 0UL;
     313             : 
     314             :   /* FIXME: debatable if it is better to do pc++ here or have the
     315             :      instruction implementations do it in their code path. */
     316             : 
     317             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~4% faster in some benchmarks, slower in others but more code footprint */
     318             : # define FD_VM_INTERP_BRANCH_END               \
     319     1095399 :     pc++;                                      \
     320     1095399 :     pc0 = pc; /* Start a new linear segment */ \
     321     1096962 :     FD_VM_INTERP_INSTR_EXEC
     322             : # else /* Use this version when tracing or optimizing code footprint */
     323             : # define FD_VM_INTERP_BRANCH_END               \
     324           0 :     pc++;                                      \
     325           0 :     pc0 = pc; /* Start a new linear segment */ \
     326             :     /* FIXME: TEST sigsplit HERE */            \
     327           0 :     goto interp_exec
     328             : # endif
     329             : 
     330             :   /* FD_VM_INTERP_STACK_PUSH pushes reg[6:9] onto the shadow stack and
     331             :      advances reg[10] to a new user stack frame.  If there are no more
     332             :      stack frames available, will do a SIGSTACK. */
     333             : 
     334             :   /* FIXME: double check faulting is desired on stack overflow. */
     335             : 
     336             :   /* FIXME: a pre-belt-sanding FIXME implied the TLB should be updated
     337             :      to prevent byte code from accessing the stack outside its current
     338             :      stack frame.  But this would break the common practice of a
     339             :      function passing a pointer to something on its stack into a
     340             :      function that it calls:
     341             : 
     342             :        void foo( ... ) {
     343             :          ...
     344             :          int ret;
     345             :          bar( &ret );
     346             :          ...
     347             :        }
     348             : 
     349             :      So this probably shouldn't be done.  But, if it is in fact
     350             :      necessary, the TLB updates would be here and in pop. */
     351             : 
     352             :   /* FIXME: unvalidated code mucking with r10 */
     353             : 
     354      115653 : # define FD_VM_INTERP_STACK_PUSH                                                                            \
     355      135288 :   shadow[ frame_cnt ].r6 = reg[6];                                                                          \
     356      135288 :   shadow[ frame_cnt ].r7 = reg[7];                                                                          \
     357      135288 :   shadow[ frame_cnt ].r8 = reg[8];                                                                          \
     358      135288 :   shadow[ frame_cnt ].r9 = reg[9];                                                                          \
     359      135288 :   shadow[ frame_cnt ].r10= reg[10];                                                                         \
     360      135288 :   shadow[ frame_cnt ].pc = pc;                                                                              \
     361      135288 :   if( FD_UNLIKELY( ++frame_cnt>=frame_max ) ) goto sigstack; /* Note: untaken branches don't consume BTB */ \
     362      135288 :   if( !FD_VM_SBPF_DYNAMIC_STACK_FRAMES( sbpf_version ) ) reg[10] += vm->stack_frame_size;
     363             : 
     364             :   /* We subtract the heap cost in the BPF loader */
     365             : 
     366      115653 :   goto interp_exec; /* Silly but to avoid unused label warning in some configurations */
     367      115653 : interp_exec:
     368             : 
     369             : # ifdef FD_VM_INTERP_EXE_TRACING_ENABLED
     370             :   /* Note: when tracing or optimizing for code footprint, all
     371             :      instruction execution starts here such that this is only point
     372             :      where exe tracing diagnostics are needed. */
     373           3 :   if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigtext;
     374           3 :   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 );
     375           3 : # endif
     376             : 
     377      115653 :   FD_VM_INTERP_INSTR_EXEC;
     378             : 
     379             :   /* 0x00 - 0x0f ******************************************************/
     380             : 
     381             :   FD_VM_INTERP_INSTR_BEGIN(0x04) /* FD_SBPF_OP_ADD_IMM */
     382         396 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst + (int)imm );
     383         396 :   FD_VM_INTERP_INSTR_END;
     384             : 
     385             :   FD_VM_INTERP_INSTR_BEGIN(0x04depr) /* FD_SBPF_OP_ADD_IMM deprecated SIMD-0174 */
     386         768 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)imm );
     387         768 :   FD_VM_INTERP_INSTR_END;
     388             : 
     389             :   FD_VM_INTERP_BRANCH_BEGIN(0x05) /* FD_SBPF_OP_JA */
     390       75612 :     pc += offset;
     391       75612 :   FD_VM_INTERP_BRANCH_END;
     392             : 
     393             :   FD_VM_INTERP_INSTR_BEGIN(0x07) /* FD_SBPF_OP_ADD64_IMM */
     394      505638 :     reg[ dst ] = reg_dst + (ulong)(long)(int)imm;
     395      505638 :   FD_VM_INTERP_INSTR_END;
     396             : 
     397             :   FD_VM_INTERP_INSTR_BEGIN(0x0c) /* FD_SBPF_OP_ADD_REG */
     398         393 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst + (int)reg_src );
     399         393 :   FD_VM_INTERP_INSTR_END;
     400             : 
     401             :   FD_VM_INTERP_INSTR_BEGIN(0x0cdepr) /* FD_SBPF_OP_ADD_REG deprecated SIMD-0174 */
     402         762 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)reg_src );
     403         762 :   FD_VM_INTERP_INSTR_END;
     404             : 
     405             :   FD_VM_INTERP_INSTR_BEGIN(0x0f) /* FD_SBPF_OP_ADD64_REG */
     406      427245 :     reg[ dst ] = reg_dst + reg_src;
     407      427245 :   FD_VM_INTERP_INSTR_END;
     408             : 
     409             :   /* 0x10 - 0x1f ******************************************************/
     410             : 
     411             :   FD_VM_INTERP_INSTR_BEGIN(0x14) /* FD_SBPF_OP_SUB_IMM */
     412         396 :     reg[ dst ] = (ulong)(uint)( (int)imm - (int)reg_dst );
     413         396 :   FD_VM_INTERP_INSTR_END;
     414             : 
     415             :   FD_VM_INTERP_INSTR_BEGIN(0x14depr) /* FD_SBPF_OP_SUB_IMM deprecated SIMD-0174 */
     416         759 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)imm );
     417         759 :   FD_VM_INTERP_INSTR_END;
     418             : 
     419             :   FD_VM_INTERP_BRANCH_BEGIN(0x15) /* FD_SBPF_OP_JEQ_IMM */
     420       95742 :     pc += fd_ulong_if( reg_dst==(ulong)(long)(int)imm, offset, 0UL );
     421       95742 :   FD_VM_INTERP_BRANCH_END;
     422             : 
     423             :   FD_VM_INTERP_INSTR_BEGIN(0x17) /* FD_SBPF_OP_SUB64_IMM */
     424         393 :     reg[ dst ] = (ulong)(long)(int)imm - reg_dst;
     425         393 :   FD_VM_INTERP_INSTR_END;
     426             : 
     427             :   FD_VM_INTERP_INSTR_BEGIN(0x17depr) /* FD_SBPF_OP_SUB64_IMM deprecated SIMD-0174 */
     428         765 :     reg[ dst ] = reg_dst - (ulong)(long)(int)imm;
     429         765 :   FD_VM_INTERP_INSTR_END;
     430             : 
     431             :   FD_VM_INTERP_INSTR_BEGIN(0x18) /* FD_SBPF_OP_LDQ */
     432       84123 :     pc++;
     433       84123 :     ic_correction++;
     434             :     /* No need to check pc because it's already checked during validation.
     435             :        if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigsplit; // Note: untaken branches don't consume BTB */
     436       84123 :     reg[ dst ] = (ulong)((ulong)imm | ((ulong)fd_vm_instr_imm( text[ pc ] ) << 32));
     437       84123 :   FD_VM_INTERP_INSTR_END;
     438             : 
     439             :   FD_VM_INTERP_INSTR_BEGIN(0x1c) /* FD_SBPF_OP_SUB_REG */
     440         396 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst - (int)reg_src );
     441         396 :   FD_VM_INTERP_INSTR_END;
     442             : 
     443             :   FD_VM_INTERP_INSTR_BEGIN(0x1cdepr) /* FD_SBPF_OP_SUB_REG deprecated SIMD-0174 */
     444         759 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)reg_src );
     445         759 :   FD_VM_INTERP_INSTR_END;
     446             : 
     447             :   FD_VM_INTERP_BRANCH_BEGIN(0x1d) /* FD_SBPF_OP_JEQ_REG */
     448       23646 :     pc += fd_ulong_if( reg_dst==reg_src, offset, 0UL );
     449       23646 :   FD_VM_INTERP_BRANCH_END;
     450             : 
     451             :   FD_VM_INTERP_INSTR_BEGIN(0x1f) /* FD_SBPF_OP_SUB64_REG */
     452       98166 :     reg[ dst ] = reg_dst - reg_src;
     453       98166 :   FD_VM_INTERP_INSTR_END;
     454             : 
     455             :   /* 0x20 - 0x2f ******************************************************/
     456             : 
     457             :   FD_VM_INTERP_INSTR_BEGIN(0x24) /* FD_SBPF_OP_MUL_IMM */
     458         762 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)imm );
     459         762 :   FD_VM_INTERP_INSTR_END;
     460             : 
     461             :   FD_VM_INTERP_BRANCH_BEGIN(0x25) /* FD_SBPF_OP_JGT_IMM */
     462       41019 :     pc += fd_ulong_if( reg_dst>(ulong)(long)(int)imm, offset, 0UL );
     463       41019 :   FD_VM_INTERP_BRANCH_END;
     464             : 
     465        2094 :   FD_VM_INTERP_INSTR_BEGIN(0x27) { /* FD_SBPF_OP_STB */
     466        2094 :     uchar is_multi_region = 0;
     467        2094 :     ulong vaddr           = reg_dst + offset;
     468        2094 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     469        2094 :     if( FD_UNLIKELY( !haddr ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */
     470         159 :     fd_vm_mem_st_1( haddr, (uchar)imm );
     471         159 :   }
     472         159 :   FD_VM_INTERP_INSTR_END;
     473             : 
     474       98685 :   FD_VM_INTERP_INSTR_BEGIN(0x2c) { /* FD_SBPF_OP_LDXB */
     475       98685 :     uchar is_multi_region = 0;
     476       98685 :     ulong vaddr           = reg_src + offset;
     477       98685 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     478       98685 :     if( FD_UNLIKELY( !haddr ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */
     479       97305 :     reg[ dst ] = fd_vm_mem_ld_1( haddr );
     480       97305 :   }
     481       97305 :   FD_VM_INTERP_INSTR_END;
     482             : 
     483             :   FD_VM_INTERP_BRANCH_BEGIN(0x2d) /* FD_SBPF_OP_JGT_REG */
     484      218895 :     pc += fd_ulong_if( reg_dst>reg_src, offset, 0UL );
     485      218895 :   FD_VM_INTERP_BRANCH_END;
     486             : 
     487       67302 :   FD_VM_INTERP_INSTR_BEGIN(0x2f) { /* FD_SBPF_OP_STXB */
     488       67302 :     uchar is_multi_region = 0;
     489       67302 :     ulong vaddr           = reg_dst + offset;
     490       67302 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     491       67302 :     if( FD_UNLIKELY( !haddr ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigrdonly */
     492       65364 :     fd_vm_mem_st_1( haddr, (uchar)reg_src );
     493       65364 :   }
     494       65364 :   FD_VM_INTERP_INSTR_END;
     495             : 
     496             :   FD_VM_INTERP_INSTR_BEGIN(0x27depr) /* FD_SBPF_OP_MUL64_IMM */
     497       47202 :     reg[ dst ] = (ulong)( (long)reg_dst * (long)(int)imm );
     498       47202 :   FD_VM_INTERP_INSTR_END;
     499             : 
     500             :   FD_VM_INTERP_INSTR_BEGIN(0x2cdepr) /* FD_SBPF_OP_MUL_REG */
     501         771 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)reg_src );
     502         771 :   FD_VM_INTERP_INSTR_END;
     503             : 
     504             :   FD_VM_INTERP_INSTR_BEGIN(0x2fdepr) /* FD_SBPF_OP_MUL64_REG */
     505      104412 :     reg[ dst ] = reg_dst * reg_src;
     506      104412 :   FD_VM_INTERP_INSTR_END;
     507             : 
     508             :   /* 0x30 - 0x3f ******************************************************/
     509             : 
     510             :   FD_VM_INTERP_INSTR_BEGIN(0x34) /* FD_SBPF_OP_DIV_IMM */
     511         702 :     /* FIXME: convert to a multiply at validation time (usually probably
     512         702 :        not worth it) */
     513         702 :     reg[ dst ] = (ulong)((uint)reg_dst / imm);
     514         702 :   FD_VM_INTERP_INSTR_END;
     515             : 
     516             :   FD_VM_INTERP_BRANCH_BEGIN(0x35) /* FD_SBPF_OP_JGE_IMM */
     517        7143 :     pc += fd_ulong_if( reg_dst>=(ulong)(long)(int)imm, offset, 0UL );
     518        7143 :   FD_VM_INTERP_BRANCH_END;
     519             : 
     520             :   FD_VM_INTERP_INSTR_BEGIN(0x36) /* FD_SBPF_OP_UHMUL64_IMM */
     521         363 :     reg[ dst ] = (ulong)(( (uint128)reg_dst * (uint128)(ulong)imm ) >> 64 );
     522         363 :   FD_VM_INTERP_INSTR_END;
     523             : 
     524        2091 :   FD_VM_INTERP_INSTR_BEGIN(0x37) { /* FD_SBPF_OP_STH */
     525        2091 :     uchar is_multi_region = 0;
     526        2091 :     ulong vaddr           = reg_dst + offset;
     527        2091 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     528        2091 :     int   sigsegv         = !haddr;
     529        2091 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     530         159 :     fd_vm_mem_st_2( vm, vaddr, haddr, (ushort)imm, is_multi_region );
     531         159 :   }
     532         159 :   FD_VM_INTERP_INSTR_END;
     533             : 
     534        3555 :   FD_VM_INTERP_INSTR_BEGIN(0x3c) { /* FD_SBPF_OP_LDXH */
     535        3555 :     uchar is_multi_region = 0;
     536        3555 :     ulong vaddr           = reg_src + offset;
     537        3555 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     538        3555 :     int   sigsegv         = !haddr;
     539        3555 :     if( FD_UNLIKELY( sigsegv ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     540        2172 :     reg[ dst ] = fd_vm_mem_ld_2( vm, vaddr, haddr, is_multi_region );
     541        2172 :   }
     542        2172 :   FD_VM_INTERP_INSTR_END;
     543             : 
     544             :   FD_VM_INTERP_BRANCH_BEGIN(0x3d) /* FD_SBPF_OP_JGE_REG */
     545       46419 :     pc += fd_ulong_if( reg_dst>=reg_src, offset, 0UL );
     546       46419 :   FD_VM_INTERP_BRANCH_END;
     547             : 
     548        4122 :   FD_VM_INTERP_INSTR_BEGIN(0x3f) { /* FD_SBPF_OP_STXH */
     549        4122 :     uchar is_multi_region = 0;
     550        4122 :     ulong vaddr           = reg_dst + offset;
     551        4122 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     552        4122 :     int   sigsegv         = !haddr;
     553        4122 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus/rdonly */
     554        2187 :     fd_vm_mem_st_2( vm, vaddr, haddr, (ushort)reg_src, is_multi_region );
     555        2187 :   }
     556        2187 :   FD_VM_INTERP_INSTR_END;
     557             : 
     558             :   FD_VM_INTERP_INSTR_BEGIN(0x3e) /* FD_SBPF_OP_UHMUL64_REG */
     559         363 :     reg[ dst ] = (ulong)(( (uint128)reg_dst * (uint128)reg_src ) >> 64 );
     560         363 :   FD_VM_INTERP_INSTR_END;
     561             : 
     562             :   FD_VM_INTERP_INSTR_BEGIN(0x37depr) /* FD_SBPF_OP_DIV64_IMM */
     563       10920 :     reg[ dst ] = reg_dst / (ulong)(long)(int)imm;
     564       10920 :   FD_VM_INTERP_INSTR_END;
     565             : 
     566             :   FD_VM_INTERP_INSTR_BEGIN(0x3cdepr) /* FD_SBPF_OP_DIV_REG */
     567         777 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     568         618 :     reg[ dst ] = (ulong)((uint)reg_dst / (uint)reg_src);
     569         618 :   FD_VM_INTERP_INSTR_END;
     570             : 
     571             :   FD_VM_INTERP_INSTR_BEGIN(0x3fdepr) /* FD_SBPF_OP_DIV64_REG */
     572       32430 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     573       32274 :     reg[ dst ] = reg_dst / reg_src;
     574       32274 :   FD_VM_INTERP_INSTR_END;
     575             : 
     576             :   /* 0x40 - 0x4f ******************************************************/
     577             : 
     578             :   FD_VM_INTERP_INSTR_BEGIN(0x44) /* FD_SBPF_OP_OR_IMM */
     579        1131 :     reg[ dst ] = (ulong)( (uint)reg_dst | imm );
     580        1131 :   FD_VM_INTERP_INSTR_END;
     581             : 
     582             :   FD_VM_INTERP_BRANCH_BEGIN(0x45) /* FD_SBPF_OP_JSET_IMM */
     583        2340 :     pc += fd_ulong_if( !!(reg_dst & (ulong)(long)(int)imm), offset, 0UL );
     584        2340 :   FD_VM_INTERP_BRANCH_END;
     585             : 
     586             :   FD_VM_INTERP_INSTR_BEGIN(0x46) /* FD_SBPF_OP_UDIV32_IMM */
     587         369 :     reg[ dst ] = (ulong)( (uint)reg_dst / (uint)imm );
     588         369 :   FD_VM_INTERP_INSTR_END;
     589             : 
     590             :   FD_VM_INTERP_INSTR_BEGIN(0x47) /* FD_SBPF_OP_OR64_IMM */
     591        1200 :     reg[ dst ] = reg_dst | (ulong)(long)(int)imm;
     592        1200 :   FD_VM_INTERP_INSTR_END;
     593             : 
     594             :   FD_VM_INTERP_INSTR_BEGIN(0x4c) /* FD_SBPF_OP_OR_REG */
     595        1137 :     reg[ dst ] = (ulong)(uint)( reg_dst | reg_src );
     596        1137 :   FD_VM_INTERP_INSTR_END;
     597             : 
     598             :   FD_VM_INTERP_BRANCH_BEGIN(0x4d) /* FD_SBPF_OP_JSET_REG */
     599        1740 :     pc += fd_ulong_if( !!(reg_dst & reg_src), offset, 0UL );
     600        1740 :   FD_VM_INTERP_BRANCH_END;
     601             : 
     602             :   FD_VM_INTERP_INSTR_BEGIN(0x4e) /* FD_SBPF_OP_UDIV32_REG */
     603         408 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     604         324 :     reg[ dst ] = (ulong)( (uint)reg_dst / (uint)reg_src );
     605         324 :   FD_VM_INTERP_INSTR_END;
     606             : 
     607             :   FD_VM_INTERP_INSTR_BEGIN(0x4f) /* FD_SBPF_OP_OR64_REG */
     608       63276 :     reg[ dst ] = reg_dst | reg_src;
     609       63276 :   FD_VM_INTERP_INSTR_END;
     610             : 
     611             :   /* 0x50 - 0x5f ******************************************************/
     612             : 
     613             :   FD_VM_INTERP_INSTR_BEGIN(0x54) /* FD_SBPF_OP_AND_IMM */
     614        1134 :     reg[ dst ] = (ulong)( (uint)reg_dst & imm );
     615        1134 :   FD_VM_INTERP_INSTR_END;
     616             : 
     617             :   FD_VM_INTERP_BRANCH_BEGIN(0x55) /* FD_SBPF_OP_JNE_IMM */
     618      162303 :     pc += fd_ulong_if( reg_dst!=(ulong)(long)(int)imm, offset, 0UL );
     619      162303 :   FD_VM_INTERP_BRANCH_END;
     620             : 
     621             :   FD_VM_INTERP_INSTR_BEGIN(0x56) /* FD_SBPF_OP_UDIV64_IMM */
     622         369 :     reg[ dst ] = reg_dst / (ulong)imm;
     623         369 :   FD_VM_INTERP_INSTR_END;
     624             : 
     625             :   FD_VM_INTERP_INSTR_BEGIN(0x57) /* FD_SBPF_OP_AND64_IMM */
     626       33675 :     reg[ dst ] = reg_dst & (ulong)(long)(int)imm;
     627       33675 :   FD_VM_INTERP_INSTR_END;
     628             : 
     629             :   FD_VM_INTERP_INSTR_BEGIN(0x5c) /* FD_SBPF_OP_AND_REG */
     630        1146 :     reg[ dst ] = (ulong)(uint)( reg_dst & reg_src );
     631        1146 :   FD_VM_INTERP_INSTR_END;
     632             : 
     633             :   FD_VM_INTERP_BRANCH_BEGIN(0x5d) /* FD_SBPF_OP_JNE_REG */
     634       29958 :     pc += fd_ulong_if( reg_dst!=reg_src, offset, 0UL );
     635       29958 :   FD_VM_INTERP_BRANCH_END;
     636             : 
     637             :   FD_VM_INTERP_INSTR_BEGIN(0x5e) /* FD_SBPF_OP_UDIV64_REG */
     638         405 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     639         324 :     reg[ dst ] = reg_dst / reg_src;
     640         324 :   FD_VM_INTERP_INSTR_END;
     641             : 
     642             :   FD_VM_INTERP_INSTR_BEGIN(0x5f) /* FD_SBPF_OP_AND64_REG */
     643       48489 :     reg[ dst ] = reg_dst & reg_src;
     644       48489 :   FD_VM_INTERP_INSTR_END;
     645             : 
     646             :   /* 0x60 - 0x6f ******************************************************/
     647             : 
     648             :   /* FIXME: CHECK THE CU COST MODEL FOR THESE (IS IT LIKE
     649             :      FD_VM_CONSUME_MEM AND NOT JUST FIXED) */
     650             :   /* FIXME: MEM TRACING DIAGNOSTICS GO IN HERE */
     651             : 
     652             :   FD_VM_INTERP_INSTR_BEGIN(0x64) /* FD_SBPF_OP_LSH_IMM */
     653         813 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L291 */
     654         813 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, (uint)imm ) );
     655         813 :   FD_VM_INTERP_INSTR_END;
     656             : 
     657             :   FD_VM_INTERP_BRANCH_BEGIN(0x65) /* FD_SBPF_OP_JSGT_IMM */
     658       14685 :     pc += fd_ulong_if( (long)reg_dst>(long)(int)imm, offset, 0UL );
     659       14685 :   FD_VM_INTERP_BRANCH_END;
     660             : 
     661             :   FD_VM_INTERP_INSTR_BEGIN(0x66) /* FD_SBPF_OP_UREM32_IMM */
     662         369 :     reg[ dst ] = (ulong)( (uint)reg_dst % (uint)imm );
     663         369 :   FD_VM_INTERP_INSTR_END;
     664             : 
     665             :   FD_VM_INTERP_INSTR_BEGIN(0x67) /* FD_SBPF_OP_LSH64_IMM */
     666      137535 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L376 */
     667      137535 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, imm );
     668      137535 :   FD_VM_INTERP_INSTR_END;
     669             : 
     670             :   FD_VM_INTERP_INSTR_BEGIN(0x6c) /* FD_SBPF_OP_LSH_REG */
     671        1530 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L292 */
     672        1530 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, reg_src ) );
     673        1530 :   FD_VM_INTERP_INSTR_END;
     674             : 
     675             :   FD_VM_INTERP_BRANCH_BEGIN(0x6d) /* FD_SBPF_OP_JSGT_REG */
     676       28251 :     pc += fd_ulong_if( (long)reg_dst>(long)reg_src, offset, 0UL );
     677       28251 :   FD_VM_INTERP_BRANCH_END;
     678             : 
     679             :   FD_VM_INTERP_INSTR_BEGIN(0x6e) /* FD_SBPF_OP_UREM32_REG */
     680         408 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     681         324 :     reg[ dst ] = (ulong)( (uint)reg_dst % (uint)reg_src );
     682         324 :   FD_VM_INTERP_INSTR_END;
     683             : 
     684             :   FD_VM_INTERP_INSTR_BEGIN(0x6f) /* FD_SBPF_OP_LSH64_REG */
     685        5796 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L377 */
     686        5796 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, reg_src );
     687        5796 :   FD_VM_INTERP_INSTR_END;
     688             : 
     689             :   /* 0x70 - 0x7f ******************************************************/
     690             : 
     691             :   FD_VM_INTERP_INSTR_BEGIN(0x74) /* FD_SBPF_OP_RSH_IMM */
     692         369 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L293 */
     693         369 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, imm ) );
     694         369 :   FD_VM_INTERP_INSTR_END;
     695             : 
     696             :   FD_VM_INTERP_BRANCH_BEGIN(0x75) /* FD_SBPF_OP_JSGE_IMM */
     697        7731 :     pc += fd_ulong_if( (long)reg_dst>=(long)(int)imm, offset, 0UL );
     698        7731 :   FD_VM_INTERP_BRANCH_END;
     699             : 
     700             :   FD_VM_INTERP_INSTR_BEGIN(0x76) /* FD_SBPF_OP_UREM64_IMM */
     701         369 :     reg[ dst ] = reg_dst % (ulong)imm;
     702         369 :   FD_VM_INTERP_INSTR_END;
     703             : 
     704             :   FD_VM_INTERP_INSTR_BEGIN(0x77) /* FD_SBPF_OP_RSH64_IMM */
     705      200730 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L378 */
     706      200730 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, imm );
     707      200730 :   FD_VM_INTERP_INSTR_END;
     708             : 
     709             :   FD_VM_INTERP_INSTR_BEGIN(0x7c) /* FD_SBPF_OP_RSH_REG */
     710        1089 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L294 */
     711        1089 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, (uint)reg_src ) );
     712        1089 :   FD_VM_INTERP_INSTR_END;
     713             : 
     714             :   FD_VM_INTERP_BRANCH_BEGIN(0x7d) /* FD_SBPF_OP_JSGE_REG */
     715       30513 :     pc += fd_ulong_if( (long)reg_dst>=(long)reg_src, offset, 0UL );
     716       30513 :   FD_VM_INTERP_BRANCH_END;
     717             : 
     718             :   FD_VM_INTERP_INSTR_BEGIN(0x7e) /* FD_SBPF_OP_UREM64_REG */
     719         405 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     720         324 :     reg[ dst ] = reg_dst % reg_src;
     721         324 :   FD_VM_INTERP_INSTR_END;
     722             : 
     723             :   FD_VM_INTERP_INSTR_BEGIN(0x7f) /* FD_SBPF_OP_RSH64_REG */
     724        5943 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L379 */
     725        5943 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, reg_src );
     726        5943 :   FD_VM_INTERP_INSTR_END;
     727             : 
     728             :   /* 0x80-0x8f ********************************************************/
     729             : 
     730             :   FD_VM_INTERP_INSTR_BEGIN(0x84) /* FD_SBPF_OP_NEG */
     731         723 :     reg[ dst ] = (ulong)( -(uint)reg_dst );
     732         723 :   FD_VM_INTERP_INSTR_END;
     733             : 
     734             :   FD_VM_INTERP_BRANCH_BEGIN(0x85) /* FD_SBPF_OP_CALL_IMM */
     735           0 :     /* imm has already been validated */
     736           0 :     FD_VM_INTERP_STACK_PUSH;
     737           0 :     pc = (ulong)( (long)pc + (long)(int)imm );
     738           0 :   FD_VM_INTERP_BRANCH_END;
     739             : 
     740      140247 :   FD_VM_INTERP_BRANCH_BEGIN(0x85depr) { /* FD_SBPF_OP_CALL_IMM */
     741             : 
     742      140247 :     fd_sbpf_syscalls_t const * syscall = fd_sbpf_syscalls_query_const( syscalls, imm, NULL );
     743      140247 :     if( FD_UNLIKELY( !syscall ) ) { /* Optimize for the syscall case */
     744             : 
     745             :       /* Note we do the stack push before updating the pc(*). This implies
     746             :        that the call stack frame gets allocated _before_ checking if the
     747             :        call target is valid.  It would be fine to switch the order
     748             :        though such would change the precise faulting semantics of
     749             :        sigcall and sigstack.
     750             :        
     751             :        (*)but after checking calldests, see point below. */
     752             : 
     753             :       /* Agave's order of checks
     754             :          (https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L486):
     755             :           1. Lookup imm hash in FunctionRegistry (calldests_test is our equivalent)
     756             :           2. Push stack frame
     757             :           3. Check PC
     758             :           4. Update PC
     759             :           
     760             :           Following this precisely is impossible as our PC check also
     761             :           serves as a bounds check for the calldests_test call. So we
     762             :           have to perform step 3 before step 1. The following
     763             :           is a best-effort implementation that should match the VM state
     764             :           in all ways except error code. */
     765             : 
     766             :       /* Special case to handle entrypoint.
     767             :          ebpf::hash_symbol_name(b"entrypoint") = 0xb00c380, and
     768             :          fd_pchash_inverse( 0xb00c380U ) = 0x71e3cf81U */
     769      132438 :       if( FD_UNLIKELY( imm==0x71e3cf81U ) ) {
     770        1917 :         FD_VM_INTERP_STACK_PUSH;
     771        1890 :         pc = entry_pc - 1;
     772      130521 :       } else {
     773             : 
     774      130521 :         ulong target_pc = (ulong)fd_pchash_inverse( imm );
     775      130521 :         if( FD_UNLIKELY( target_pc>text_cnt ) ) {
     776             :           /* ...to match state of Agave VM when faulting
     777             :              Note: this check MUST be BEFORE fd_sbpf_calldests_test,
     778             :              because it prevents overflowing calldests. */
     779        1344 :           goto sigcall;
     780        1344 :         }
     781             : 
     782      129177 :         if( FD_UNLIKELY( !fd_sbpf_calldests_test( calldests, target_pc ) ) ) {
     783          72 :           goto sigcall;
     784          72 :         }
     785             : 
     786      258198 :         FD_VM_INTERP_STACK_PUSH;
     787      258198 :         pc = target_pc - 1;
     788      258198 :       }
     789             : 
     790      132438 :     } else {
     791             : 
     792        7809 :       FD_VM_INTERP_SYSCALL_EXEC;
     793             : 
     794        7701 :     }
     795      140247 :   } FD_VM_INTERP_BRANCH_END;
     796             : 
     797             :   FD_VM_INTERP_INSTR_BEGIN(0x86) /* FD_SBPF_OP_LMUL32_IMM */
     798         399 :     reg[ dst ] = (ulong)( (uint)reg_dst * imm );
     799         399 :   FD_VM_INTERP_INSTR_END;
     800             : 
     801        2091 :   FD_VM_INTERP_INSTR_BEGIN(0x87) { /* FD_SBPF_OP_STW */
     802        2091 :     uchar is_multi_region = 0;
     803        2091 :     ulong vaddr           = reg_dst + offset;
     804        2091 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     805        2091 :     int   sigsegv         = !haddr;
     806        2091 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     807         159 :     fd_vm_mem_st_4( vm, vaddr, haddr, imm, is_multi_region );
     808         159 :   } FD_VM_INTERP_INSTR_END;
     809             : 
     810             :   FD_VM_INTERP_INSTR_BEGIN(0x87depr) /* FD_SBPF_OP_NEG64 deprecated */
     811       14997 :     reg[ dst ] = -reg_dst;
     812       14997 :   FD_VM_INTERP_INSTR_END;
     813             : 
     814       21030 :   FD_VM_INTERP_INSTR_BEGIN(0x8c) { /* FD_SBPF_OP_LDXW */
     815       21030 :     uchar is_multi_region = 0;
     816       21030 :     ulong vaddr           = reg_src + offset;
     817       21030 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     818       21030 :     int   sigsegv         = !haddr;
     819       21030 :     if( FD_UNLIKELY( sigsegv ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     820       19650 :     reg[ dst ] = fd_vm_mem_ld_4( vm, vaddr, haddr, is_multi_region );
     821       19650 :   }
     822       19650 :   FD_VM_INTERP_INSTR_END;
     823             : 
     824       19650 :   FD_VM_INTERP_BRANCH_BEGIN(0x8d) { /* FD_SBPF_OP_CALL_REG */
     825             : 
     826         528 :     FD_VM_INTERP_STACK_PUSH;
     827             : 
     828         525 :     ulong vaddr = reg_src;
     829             : 
     830             :     /* Notes: Agave checks region and target_pc before updating the pc.
     831             :        To match their state, we do the same, even though we could simply
     832             :        update the pc and let BRANCH_END fail.
     833             :        Also, Agave doesn't check alignment. */
     834             : 
     835         525 :     ulong region = vaddr >> 32;
     836             :     /* ulong align  = vaddr & 7UL; */
     837         525 :     ulong target_pc = ((vaddr & FD_VM_OFFSET_MASK)/8UL) - text_word_off;
     838         525 :     if( FD_UNLIKELY( (region!=1UL) | (target_pc>=text_cnt) ) ) goto sigtextbr; /* Note: untaken branches don't consume BTB */
     839         195 :     pc = target_pc - 1;
     840             : 
     841         195 :   } FD_VM_INTERP_BRANCH_END;
     842             : 
     843        3738 :   FD_VM_INTERP_BRANCH_BEGIN(0x8ddepr) { /* FD_SBPF_OP_CALL_REG */
     844             : 
     845        3738 :     FD_VM_INTERP_STACK_PUSH;
     846             : 
     847        3732 :     ulong vaddr = reg[ imm & 15U ];
     848             : 
     849             :     /* Notes: Agave checks region and target_pc before updating the pc.
     850             :        To match their state, we do the same, even though we could simply
     851             :        update the pc and let BRANCH_END fail.
     852             :        Also, Agave doesn't check alignment. */
     853             : 
     854        3732 :     ulong region = vaddr >> 32;
     855             :     /* ulong align  = vaddr & 7UL; */
     856        3732 :     ulong target_pc = ((vaddr & FD_VM_OFFSET_MASK)/8UL) - text_word_off;
     857        3732 :     if( FD_UNLIKELY( (region!=1UL) | (target_pc>=text_cnt) ) ) goto sigtextbr; /* Note: untaken branches don't consume BTB */
     858        3546 :     pc = target_pc - 1;
     859             : 
     860        3546 :   } FD_VM_INTERP_BRANCH_END;
     861             : 
     862             :   FD_VM_INTERP_INSTR_BEGIN(0x8e) /* FD_SBPF_OP_LMUL32_REG */
     863         393 :     reg[ dst ] = (ulong)( (uint)reg_dst * (uint)reg_src );
     864         393 :   FD_VM_INTERP_INSTR_END;
     865             : 
     866       17043 :   FD_VM_INTERP_INSTR_BEGIN(0x8f) { /* FD_SBPF_OP_STXW */
     867       17043 :     uchar is_multi_region = 0;
     868       17043 :     ulong vaddr           = reg_dst + offset;
     869       17043 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     870       17043 :     int   sigsegv         = !haddr;
     871       17043 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus/rdonly */
     872       15108 :     fd_vm_mem_st_4( vm, vaddr, haddr, (uint)reg_src, is_multi_region );
     873       15108 :   }
     874       15108 :   FD_VM_INTERP_INSTR_END;
     875             : 
     876             :   /* 0x90 - 0x9f ******************************************************/
     877             : 
     878             :   FD_VM_INTERP_INSTR_BEGIN(0x94) /* FD_SBPF_OP_MOD_IMM */
     879         708 :     reg[ dst ] = (ulong)( (uint)reg_dst % imm );
     880         708 :   FD_VM_INTERP_INSTR_END;
     881             : 
     882         708 :   FD_VM_INTERP_BRANCH_BEGIN(0x95) { /* FD_SBPF_OP_SYSCALL */
     883             :     /* imm has already been validated to not overflow */
     884           0 :     uint syscall_key = FD_VM_SBPF_STATIC_SYSCALLS_LIST[ imm ];
     885           0 :     fd_sbpf_syscalls_t const * syscall = fd_sbpf_syscalls_query_const( syscalls, syscall_key, NULL );
     886             : 
     887             :     /* this check is probably useless, as validation includes checking that the
     888             :        syscall is active in this epoch.
     889             :        However, it's safe to keep it here, because at the time of writing this
     890             :        code we're not (re)validating all programs at every new epoch. */
     891           0 :     if( FD_UNLIKELY( !syscall ) ) goto sigill;
     892             : 
     893           0 :     FD_VM_INTERP_SYSCALL_EXEC;
     894             : 
     895           0 :   } FD_VM_INTERP_BRANCH_END;
     896             : 
     897             :   FD_VM_INTERP_INSTR_BEGIN(0x96) /* FD_SBPF_OP_LMUL64_IMM */
     898         591 :     reg[ dst ] = reg_dst * (ulong)(long)(int)imm;
     899         591 :   FD_VM_INTERP_INSTR_END;
     900             : 
     901        2091 :   FD_VM_INTERP_INSTR_BEGIN(0x97) { /* FD_SBPF_OP_STQ */
     902        2091 :     uchar is_multi_region = 0;
     903        2091 :     ulong vaddr           = reg_dst + offset;
     904        2091 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     905        2091 :     int   sigsegv         = !haddr;
     906        2091 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     907         159 :     fd_vm_mem_st_8( vm, vaddr, haddr, (ulong)(long)(int)imm, is_multi_region );
     908         159 :   }
     909         159 :   FD_VM_INTERP_INSTR_END;
     910             : 
     911      633696 :   FD_VM_INTERP_INSTR_BEGIN(0x9c) { /* FD_SBPF_OP_LDXQ */
     912      633696 :     uchar is_multi_region = 0;
     913      633696 :     ulong vaddr           = reg_src + offset;
     914      633696 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     915      633696 :     int   sigsegv         = !haddr;
     916      633696 :     if( FD_UNLIKELY( sigsegv ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     917      632106 :     reg[ dst ] = fd_vm_mem_ld_8( vm, vaddr, haddr, is_multi_region );
     918      632106 :   }
     919      632106 :   FD_VM_INTERP_INSTR_END;
     920             : 
     921             :   FD_VM_INTERP_BRANCH_BEGIN(0x9d) /* FD_SBPF_OP_EXIT */
     922      219816 :       /* Agave JIT VM exit implementation analysis below.
     923      219816 : 
     924      219816 :        Agave references:
     925      219816 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L503-L509
     926      219816 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L697-L702 */
     927      219816 :     if( FD_UNLIKELY( !frame_cnt ) ) goto sigexit; /* Exit program */
     928      130815 :     frame_cnt--;
     929      130815 :     reg[6]   = shadow[ frame_cnt ].r6;
     930      130815 :     reg[7]   = shadow[ frame_cnt ].r7;
     931      130815 :     reg[8]   = shadow[ frame_cnt ].r8;
     932      130815 :     reg[9]   = shadow[ frame_cnt ].r9;
     933      130815 :     reg[10]  = shadow[ frame_cnt ].r10;
     934      130815 :     pc       = shadow[ frame_cnt ].pc;
     935      130815 :   FD_VM_INTERP_BRANCH_END;
     936             : 
     937             :   FD_VM_INTERP_INSTR_BEGIN(0x9e) /* FD_SBPF_OP_LMUL64_REG */
     938         417 :     reg[ dst ] = reg_dst * reg_src;
     939         417 :   FD_VM_INTERP_INSTR_END;
     940             : 
     941      543399 :   FD_VM_INTERP_INSTR_BEGIN(0x9f) { /* FD_SBPF_OP_STXQ */
     942      543399 :     uchar is_multi_region = 0;
     943      543399 :     ulong vaddr           = reg_dst + offset;
     944      543399 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     945      543399 :     int   sigsegv         = !haddr;
     946      543399 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus/rdonly */
     947      541467 :     fd_vm_mem_st_8( vm, vaddr, haddr, reg_src, is_multi_region );
     948      541467 :   }
     949      541467 :   FD_VM_INTERP_INSTR_END;
     950             : 
     951             :   FD_VM_INTERP_INSTR_BEGIN(0x97depr) /* FD_SBPF_OP_MOD64_IMM */
     952         705 :     reg[ dst ] = reg_dst % (ulong)(long)(int)imm;
     953         705 :   FD_VM_INTERP_INSTR_END;
     954             : 
     955             :   FD_VM_INTERP_INSTR_BEGIN(0x9cdepr) /* FD_SBPF_OP_MOD_REG */
     956         783 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     957         624 :     reg[ dst ] = (ulong)( ((uint)reg_dst % (uint)reg_src) );
     958         624 :   FD_VM_INTERP_INSTR_END;
     959             : 
     960             :   FD_VM_INTERP_INSTR_BEGIN(0x9fdepr) /* FD_SBPF_OP_MOD64_REG */
     961         774 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     962         618 :     reg[ dst ] = reg_dst % reg_src;
     963         618 :   FD_VM_INTERP_INSTR_END;
     964             : 
     965             :   /* 0xa0 - 0xaf ******************************************************/
     966             : 
     967             :   FD_VM_INTERP_INSTR_BEGIN(0xa4) /* FD_SBPF_OP_XOR_IMM */
     968        1089 :     reg[ dst ] = (ulong)( (uint)reg_dst ^ imm );
     969        1089 :   FD_VM_INTERP_INSTR_END;
     970             : 
     971             :   FD_VM_INTERP_BRANCH_BEGIN(0xa5) /* FD_SBPF_OP_JLT_IMM */
     972        4176 :     pc += fd_ulong_if( reg_dst<(ulong)(long)(int)imm, offset, 0UL );
     973        4176 :   FD_VM_INTERP_BRANCH_END;
     974             : 
     975             :   FD_VM_INTERP_INSTR_BEGIN(0xa7) /* FD_SBPF_OP_XOR64_IMM */
     976       15864 :     reg[ dst ] = reg_dst ^ (ulong)(long)(int)imm;
     977       15864 :   FD_VM_INTERP_INSTR_END;
     978             : 
     979             :   FD_VM_INTERP_INSTR_BEGIN(0xac) /* FD_SBPF_OP_XOR_REG */
     980        1101 :     reg[ dst ] = (ulong)(uint)( reg_dst ^ reg_src );
     981        1101 :   FD_VM_INTERP_INSTR_END;
     982             : 
     983             :   FD_VM_INTERP_BRANCH_BEGIN(0xad) /* FD_SBPF_OP_JLT_REG */
     984        3573 :     pc += fd_ulong_if( reg_dst<reg_src, offset, 0UL );
     985        3573 :   FD_VM_INTERP_BRANCH_END;
     986             : 
     987             :   FD_VM_INTERP_INSTR_BEGIN(0xaf) /* FD_SBPF_OP_XOR64_REG */
     988        2418 :     reg[ dst ] = reg_dst ^ reg_src;
     989        2418 :   FD_VM_INTERP_INSTR_END;
     990             : 
     991             :   /* 0xb0 - 0xbf ******************************************************/
     992             : 
     993             :   FD_VM_INTERP_INSTR_BEGIN(0xb4) /* FD_SBPF_OP_MOV_IMM */
     994        1416 :     reg[ dst ] = (ulong)imm;
     995        1416 :   FD_VM_INTERP_INSTR_END;
     996             : 
     997             :   FD_VM_INTERP_BRANCH_BEGIN(0xb5) /* FD_SBPF_OP_JLE_IMM */
     998        7146 :     pc += fd_ulong_if( reg_dst<=(ulong)(long)(int)imm, offset, 0UL );
     999        7146 :   FD_VM_INTERP_BRANCH_END;
    1000             : 
    1001             :   FD_VM_INTERP_INSTR_BEGIN(0xb6) /* FD_SBPF_OP_SHMUL64_IMM */
    1002         363 :     reg[ dst ] = (ulong)(( (int128)(long)reg_dst * (int128)(long)(int)imm ) >> 64 );
    1003         363 :   FD_VM_INTERP_INSTR_END;
    1004             : 
    1005             :   FD_VM_INTERP_INSTR_BEGIN(0xb7) /* FD_SBPF_OP_MOV64_IMM */
    1006      493233 :     reg[ dst ] = (ulong)(long)(int)imm;
    1007      493233 :   FD_VM_INTERP_INSTR_END;
    1008             : 
    1009             :   FD_VM_INTERP_INSTR_BEGIN(0xbc) /* FD_SBPF_OP_MOV_REG */
    1010         363 :     reg[ dst ] = (ulong)(long)(int)reg_src;
    1011         363 :   FD_VM_INTERP_INSTR_END;
    1012             : 
    1013             :   FD_VM_INTERP_INSTR_BEGIN(0xbcdepr) /* FD_SBPF_OP_MOV_REG deprecated SIMD-1074 */
    1014         735 :     reg[ dst ] = (ulong)(uint)reg_src;
    1015         735 :   FD_VM_INTERP_INSTR_END;
    1016             : 
    1017             :   FD_VM_INTERP_BRANCH_BEGIN(0xbd) /* FD_SBPF_OP_JLE_REG */
    1018        5952 :     pc += fd_ulong_if( reg_dst<=reg_src, offset, 0UL );
    1019        5952 :   FD_VM_INTERP_BRANCH_END;
    1020             : 
    1021             :   FD_VM_INTERP_INSTR_BEGIN(0xbe) /* FD_SBPF_OP_SHMUL64_REG */
    1022         363 :     reg[ dst ] = (ulong)(( (int128)(long)reg_dst * (int128)(long)reg_src ) >> 64 );
    1023         363 :   FD_VM_INTERP_INSTR_END;
    1024             : 
    1025             :   FD_VM_INTERP_INSTR_BEGIN(0xbf) /* FD_SBPF_OP_MOV64_REG */
    1026     1054701 :     reg[ dst ] = reg_src;
    1027     1054701 :   FD_VM_INTERP_INSTR_END;
    1028             : 
    1029             :   /* 0xc0 - 0xcf ******************************************************/
    1030             : 
    1031             :   FD_VM_INTERP_INSTR_BEGIN(0xc4) /* FD_SBPF_OP_ARSH_IMM */
    1032         564 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst >> imm ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
    1033         564 :   FD_VM_INTERP_INSTR_END;
    1034             : 
    1035             :   FD_VM_INTERP_BRANCH_BEGIN(0xc5) /* FD_SBPF_OP_JSLT_IMM */ /* FIXME: CHECK IMM SIGN EXTENSION */
    1036        4344 :     pc += fd_ulong_if( (long)reg_dst<(long)(int)imm, offset, 0UL );
    1037        4344 :   FD_VM_INTERP_BRANCH_END;
    1038             : 
    1039             :   FD_VM_INTERP_INSTR_BEGIN(0xc6) /* FD_SBPF_OP_SDIV32_IMM */
    1040         384 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)imm==-1) ) ) goto sigfpeof;
    1041         372 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst / (int)imm );
    1042         372 :   FD_VM_INTERP_INSTR_END;
    1043             : 
    1044             :   FD_VM_INTERP_INSTR_BEGIN(0xc7) /* FD_SBPF_OP_ARSH64_IMM */
    1045       22593 :     reg[ dst ] = (ulong)( (long)reg_dst >> imm ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
    1046       22593 :   FD_VM_INTERP_INSTR_END;
    1047             : 
    1048             :   FD_VM_INTERP_INSTR_BEGIN(0xcc) /* FD_SBPF_OP_ARSH_REG */
    1049        1092 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst >> (uint)reg_src ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
    1050        1092 :   FD_VM_INTERP_INSTR_END;
    1051             : 
    1052             :   FD_VM_INTERP_BRANCH_BEGIN(0xcd) /* FD_SBPF_OP_JSLT_REG */
    1053        4158 :     pc += fd_ulong_if( (long)reg_dst<(long)reg_src, offset, 0UL );
    1054        4158 :   FD_VM_INTERP_BRANCH_END;
    1055             : 
    1056             :   FD_VM_INTERP_INSTR_BEGIN(0xce) /* FD_SBPF_OP_SDIV32_REG */
    1057         432 :     if( FD_UNLIKELY( !(int)reg_src ) ) goto sigfpe;
    1058         339 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)reg_src==-1) ) ) goto sigfpeof;
    1059         327 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst / (int)reg_src );
    1060         327 :   FD_VM_INTERP_INSTR_END;
    1061             : 
    1062             :   FD_VM_INTERP_INSTR_BEGIN(0xcf) /* FD_SBPF_OP_ARSH64_REG */
    1063        1089 :     reg[ dst ] = (ulong)( (long)reg_dst >> reg_src ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
    1064        1089 :   FD_VM_INTERP_INSTR_END;
    1065             : 
    1066             :   /* 0xd0 - 0xdf ******************************************************/
    1067             : 
    1068             :   FD_VM_INTERP_INSTR_BEGIN(0xd4) /* FD_SBPF_OP_END_LE */
    1069         207 :     switch( imm ) {
    1070          69 :     case 16U: reg[ dst ] = (ushort)reg_dst; break;
    1071          66 :     case 32U: reg[ dst ] = (uint)  reg_dst; break;
    1072          66 :     case 64U:                               break;
    1073           6 :     default: goto sigill;
    1074         207 :     }
    1075         201 :   FD_VM_INTERP_INSTR_END;
    1076             : 
    1077             :   FD_VM_INTERP_BRANCH_BEGIN(0xd5) /* FD_SBPF_OP_JSLE_IMM */
    1078        3516 :     pc += fd_ulong_if( (long)reg_dst<=(long)(int)imm, offset, 0UL );
    1079        3516 :   FD_VM_INTERP_BRANCH_END;
    1080             : 
    1081             :   FD_VM_INTERP_INSTR_BEGIN(0xd6) /* FD_SBPF_OP_SDIV64_IMM */
    1082         381 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)(int)imm==-1L) ) ) goto sigfpeof;
    1083         375 :     reg[ dst ] = (ulong)( (long)reg_dst / (long)(int)imm );
    1084         375 :   FD_VM_INTERP_INSTR_END;
    1085             : 
    1086             :   FD_VM_INTERP_INSTR_BEGIN(0xdc) /* FD_SBPF_OP_END_BE */
    1087         417 :     switch( imm ) {
    1088         132 :     case 16U: reg[ dst ] = (ulong)fd_ushort_bswap( (ushort)reg_dst ); break;
    1089         102 :     case 32U: reg[ dst ] = (ulong)fd_uint_bswap  ( (uint)  reg_dst ); break;
    1090         171 :     case 64U: reg[ dst ] =        fd_ulong_bswap ( (ulong) reg_dst ); break;
    1091          12 :     default: goto sigill;
    1092         417 :     }
    1093         405 :   FD_VM_INTERP_INSTR_END;
    1094             : 
    1095             :   FD_VM_INTERP_BRANCH_BEGIN(0xdd) /* FD_SBPF_OP_JSLE_REG */
    1096        2916 :     pc += fd_ulong_if( (long)reg_dst<=(long)reg_src, offset, 0UL );
    1097        2916 :   FD_VM_INTERP_BRANCH_END;
    1098             : 
    1099             :   FD_VM_INTERP_INSTR_BEGIN(0xde) /* FD_SBPF_OP_SDIV64_REG */
    1100         426 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
    1101         336 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)reg_src==-1L) ) ) goto sigfpeof;
    1102         330 :     reg[ dst ] = (ulong)( (long)reg_dst / (long)reg_src );
    1103         330 :   FD_VM_INTERP_INSTR_END;
    1104             : 
    1105             :   /* 0xe0 - 0xef ******************************************************/
    1106             : 
    1107             :   FD_VM_INTERP_INSTR_BEGIN(0xe6) /* FD_SBPF_OP_SREM32_IMM */
    1108         384 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)imm==-1) ) ) goto sigfpeof;
    1109         372 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst % (int)imm );
    1110         372 :   FD_VM_INTERP_INSTR_END;
    1111             : 
    1112             :   FD_VM_INTERP_INSTR_BEGIN(0xee) /* FD_SBPF_OP_SREM32_REG */
    1113         432 :     if( FD_UNLIKELY( !(int)reg_src ) ) goto sigfpe;
    1114         339 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)reg_src==-1) ) ) goto sigfpeof;
    1115         327 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst % (int)reg_src );
    1116         327 :   FD_VM_INTERP_INSTR_END;
    1117             : 
    1118             :   /* 0xf0 - 0xff ******************************************************/
    1119             : 
    1120             :   FD_VM_INTERP_INSTR_BEGIN(0xf6) /* FD_SBPF_OP_SREM64_IMM */
    1121         381 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)(int)imm==-1L) ) ) goto sigfpeof;
    1122         375 :     reg[ dst ] = (ulong)( (long)reg_dst % (long)(int)imm );
    1123         375 :   FD_VM_INTERP_INSTR_END;
    1124             : 
    1125             :   FD_VM_INTERP_BRANCH_BEGIN(0xf7) /* FD_SBPF_OP_HOR64 */
    1126         381 :     reg[ dst ] = reg_dst | (((ulong)imm) << 32);
    1127         381 :   FD_VM_INTERP_BRANCH_END;
    1128             : 
    1129             :   FD_VM_INTERP_INSTR_BEGIN(0xfe) /* FD_SBPF_OP_SREM64_REG */
    1130         426 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
    1131         336 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)reg_src==-1L) ) ) goto sigfpeof;
    1132         330 :     reg[ dst ] = (ulong)( (long)reg_dst % (long)reg_src );
    1133         330 :   FD_VM_INTERP_INSTR_END;
    1134             : 
    1135             :   /* FIXME: sigbus/sigrdonly are mapped to sigsegv for simplicity
    1136             :      currently but could be enabled if desired. */
    1137             : 
    1138             :   /* Note: sigtextbr is for sigtext errors that occur on branching 
    1139             :      instructions (i.e., prefixed with FD_VM_INTERP_BRANCH_BEGIN).
    1140             :      We skip a repeat ic accumulation in FD_VM_INTERP_FAULT */
    1141             : 
    1142             :   /* FD_VM_INTERP_FAULT accumulates to ic and cu all non-faulting
    1143             :      instructions preceeding a fault generated by a non-branching
    1144             :      instruction.  When a non-branching instruction faults, pc is at the
    1145             :      instruction and the number of non-branching instructions that have
    1146             :      not yet been reflected in ic and cu is:
    1147             : 
    1148             :        pc - pc0 + 1 - ic_correction
    1149             : 
    1150             :      as per the accounting described above. +1 to include the faulting
    1151             :      instruction itself.
    1152             : 
    1153             :      Note that, for a sigtext caused by a branch instruction, pc0==pc
    1154             :      (from the BRANCH_END) and ic_correction==0 (from the BRANCH_BEGIN)
    1155             :      such that the below does not change the already current values in
    1156             :      ic and cu.  Thus it also "does the right thing" in both the
    1157             :      non-branching and branching cases for sigtext.  The same applies to
    1158             :      sigsplit. */
    1159             : 
    1160           0 : #define FD_VM_INTERP_FAULT                                          \
    1161       23880 :   ic_correction = pc - pc0 + 1UL - ic_correction;                   \
    1162       23880 :   ic += ic_correction;                                              \
    1163       23880 :   if ( FD_UNLIKELY( ic_correction > cu ) ) err = FD_VM_ERR_SIGCOST; \
    1164       23880 :   cu -= fd_ulong_min( ic_correction, cu )
    1165             : 
    1166          81 : sigtext:     err = FD_VM_ERR_SIGTEXT;     FD_VM_INTERP_FAULT;                     goto interp_halt;
    1167         516 : sigtextbr:   err = FD_VM_ERR_SIGTEXT;     /* ic current */      /* cu current */  goto interp_halt;
    1168        1416 : sigcall:     err = FD_VM_ERR_SIGILL;      /* ic current */      /* cu current */  goto interp_halt;
    1169          48 : sigstack:    err = FD_VM_ERR_SIGSTACK;    /* ic current */      /* cu current */  goto interp_halt;
    1170        1197 : sigill:      err = FD_VM_ERR_SIGILL;      FD_VM_INTERP_FAULT;                     goto interp_halt;
    1171       21204 : sigsegv:     err = FD_VM_ERR_SIGSEGV;     FD_VM_INTERP_FAULT;                     goto interp_halt;
    1172         684 : sigcost:     err = FD_VM_ERR_SIGCOST;     /* ic current */      cu = 0UL;         goto interp_halt;
    1173         108 : sigsyscall:  err = FD_VM_ERR_SIGSYSCALL;  /* ic current */      /* cu current */  goto interp_halt;
    1174        1326 : sigfpe:      err = FD_VM_ERR_SIGFPE;      FD_VM_INTERP_FAULT;                     goto interp_halt;
    1175          72 : sigfpeof:    err = FD_VM_ERR_SIGFPE_OF;   FD_VM_INTERP_FAULT;                     goto interp_halt;
    1176       89001 : sigexit:     /* err current */            /* ic current */      /* cu current */  goto interp_halt;
    1177             : 
    1178           0 : #undef FD_VM_INTERP_FAULT
    1179             : 
    1180      115653 : interp_halt:
    1181             : 
    1182             :   /* Pack the unpacked execution state into vm to give a precise view of
    1183             :      the execution when the vm halted. */
    1184             : 
    1185      115653 :   vm->pc        = pc;
    1186      115653 :   vm->ic        = ic;
    1187      115653 :   vm->cu        = cu;
    1188      115653 :   vm->frame_cnt = frame_cnt;
    1189             : 
    1190      115653 : # undef FD_VM_INTERP_STACK_PUSH
    1191             : 
    1192      115653 : # undef FD_VM_INTERP_BRANCH_END
    1193      115653 : # undef FD_VM_INTERP_BRANCH_BEGIN
    1194             : 
    1195      115653 : # undef FD_VM_INTERP_INSTR_END
    1196      115653 : # undef FD_VM_INTERP_INSTR_BEGIN
    1197      115653 : # undef FD_VM_INTERP_INSTR_EXEC
    1198             : 
    1199      115653 : # if defined(__clang__)
    1200      115653 : # pragma clang diagnostic pop
    1201      115653 : # endif
    1202             : 
    1203      115653 : # if defined(__GNUC__)
    1204      115653 : # pragma GCC diagnostic pop
    1205      115653 : # endif
    1206             : 
    1207             : /*   Agave/JIT CU model analysis (and why we are conformant!):
    1208             : 
    1209             :      The Agave JIT employs a similar strategy of accumulating instructions
    1210             :      in a linear run and processing them at the start of a new linear 
    1211             :      run/branch (side note: the JIT treats the LDQ instruction as a "branch" 
    1212             :      that jumps pc + 2). 
    1213             :      
    1214             :      In what is assumed to be an act of register conservation, the JIT 
    1215             :      uses a catch-all "instruction meter" (IM) register (REGISTER_INSTRUCTION_METER)
    1216             :      that represents two different interpretations of the question
    1217             :      "how many instructions can I execute?".
    1218             : 
    1219             :      The IM, depending on where we are in the execution, either represents:
    1220             :         1. IM => The number of instructions remaining before exhausting CU 
    1221             :         budget. This is analagous to vm->cu in our interpreter.
    1222             :         2. IM' => The last pc you can execute in the current linear run before
    1223             :         exhausting CU budget.  Mathematically, IM' = IM + pc0
    1224             :         where pc0, just like our definition, is the start of the linear run.
    1225             :         
    1226             :         Note: IM' can go past the actual basic block/segment. In-fact, 
    1227             :         it typically does, and implies we can execute the full block without
    1228             :         exhausting CU budget (reminder that LDQ is treated as a branch).
    1229             :       
    1230             :       By default, the IM' form is used during execution. The IM form is used:
    1231             :         - (transiently) during the processing of a branch instruction 
    1232             :         - in post-VM cleanup (updates EbpfVm::previous_instruction_meter).
    1233             : 
    1234             :       When a branch instruction is encountered, the JIT checks
    1235             :       for CU exhaustion with pc > IM', and throws an exception if so. This is valid,
    1236             :       because as described above, IM' is the largest PC you can reach.
    1237             :       
    1238             :       If we haven't exhausted our CU limit, it updates IM':
    1239             :         1. IM = IM' - (pc + 1)  # Note that IM' at this point is IM + pc0', 
    1240             :                                 # where pc0' is the start of the current linear run.
    1241             :         2. IM' = IM + pc0       # pc0 is the start of the new linear run (typically the target pc)
    1242             :       
    1243             :       Code (that does the above in one ALU instruction):
    1244             :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L891
    1245             : 
    1246             : 
    1247             :       ### How does this relate to our interpreter?
    1248             : 
    1249             :       This process is similar to FD_VM_INTERP_BRANCH_BEGIN.
    1250             :       We just deal with the IM form throughout (with vm->cu and ic_correction).
    1251             :       If we break down step 1 from above with what we know about IM and IM',
    1252             :       we get the following:
    1253             :         1. IM = IM' - (pc + 1)
    1254             :            IM = (IM + pc0') - (pc + 1)
    1255             :            IM = IM + (pc0' - (pc + 1))
    1256             :            IM = IM - ((pc + 1) - pc0')
    1257             :            IM = IM - ic_correction
    1258             :       Here, ((pc + 1) - pc0') is the number of instrutions executed in the current
    1259             :       linear run. This is the same as our ic_correction(*) in FD_VM_INTERP_BRANCH_BEGIN.
    1260             : 
    1261             :       If we replace IM with cu, this effectively becomes the
    1262             :            cu -= ic_correction 
    1263             :       line in FD_VM_INTERP_BRANCH_BEGIN.
    1264             : 
    1265             :       (*) Note: ic_correction (also) takes two forms. It is either the instruction 
    1266             :       accumulator or the number of instructions executed in the current linear run. 
    1267             :       It (transiently) takes the latter form during FD_VM_INTERP_BRANCH_BEGIN and 
    1268             :       FD_VM_INTERP_FAULT, and the former form otherwise.
    1269             : */
    1270             : 
    1271             : /* (WIP) Precise faulting and the Agave JIT:
    1272             :    
    1273             :    Since the cost model is a part of consensus, we need to conform with the Agave/JIT
    1274             :    cost model 1:1. To achieve that, our faulting model also needs to match precisely. This
    1275             :    section covers the various faults that the respective VMs implement and how they match.
    1276             : 
    1277             :    # Normal VM exit (sigexit):
    1278             :    VM exit instruction entrypoint: https://github.com/solana-labs/rbpf/blob/12237895305ab38514be865ebed6268553e4f589/src/jit.rs#L698-L708
    1279             : 
    1280             :    Pseudocode (with FD semantics):
    1281             :    ```
    1282             :     # pc is at the exit instruction
    1283             :     # pc0 is the start of the current linear run
    1284             :     if (frame_cnt == 0) {
    1285             :         goto sigexit;
    1286             :     }
    1287             :     ...
    1288             : 
    1289             :     sigexit:
    1290             :     if IM' <= pc {
    1291             :       goto sigcost;
    1292             :     } else {
    1293             :       goto interp_halt;
    1294             :     }
    1295             :     ```
    1296             : 
    1297             :     Breaking down the IM' < pc check:
    1298             :     - IM' = IM + pc0
    1299             :     - pc  = ic + pc0, where (ic + 1) is the number of instructions executed in the current linear run
    1300             : 
    1301             :     IM' <= pc
    1302             :     IM + pc0 <= ic + pc0
    1303             :     IM <= ic
    1304             :     IM <= pc - pc0
    1305             :     IM < pc - pc0 + 1 # all unsigned integers
    1306             :     IM < ic_correction
    1307             : 
    1308             :     This is analagous to the ic_correction>cu check in VM_INTERP_BRANCH_BEGIN.
    1309             :    
    1310             :    # (TODO) Text Overrun (sigtext/sigsplit):
    1311             : 
    1312             : */

Generated by: LCOV version 1.14