LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_interp_core.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 478 583 82.0 %
Date: 2026-03-19 18:19:27 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        6354 : # 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      284675 : #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       34139 : #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      117912 : #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          14 : #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       22475 : #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        7279 : #define FD_RUST_LONG_WRAPPING_SHR( a, b ) ((a) >> ( (b) & ( 63 ) ))
      93             : 
      94             : 
      95           0 : # define FD_VM_INTERP_INSTR_EXEC                                                                 \
      96    15598333 :   if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigtext; /* Note: untaken branches don't consume BTB */ \
      97    15598333 :   instr   = text[ pc ];                  /* Guaranteed in-bounds */                              \
      98    15594763 :   opcode  = fd_vm_instr_opcode( instr ); /* in [0,256) even if malformed */                      \
      99    15594763 :   dst     = fd_vm_instr_dst   ( instr ); /* in [0, 16) even if malformed */                      \
     100    15594763 :   src     = fd_vm_instr_src   ( instr ); /* in [0, 16) even if malformed */                      \
     101    15594763 :   offset  = fd_vm_instr_offset( instr ); /* in [-2^15,2^15) even if malformed */                 \
     102    15594763 :   imm     = fd_vm_instr_imm   ( instr ); /* in [0,2^32) even if malformed */                     \
     103    15594763 :   reg_dst = reg[ dst ];                  /* Guaranteed in-bounds */                              \
     104    15594763 :   reg_src = reg[ src ];                  /* Guaranteed in-bounds */                              \
     105    15594763 :   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.  We use ret reduce the risk of the syscall
     118             :    accidentally modifying other registers (note however since a
     119             :    syscall has the vm handle it still do arbitrary modifications
     120             :    to the vm state) and the risk of a pointer escape on reg from
     121             :    inhibiting compiler optimizations (this risk is likely low in
     122             :    as this is the only point in the whole interpreter core that
     123             :    calls outside this translation unit).
     124             :    At this point, vm->cu is positive.
     125             : 
     126             :    Error handling.
     127             :    If we trust syscall implementations to handle the vm state
     128             :    correctly, the below could be implemented as unpacking the vm
     129             :    state and jumping to sigsys on error.  But we provide some
     130             :    extra protection to make various strong guarantees:
     131             : 
     132             :    - We do not let the syscall modify pc currently as nothing
     133             :      requires this and it reduces risk of a syscall bug mucking
     134             :      up the interpreter.  If there ever was a syscall that
     135             :      needed to modify the pc (e.g. a syscall that has execution
     136             :      resume from a different location than the instruction
     137             :      following the syscall), do "pc = vm->pc" below.
     138             : 
     139             :    - We do not let the syscall modify ic currently as nothing
     140             :      requires this and it keeps the ic precise.  If a future
     141             :      syscall needs this, do "ic = vm->ic" below.
     142             : 
     143             :    - We do not let the syscall increase cu as nothing requires
     144             :      this and it guarantees the interpreter will halt in a
     145             :      reasonable finite amount of time.  If a future syscall
     146             :      needs this, do "cu = vm->cu" below.
     147             : 
     148             :    - A syscall that returns SIGCOST is always treated as though
     149             :      it also zerod cu.
     150             : 
     151             :    At this point, vm->cu is whatever the syscall tried to set
     152             :    and cu is positive.
     153             : 
     154             :    Exit
     155             :    At this point, cu is positive and err is clear.
     156             : */
     157             : 
     158           0 : # if FD_HAS_FLATCC
     159           0 : # define FD_VM_INTERP_SYSCALL_EXEC_DUMP                                       \
     160             :   /* Dumping for debugging purposes */                                        \
     161        7558 :   if( FD_UNLIKELY( vm->dump_syscall_to_pb ) ) {                               \
     162           0 :     fd_dump_vm_syscall_to_protobuf( vm, syscall->name );                      \
     163           0 :   }
     164             : # else
     165             : # define FD_VM_INTERP_SYSCALL_EXEC_DUMP
     166             : # endif
     167           0 : # define FD_VM_INTERP_SYSCALL_EXEC                                            \
     168             :   /* Setup */                                                                 \
     169        7558 :   vm->pc        = pc;                                                         \
     170        7558 :   vm->ic        = ic;                                                         \
     171        7558 :   vm->cu        = cu;                                                         \
     172        7558 :   vm->frame_cnt = frame_cnt;                                                  \
     173        7558 :   FD_VM_INTERP_SYSCALL_EXEC_DUMP                                              \
     174             :   /* Execution */                                                             \
     175        7558 :   ulong ret[1];                                                               \
     176        7558 :   err = syscall->func( vm, reg[1], reg[2], reg[3], reg[4], reg[5], ret );     \
     177        7558 :   reg[0] = ret[0];                                                            \
     178             :   /* Error handling */                                                        \
     179        7558 :   ulong cu_req = vm->cu;                                                      \
     180        7558 :   cu = fd_ulong_min( cu_req, cu );                                            \
     181        7558 :   if( FD_UNLIKELY( err ) ) {                                                  \
     182        3562 :     if( err==FD_VM_SYSCALL_ERR_COMPUTE_BUDGET_EXCEEDED ) cu = 0UL; /* cmov */ \
     183        3562 :     FD_VM_TEST_ERR_EXISTS( vm );                                              \
     184        3562 :     goto sigsyscall;                                                          \
     185        3562 :   }                                                                           \
     186             :   /* Exit */
     187             : 
     188             : 
     189             :   /* FD_VM_INTERP_INSTR_BEGIN / FD_VM_INTERP_INSTR_END bracket opcode's
     190             :      implementation for an opcode that does not branch.  On entry, the
     191             :      instruction word has been unpacked into dst / src / offset / imm
     192             :      and reg[dst] / reg[src] has been prefetched into reg_dst / reg_src. */
     193             : 
     194    10296292 : # define FD_VM_INTERP_INSTR_BEGIN(opcode) interp_##opcode:
     195             : 
     196             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~0.3% faster in some benchmarks, slower in others but more code footprint */
     197    10296108 : # define FD_VM_INTERP_INSTR_END pc++; FD_VM_INTERP_INSTR_EXEC
     198             : # else /* Use this version when tracing or optimizing code footprint */
     199           0 : # define FD_VM_INTERP_INSTR_END pc++; goto interp_exec
     200             : # endif
     201             : 
     202             :   /* Instead of doing a lot of compute budget calcs and tests every
     203             :      instruction, we note that the program counter increases
     204             :      monotonically after a branch (or a program start) until the next
     205             :      branch (or program termination).  We save the program counter of
     206             :      the start of such a segment in pc0.  Whenever we encounter a branch
     207             :      (or a program termination) at pc, we know we processed pc-pc0+1
     208             :      text words (including the text word for the branch instruction
     209             :      itself as all branch instructions are single word).
     210             : 
     211             :      Each instruction costs 1 cu (syscalls can cost extra on top of
     212             :      this that is accounted separately in CALL_IMM below).  Since there
     213             :      could have been multiword instructions in this segment, at start of
     214             :      such a segment, we zero out the accumulator ic_correction and have
     215             :      every multiword instruction in the segment accumulate the number of
     216             :      extra text words it has to this variable.  (Sigh ... it would be a
     217             :      lot simpler to bill based on text words processed but this would be
     218             :      very difficult to make this protocol change at this point.)
     219             : 
     220             :      When we encounter a branch at pc, the number of instructions
     221             :      processed (and thus the number of compute units to bill for that
     222             :      segment) is thus:
     223             : 
     224             :        pc - pc0 + 1 - ic_correction
     225             : 
     226             :      IMPORTANT SAFETY TIP!  This implies the worst case interval before
     227             :      checking the cu budget is the worst case text_cnt.  But since all
     228             :      such instructions are cheap 1 cu instructions and processed fast
     229             :      and text max is limited in size, this should be acceptable in
     230             :      practice.  FIXME: DOUBLE CHECK THE MATH ABOVE AGAINST PROTOCOL
     231             :      LIMITS. */
     232             : 
     233           0 :   ulong pc0           = pc;
     234           0 :   ulong ic_correction = 0UL;
     235             : 
     236           0 : # define FD_VM_INTERP_BRANCH_BEGIN(opcode)                                                              \
     237     5298450 :   interp_##opcode:                                                                                      \
     238             :     /* Bill linear text segment and this branch instruction as per the above */                         \
     239     5298450 :     ic_correction = pc - pc0 + 1UL - ic_correction;                                                     \
     240     5298450 :     ic += ic_correction;                                                                                \
     241     5298450 :     if( FD_UNLIKELY( ic_correction>cu ) ) goto sigcost; /* Note: untaken branches don't consume BTB */  \
     242     5298450 :     cu -= ic_correction;                                                                                \
     243             :     /* At this point, cu>=0 */                                                                          \
     244     5297118 :     ic_correction = 0UL;
     245             : 
     246             :   /* FIXME: debatable if it is better to do pc++ here or have the
     247             :      instruction implementations do it in their code path. */
     248             : 
     249             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~4% faster in some benchmarks, slower in others but more code footprint */
     250             : # define FD_VM_INTERP_BRANCH_END               \
     251     5292303 :     pc++;                                      \
     252     5292303 :     pc0 = pc; /* Start a new linear segment */ \
     253     5295872 :     FD_VM_INTERP_INSTR_EXEC
     254             : # else /* Use this version when tracing or optimizing code footprint */
     255             : # define FD_VM_INTERP_BRANCH_END               \
     256           0 :     pc++;                                      \
     257           0 :     pc0 = pc; /* Start a new linear segment */ \
     258             :     /* FIXME: TEST sigsplit HERE */            \
     259           0 :     goto interp_exec
     260             : # endif
     261             : 
     262             :   /* FD_VM_INTERP_STACK_PUSH pushes reg[6:9] onto the shadow stack and
     263             :      advances reg[10] to a new user stack frame.  If there are no more
     264             :      stack frames available, will do a SIGSTACK. */
     265             : 
     266             :   /* FIXME: double check faulting is desired on stack overflow. */
     267             : 
     268             :   /* FIXME: a pre-belt-sanding FIXME implied the TLB should be updated
     269             :      to prevent byte code from accessing the stack outside its current
     270             :      stack frame.  But this would break the common practice of a
     271             :      function passing a pointer to something on its stack into a
     272             :      function that it calls:
     273             : 
     274             :        void foo( ... ) {
     275             :          ...
     276             :          int ret;
     277             :          bar( &ret );
     278             :          ...
     279             :        }
     280             : 
     281             :      So this probably shouldn't be done.  But, if it is in fact
     282             :      necessary, the TLB updates would be here and in pop. */
     283             : 
     284             :   /* FIXME: unvalidated code mucking with r10 */
     285             : 
     286           0 : # define FD_VM_INTERP_STACK_PUSH                                                                             \
     287       91992 :   shadow[ frame_cnt ].r6  = reg[6];                                                                          \
     288       91992 :   shadow[ frame_cnt ].r7  = reg[7];                                                                          \
     289       91992 :   shadow[ frame_cnt ].r8  = reg[8];                                                                          \
     290       91992 :   shadow[ frame_cnt ].r9  = reg[9];                                                                          \
     291       91992 :   shadow[ frame_cnt ].r10 = reg[10];                                                                         \
     292       91992 :   shadow[ frame_cnt ].pc  = pc;                                                                              \
     293       91992 :   if( FD_UNLIKELY( ++frame_cnt>=frame_max ) ) goto sigstack; /* Note: untaken branches don't consume BTB */  \
     294       91992 :   if( !fd_sbpf_dynamic_stack_frames_enabled( sbpf_version ) ) reg[10] += FD_VM_STACK_FRAME_SZ * 2UL;         \
     295           0 : 
     296             :   /* We subtract the heap cost in the BPF loader */
     297             : 
     298           0 :   goto interp_exec; /* Silly but to avoid unused label warning in some configurations */
     299        6353 : interp_exec:
     300             : 
     301             : # ifdef FD_VM_INTERP_EXE_TRACING_ENABLED
     302             :   /* Note: when tracing or optimizing for code footprint, all
     303             :      instruction execution starts here such that this is only point
     304             :      where exe tracing diagnostics are needed. */
     305           0 :   if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigtext;
     306           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 );
     307           0 : # endif
     308             : 
     309        6353 :   FD_VM_INTERP_INSTR_EXEC;
     310             : 
     311             :   /* 0x00 - 0x0f ******************************************************/
     312             : 
     313             :   FD_VM_INTERP_INSTR_BEGIN(0x04) /* FD_SBPF_OP_ADD_IMM */
     314           3 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst + (int)imm );
     315           3 :   FD_VM_INTERP_INSTR_END;
     316             : 
     317             :   FD_VM_INTERP_INSTR_BEGIN(0x04depr) /* FD_SBPF_OP_ADD_IMM deprecated SIMD-0174 */
     318      482602 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)imm );
     319      482602 :   FD_VM_INTERP_INSTR_END;
     320             : 
     321             :   FD_VM_INTERP_BRANCH_BEGIN(0x05) /* FD_SBPF_OP_JA */
     322      836357 :     pc += offset;
     323      836357 :   FD_VM_INTERP_BRANCH_END;
     324             : 
     325             :   FD_VM_INTERP_INSTR_BEGIN(0x07) /* FD_SBPF_OP_ADD64_IMM */
     326      642495 :     reg[ dst ] = reg_dst + (ulong)(long)(int)imm;
     327      642495 :   FD_VM_INTERP_INSTR_END;
     328             : 
     329             :   FD_VM_INTERP_INSTR_BEGIN(0x0c) /* FD_SBPF_OP_ADD_REG */
     330           0 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst + (int)reg_src );
     331           0 :   FD_VM_INTERP_INSTR_END;
     332             : 
     333             :   FD_VM_INTERP_INSTR_BEGIN(0x0cdepr) /* FD_SBPF_OP_ADD_REG deprecated SIMD-0174 */
     334       19579 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)reg_src );
     335       19579 :   FD_VM_INTERP_INSTR_END;
     336             : 
     337             :   FD_VM_INTERP_INSTR_BEGIN(0x0f) /* FD_SBPF_OP_ADD64_REG */
     338      286759 :     reg[ dst ] = reg_dst + reg_src;
     339      286759 :   FD_VM_INTERP_INSTR_END;
     340             : 
     341             :   /* 0x10 - 0x1f ******************************************************/
     342             : 
     343             :   FD_VM_INTERP_INSTR_BEGIN(0x14) /* FD_SBPF_OP_SUB_IMM */
     344           0 :     reg[ dst ] = (ulong)(uint)( (int)imm - (int)reg_dst );
     345           0 :   FD_VM_INTERP_INSTR_END;
     346             : 
     347             :   FD_VM_INTERP_INSTR_BEGIN(0x14depr) /* FD_SBPF_OP_SUB_IMM deprecated SIMD-0174 */
     348       19882 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)imm );
     349       19882 :   FD_VM_INTERP_INSTR_END;
     350             : 
     351             :   FD_VM_INTERP_BRANCH_BEGIN(0x15) /* FD_SBPF_OP_JEQ_IMM */
     352      163483 :     pc += fd_ulong_if( reg_dst==(ulong)(long)(int)imm, offset, 0UL );
     353      163483 :   FD_VM_INTERP_BRANCH_END;
     354             : 
     355             :   FD_VM_INTERP_INSTR_BEGIN(0x17) /* FD_SBPF_OP_SUB64_IMM */
     356           0 :     reg[ dst ] = (ulong)(long)(int)imm - reg_dst;
     357           0 :   FD_VM_INTERP_INSTR_END;
     358             : 
     359             :   FD_VM_INTERP_INSTR_BEGIN(0x17depr) /* FD_SBPF_OP_SUB64_IMM deprecated SIMD-0174 */
     360           7 :     reg[ dst ] = reg_dst - (ulong)(long)(int)imm;
     361           7 :   FD_VM_INTERP_INSTR_END;
     362             : 
     363             :   FD_VM_INTERP_INSTR_BEGIN(0x18) /* FD_SBPF_OP_LDQ */
     364      812797 :     pc++;
     365      812797 :     ic_correction++;
     366             :     /* No need to check pc because it's already checked during validation.
     367             :        if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigsplit; // Note: untaken branches don't consume BTB */
     368      812797 :     reg[ dst ] = (ulong)((ulong)imm | ((ulong)fd_vm_instr_imm( text[ pc ] ) << 32));
     369      812797 :   FD_VM_INTERP_INSTR_END;
     370             : 
     371             :   FD_VM_INTERP_INSTR_BEGIN(0x1c) /* FD_SBPF_OP_SUB_REG */
     372           2 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst - (int)reg_src );
     373           2 :   FD_VM_INTERP_INSTR_END;
     374             : 
     375             :   FD_VM_INTERP_INSTR_BEGIN(0x1cdepr) /* FD_SBPF_OP_SUB_REG deprecated SIMD-0174 */
     376      214882 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)reg_src );
     377      214882 :   FD_VM_INTERP_INSTR_END;
     378             : 
     379             :   FD_VM_INTERP_BRANCH_BEGIN(0x1d) /* FD_SBPF_OP_JEQ_REG */
     380        9894 :     pc += fd_ulong_if( reg_dst==reg_src, offset, 0UL );
     381        9894 :   FD_VM_INTERP_BRANCH_END;
     382             : 
     383             :   FD_VM_INTERP_INSTR_BEGIN(0x1f) /* FD_SBPF_OP_SUB64_REG */
     384       64245 :     reg[ dst ] = reg_dst - reg_src;
     385       64245 :   FD_VM_INTERP_INSTR_END;
     386             : 
     387             :   /* 0x20 - 0x2f ******************************************************/
     388             : 
     389             :   FD_VM_INTERP_INSTR_BEGIN(0x24) /* FD_SBPF_OP_MUL_IMM */
     390      215754 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)imm );
     391      215754 :   FD_VM_INTERP_INSTR_END;
     392             : 
     393             :   FD_VM_INTERP_BRANCH_BEGIN(0x25) /* FD_SBPF_OP_JGT_IMM */
     394       28703 :     pc += fd_ulong_if( reg_dst>(ulong)(long)(int)imm, offset, 0UL );
     395       28703 :   FD_VM_INTERP_BRANCH_END;
     396             : 
     397          81 :   FD_VM_INTERP_INSTR_BEGIN(0x27) { /* FD_SBPF_OP_STB */
     398          81 :     ulong vaddr = reg_dst + offset;
     399          81 :     ulong haddr = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL );
     400          81 :     if( FD_UNLIKELY( !haddr ) ) {
     401           6 :       vm->segv_vaddr       = vaddr;
     402           6 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     403           6 :       vm->segv_access_len  = 1UL;
     404           6 :       goto sigsegv;
     405           6 :     } /* Note: untaken branches don't consume BTB */
     406          75 :     fd_vm_mem_st_1( haddr, (uchar)imm );
     407          75 :   }
     408          75 :   FD_VM_INTERP_INSTR_END;
     409             : 
     410      181063 :   FD_VM_INTERP_INSTR_BEGIN(0x2c) { /* FD_SBPF_OP_LDXB */
     411      181063 :     ulong vaddr = reg_src + offset;
     412      181063 :     ulong haddr = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_ld_sz, 0, 0UL );
     413      181063 :     if( FD_UNLIKELY( !haddr ) ) {
     414           9 :       vm->segv_vaddr       = vaddr;
     415           9 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     416           9 :       vm->segv_access_len  = 1UL;
     417           9 :       goto sigsegv;
     418           9 :     } /* Note: untaken branches don't consume BTB */
     419      181054 :     reg[ dst ] = fd_vm_mem_ld_1( haddr );
     420      181054 :   }
     421      181054 :   FD_VM_INTERP_INSTR_END;
     422             : 
     423             :   FD_VM_INTERP_BRANCH_BEGIN(0x2d) /* FD_SBPF_OP_JGT_REG */
     424      257184 :     pc += fd_ulong_if( reg_dst>reg_src, offset, 0UL );
     425      257184 :   FD_VM_INTERP_BRANCH_END;
     426             : 
     427      156490 :   FD_VM_INTERP_INSTR_BEGIN(0x2f) { /* FD_SBPF_OP_STXB */
     428      156490 :     ulong vaddr = reg_dst + offset;
     429      156490 :     ulong haddr = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL );
     430      156490 :     if( FD_UNLIKELY( !haddr ) ) {
     431          16 :       vm->segv_vaddr       = vaddr;
     432          16 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     433          16 :       vm->segv_access_len  = 1UL;
     434          16 :       goto sigsegv;
     435          16 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigrdonly */
     436      156474 :     fd_vm_mem_st_1( haddr, (uchar)reg_src );
     437      156474 :   }
     438      156474 :   FD_VM_INTERP_INSTR_END;
     439             : 
     440             :   FD_VM_INTERP_INSTR_BEGIN(0x27depr) /* FD_SBPF_OP_MUL64_IMM */
     441       40212 :     reg[ dst ] = (ulong)( (long)reg_dst * (long)(int)imm );
     442       40212 :   FD_VM_INTERP_INSTR_END;
     443             : 
     444             :   FD_VM_INTERP_INSTR_BEGIN(0x2cdepr) /* FD_SBPF_OP_MUL_REG */
     445       57928 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)reg_src );
     446       57928 :   FD_VM_INTERP_INSTR_END;
     447             : 
     448             :   FD_VM_INTERP_INSTR_BEGIN(0x2fdepr) /* FD_SBPF_OP_MUL64_REG */
     449       58162 :     reg[ dst ] = reg_dst * reg_src;
     450       58162 :   FD_VM_INTERP_INSTR_END;
     451             : 
     452             :   /* 0x30 - 0x3f ******************************************************/
     453             : 
     454             :   FD_VM_INTERP_INSTR_BEGIN(0x34) /* FD_SBPF_OP_DIV_IMM */
     455       49683 :     /* FIXME: convert to a multiply at validation time (usually probably
     456       49683 :        not worth it) */
     457       49683 :     reg[ dst ] = (ulong)((uint)reg_dst / imm);
     458       49683 :   FD_VM_INTERP_INSTR_END;
     459             : 
     460             :   FD_VM_INTERP_BRANCH_BEGIN(0x35) /* FD_SBPF_OP_JGE_IMM */
     461         381 :     pc += fd_ulong_if( reg_dst>=(ulong)(long)(int)imm, offset, 0UL );
     462         381 :   FD_VM_INTERP_BRANCH_END;
     463             : 
     464             :   FD_VM_INTERP_INSTR_BEGIN(0x36) /* FD_SBPF_OP_UHMUL64_IMM */
     465           0 :     reg[ dst ] = (ulong)(( (uint128)reg_dst * (uint128)(ulong)imm ) >> 64 );
     466           0 :   FD_VM_INTERP_INSTR_END;
     467             : 
     468       36247 :   FD_VM_INTERP_INSTR_BEGIN(0x37) { /* FD_SBPF_OP_STH */
     469       36247 :     ulong vaddr   = reg_dst + offset;
     470       36247 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL );
     471       36247 :     int   sigsegv = !haddr;
     472       36247 :     if( FD_UNLIKELY( sigsegv ) ) {
     473           4 :       vm->segv_vaddr       = vaddr;
     474           4 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     475           4 :       vm->segv_access_len  = 2UL;
     476           4 :       goto sigsegv;
     477           4 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     478       36243 :     fd_vm_mem_st_2( haddr, (ushort)imm );
     479       36243 :   }
     480       36243 :   FD_VM_INTERP_INSTR_END;
     481             : 
     482        1167 :   FD_VM_INTERP_INSTR_BEGIN(0x3c) { /* FD_SBPF_OP_LDXH */
     483        1167 :     ulong vaddr   = reg_src + offset;
     484        1167 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_ld_sz, 0, 0UL );
     485        1167 :     int   sigsegv = !haddr;
     486        1167 :     if( FD_UNLIKELY( sigsegv ) ) {
     487          10 :       vm->segv_vaddr       = vaddr;
     488          10 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     489          10 :       vm->segv_access_len  = 2UL;
     490          10 :       goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     491          10 :     }
     492        1157 :     reg[ dst ] = fd_vm_mem_ld_2( haddr );
     493        1157 :   }
     494        1157 :   FD_VM_INTERP_INSTR_END;
     495             : 
     496             :   FD_VM_INTERP_BRANCH_BEGIN(0x3d) /* FD_SBPF_OP_JGE_REG */
     497      519074 :     pc += fd_ulong_if( reg_dst>=reg_src, offset, 0UL );
     498      519074 :   FD_VM_INTERP_BRANCH_END;
     499             : 
     500        1170 :   FD_VM_INTERP_INSTR_BEGIN(0x3f) { /* FD_SBPF_OP_STXH */
     501        1170 :     ulong vaddr   = reg_dst + offset;
     502        1170 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL );
     503        1170 :     int   sigsegv = !haddr;
     504        1170 :     if( FD_UNLIKELY( sigsegv ) ) {
     505           5 :       vm->segv_vaddr       = vaddr;
     506           5 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     507           5 :       vm->segv_access_len  = 2UL;
     508           5 :       goto sigsegv;
     509           5 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     510        1165 :     fd_vm_mem_st_2( haddr, (ushort)reg_src );
     511        1165 :   }
     512        1165 :   FD_VM_INTERP_INSTR_END;
     513             : 
     514             :   FD_VM_INTERP_INSTR_BEGIN(0x3e) /* FD_SBPF_OP_UHMUL64_REG */
     515           3 :     reg[ dst ] = (ulong)(( (uint128)reg_dst * (uint128)reg_src ) >> 64 );
     516           3 :   FD_VM_INTERP_INSTR_END;
     517             : 
     518             :   FD_VM_INTERP_INSTR_BEGIN(0x37depr) /* FD_SBPF_OP_DIV64_IMM */
     519       50041 :     reg[ dst ] = reg_dst / (ulong)(long)(int)imm;
     520       50041 :   FD_VM_INTERP_INSTR_END;
     521             : 
     522             :   FD_VM_INTERP_INSTR_BEGIN(0x3cdepr) /* FD_SBPF_OP_DIV_REG */
     523      549028 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     524      549028 :     reg[ dst ] = (ulong)((uint)reg_dst / (uint)reg_src);
     525      549028 :   FD_VM_INTERP_INSTR_END;
     526             : 
     527             :   FD_VM_INTERP_INSTR_BEGIN(0x3fdepr) /* FD_SBPF_OP_DIV64_REG */
     528      155173 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     529      155173 :     reg[ dst ] = reg_dst / reg_src;
     530      155173 :   FD_VM_INTERP_INSTR_END;
     531             : 
     532             :   /* 0x40 - 0x4f ******************************************************/
     533             : 
     534             :   FD_VM_INTERP_INSTR_BEGIN(0x44) /* FD_SBPF_OP_OR_IMM */
     535       44673 :     reg[ dst ] = (ulong)( (uint)reg_dst | imm );
     536       44673 :   FD_VM_INTERP_INSTR_END;
     537             : 
     538             :   FD_VM_INTERP_BRANCH_BEGIN(0x45) /* FD_SBPF_OP_JSET_IMM */
     539      112071 :     pc += fd_ulong_if( !!(reg_dst & (ulong)(long)(int)imm), offset, 0UL );
     540      112071 :   FD_VM_INTERP_BRANCH_END;
     541             : 
     542             :   FD_VM_INTERP_INSTR_BEGIN(0x46) /* FD_SBPF_OP_UDIV32_IMM */
     543           0 :     reg[ dst ] = (ulong)( (uint)reg_dst / (uint)imm );
     544           0 :   FD_VM_INTERP_INSTR_END;
     545             : 
     546             :   FD_VM_INTERP_INSTR_BEGIN(0x47) /* FD_SBPF_OP_OR64_IMM */
     547      111217 :     reg[ dst ] = reg_dst | (ulong)(long)(int)imm;
     548      111217 :   FD_VM_INTERP_INSTR_END;
     549             : 
     550             :   FD_VM_INTERP_INSTR_BEGIN(0x4c) /* FD_SBPF_OP_OR_REG */
     551       36238 :     reg[ dst ] = (ulong)(uint)( reg_dst | reg_src );
     552       36238 :   FD_VM_INTERP_INSTR_END;
     553             : 
     554             :   FD_VM_INTERP_BRANCH_BEGIN(0x4d) /* FD_SBPF_OP_JSET_REG */
     555           2 :     pc += fd_ulong_if( !!(reg_dst & reg_src), offset, 0UL );
     556           2 :   FD_VM_INTERP_BRANCH_END;
     557             : 
     558             :   FD_VM_INTERP_INSTR_BEGIN(0x4e) /* FD_SBPF_OP_UDIV32_REG */
     559           0 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     560           0 :     reg[ dst ] = (ulong)( (uint)reg_dst / (uint)reg_src );
     561           0 :   FD_VM_INTERP_INSTR_END;
     562             : 
     563             :   FD_VM_INTERP_INSTR_BEGIN(0x4f) /* FD_SBPF_OP_OR64_REG */
     564       61779 :     reg[ dst ] = reg_dst | reg_src;
     565       61779 :   FD_VM_INTERP_INSTR_END;
     566             : 
     567             :   /* 0x50 - 0x5f ******************************************************/
     568             : 
     569             :   FD_VM_INTERP_INSTR_BEGIN(0x54) /* FD_SBPF_OP_AND_IMM */
     570       62899 :     reg[ dst ] = (ulong)( (uint)reg_dst & imm );
     571       62899 :   FD_VM_INTERP_INSTR_END;
     572             : 
     573             :   FD_VM_INTERP_BRANCH_BEGIN(0x55) /* FD_SBPF_OP_JNE_IMM */
     574      463145 :     pc += fd_ulong_if( reg_dst!=(ulong)(long)(int)imm, offset, 0UL );
     575      463145 :   FD_VM_INTERP_BRANCH_END;
     576             : 
     577             :   FD_VM_INTERP_INSTR_BEGIN(0x56) /* FD_SBPF_OP_UDIV64_IMM */
     578           0 :     reg[ dst ] = reg_dst / (ulong)imm;
     579           0 :   FD_VM_INTERP_INSTR_END;
     580             : 
     581             :   FD_VM_INTERP_INSTR_BEGIN(0x57) /* FD_SBPF_OP_AND64_IMM */
     582      170823 :     reg[ dst ] = reg_dst & (ulong)(long)(int)imm;
     583      170823 :   FD_VM_INTERP_INSTR_END;
     584             : 
     585             :   FD_VM_INTERP_INSTR_BEGIN(0x5c) /* FD_SBPF_OP_AND_REG */
     586       35934 :     reg[ dst ] = (ulong)(uint)( reg_dst & reg_src );
     587       35934 :   FD_VM_INTERP_INSTR_END;
     588             : 
     589             :   FD_VM_INTERP_BRANCH_BEGIN(0x5d) /* FD_SBPF_OP_JNE_REG */
     590     1144483 :     pc += fd_ulong_if( reg_dst!=reg_src, offset, 0UL );
     591     1144483 :   FD_VM_INTERP_BRANCH_END;
     592             : 
     593             :   FD_VM_INTERP_INSTR_BEGIN(0x5e) /* FD_SBPF_OP_UDIV64_REG */
     594           0 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     595           0 :     reg[ dst ] = reg_dst / reg_src;
     596           0 :   FD_VM_INTERP_INSTR_END;
     597             : 
     598             :   FD_VM_INTERP_INSTR_BEGIN(0x5f) /* FD_SBPF_OP_AND64_REG */
     599      139454 :     reg[ dst ] = reg_dst & reg_src;
     600      139454 :   FD_VM_INTERP_INSTR_END;
     601             : 
     602             :   /* 0x60 - 0x6f ******************************************************/
     603             : 
     604             :   /* FIXME: CHECK THE CU COST MODEL FOR THESE (IS IT LIKE
     605             :      FD_VM_CONSUME_MEM AND NOT JUST FIXED) */
     606             :   /* FIXME: MEM TRACING DIAGNOSTICS GO IN HERE */
     607             : 
     608             :   FD_VM_INTERP_INSTR_BEGIN(0x64) /* FD_SBPF_OP_LSH_IMM */
     609           4 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L291 */
     610           4 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, (uint)imm ) );
     611           4 :   FD_VM_INTERP_INSTR_END;
     612             : 
     613             :   FD_VM_INTERP_BRANCH_BEGIN(0x65) /* FD_SBPF_OP_JSGT_IMM */
     614       28236 :     pc += fd_ulong_if( (long)reg_dst>(long)(int)imm, offset, 0UL );
     615       28236 :   FD_VM_INTERP_BRANCH_END;
     616             : 
     617             :   FD_VM_INTERP_INSTR_BEGIN(0x66) /* FD_SBPF_OP_UREM32_IMM */
     618           4 :     reg[ dst ] = (ulong)( (uint)reg_dst % (uint)imm );
     619           4 :   FD_VM_INTERP_INSTR_END;
     620             : 
     621             :   FD_VM_INTERP_INSTR_BEGIN(0x67) /* FD_SBPF_OP_LSH64_IMM */
     622      259581 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L376 */
     623      259581 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, imm );
     624      259581 :   FD_VM_INTERP_INSTR_END;
     625             : 
     626             :   FD_VM_INTERP_INSTR_BEGIN(0x6c) /* FD_SBPF_OP_LSH_REG */
     627      117908 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L292 */
     628      117908 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, reg_src ) );
     629      117908 :   FD_VM_INTERP_INSTR_END;
     630             : 
     631             :   FD_VM_INTERP_BRANCH_BEGIN(0x6d) /* FD_SBPF_OP_JSGT_REG */
     632       11462 :     pc += fd_ulong_if( (long)reg_dst>(long)reg_src, offset, 0UL );
     633       11462 :   FD_VM_INTERP_BRANCH_END;
     634             : 
     635             :   FD_VM_INTERP_INSTR_BEGIN(0x6e) /* FD_SBPF_OP_UREM32_REG */
     636           0 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     637           0 :     reg[ dst ] = (ulong)( (uint)reg_dst % (uint)reg_src );
     638           0 :   FD_VM_INTERP_INSTR_END;
     639             : 
     640             :   FD_VM_INTERP_INSTR_BEGIN(0x6f) /* FD_SBPF_OP_LSH64_REG */
     641       25094 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L377 */
     642       25094 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, reg_src );
     643       25094 :   FD_VM_INTERP_INSTR_END;
     644             : 
     645             :   /* 0x70 - 0x7f ******************************************************/
     646             : 
     647             :   FD_VM_INTERP_INSTR_BEGIN(0x74) /* FD_SBPF_OP_RSH_IMM */
     648           6 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L293 */
     649           6 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, imm ) );
     650           6 :   FD_VM_INTERP_INSTR_END;
     651             : 
     652             :   FD_VM_INTERP_BRANCH_BEGIN(0x75) /* FD_SBPF_OP_JSGE_IMM */
     653          18 :     pc += fd_ulong_if( (long)reg_dst>=(long)(int)imm, offset, 0UL );
     654          18 :   FD_VM_INTERP_BRANCH_END;
     655             : 
     656             :   FD_VM_INTERP_INSTR_BEGIN(0x76) /* FD_SBPF_OP_UREM64_IMM */
     657           1 :     reg[ dst ] = reg_dst % (ulong)imm;
     658           1 :   FD_VM_INTERP_INSTR_END;
     659             : 
     660             :   FD_VM_INTERP_INSTR_BEGIN(0x77) /* FD_SBPF_OP_RSH64_IMM */
     661       28948 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L378 */
     662       28948 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, imm );
     663       28948 :   FD_VM_INTERP_INSTR_END;
     664             : 
     665             :   FD_VM_INTERP_INSTR_BEGIN(0x7c) /* FD_SBPF_OP_RSH_REG */
     666           8 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L294 */
     667           8 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, (uint)reg_src ) );
     668           8 :   FD_VM_INTERP_INSTR_END;
     669             : 
     670             :   FD_VM_INTERP_BRANCH_BEGIN(0x7d) /* FD_SBPF_OP_JSGE_REG */
     671       39781 :     pc += fd_ulong_if( (long)reg_dst>=(long)reg_src, offset, 0UL );
     672       39781 :   FD_VM_INTERP_BRANCH_END;
     673             : 
     674             :   FD_VM_INTERP_INSTR_BEGIN(0x7e) /* FD_SBPF_OP_UREM64_REG */
     675           0 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     676           0 :     reg[ dst ] = reg_dst % reg_src;
     677           0 :   FD_VM_INTERP_INSTR_END;
     678             : 
     679             :   FD_VM_INTERP_INSTR_BEGIN(0x7f) /* FD_SBPF_OP_RSH64_REG */
     680        5191 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L379 */
     681        5191 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, reg_src );
     682        5191 :   FD_VM_INTERP_INSTR_END;
     683             : 
     684             :   /* 0x80-0x8f ********************************************************/
     685             : 
     686             :   FD_VM_INTERP_INSTR_BEGIN(0x84) /* FD_SBPF_OP_NEG */
     687           8 :     reg[ dst ] = (ulong)( -(uint)reg_dst );
     688           8 :   FD_VM_INTERP_INSTR_END;
     689             : 
     690       98367 :   FD_VM_INTERP_BRANCH_BEGIN(0x85) { /* FD_SBPF_OP_CALL_IMM */
     691             : 
     692       98371 :     fd_sbpf_syscalls_t const * syscall = imm!=fd_sbpf_syscalls_key_null() ? fd_sbpf_syscalls_query_const( syscalls, (ulong)imm, NULL ) : NULL;
     693       98367 :     if( FD_UNLIKELY( !syscall ) ) { /* Optimize for the syscall case */
     694             : 
     695             :       /* Note we do the stack push before updating the pc(*). This implies
     696             :        that the call stack frame gets allocated _before_ checking if the
     697             :        call target is valid.  It would be fine to switch the order
     698             :        though such would change the precise faulting semantics of
     699             :        sigtextbr and sigstack.
     700             : 
     701             :        (*)but after checking calldests, see point below. */
     702             : 
     703             :       /* Agave's order of checks
     704             :          (https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L486):
     705             :           1. Lookup imm hash in FunctionRegistry (calldests_test is our equivalent)
     706             :           2. Push stack frame
     707             :           3. Check PC
     708             :           4. Update PC
     709             : 
     710             :           Following this precisely is impossible as our PC check also
     711             :           serves as a bounds check for the calldests_test call. So we
     712             :           have to perform step 3 before step 1. The following
     713             :           is a best-effort implementation that should match the VM state
     714             :           in all ways except error code. */
     715             : 
     716             :       /* Special case to handle entrypoint.
     717             :          ebpf::hash_symbol_name(b"entrypoint") = 0xb00c380, and
     718             :          fd_pchash_inverse( 0xb00c380U ) = 0x71e3cf81U */
     719       90809 :       if( FD_UNLIKELY( imm==0x71e3cf81U ) ) {
     720           0 :         FD_VM_INTERP_STACK_PUSH;
     721           0 :         pc = entry_pc - 1;
     722       90809 :       } else {
     723       90809 :         ulong target_pc = (ulong)fd_pchash_inverse( imm );
     724       90809 :         if( FD_UNLIKELY( target_pc>=text_cnt ) ) {
     725           6 :           goto sigillbr; /* different return between 0x85 and 0x8d */
     726           6 :         }
     727       90803 :         if( FD_UNLIKELY( !fd_sbpf_calldests_test( calldests, target_pc ) ) ) {
     728           0 :           goto sigillbr;
     729           0 :         }
     730      181605 :         FD_VM_INTERP_STACK_PUSH;
     731      181605 :         pc = target_pc - 1;
     732      181605 :       }
     733             : 
     734       90809 :     } else {
     735             : 
     736        7558 :       FD_VM_INTERP_SYSCALL_EXEC;
     737             : 
     738        3996 :     }
     739       98367 :   } FD_VM_INTERP_BRANCH_END;
     740             : 
     741             :   FD_VM_INTERP_INSTR_BEGIN(0x86) /* FD_SBPF_OP_LMUL32_IMM */
     742           2 :     reg[ dst ] = (ulong)( (uint)reg_dst * imm );
     743           2 :   FD_VM_INTERP_INSTR_END;
     744             : 
     745         148 :   FD_VM_INTERP_INSTR_BEGIN(0x87) { /* FD_SBPF_OP_STW */
     746         148 :     ulong vaddr   = reg_dst + offset;
     747         148 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL );
     748         148 :     int   sigsegv = !haddr;
     749         148 :     if( FD_UNLIKELY( sigsegv ) ) {
     750           5 :       vm->segv_vaddr       = vaddr;
     751           5 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     752           5 :       vm->segv_access_len  = 4UL;
     753           5 :       goto sigsegv;
     754           5 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     755         143 :     fd_vm_mem_st_4( haddr, imm );
     756         143 :   } FD_VM_INTERP_INSTR_END;
     757             : 
     758             :   FD_VM_INTERP_INSTR_BEGIN(0x87depr) /* FD_SBPF_OP_NEG64 deprecated */
     759       58738 :     reg[ dst ] = -reg_dst;
     760       58738 :   FD_VM_INTERP_INSTR_END;
     761             : 
     762        4674 :   FD_VM_INTERP_INSTR_BEGIN(0x8c) { /* FD_SBPF_OP_LDXW */
     763        4674 :     ulong vaddr   = reg_src + offset;
     764        4674 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_ld_sz, 0, 0UL );
     765        4674 :     int   sigsegv = !haddr;
     766        4674 :     if( FD_UNLIKELY( sigsegv ) ) {
     767           7 :       vm->segv_vaddr       = vaddr;
     768           7 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     769           7 :       vm->segv_access_len  = 4UL;
     770           7 :       goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     771           7 :     }
     772        4667 :     reg[ dst ] = fd_vm_mem_ld_4( haddr );
     773        4667 :   }
     774        4667 :   FD_VM_INTERP_INSTR_END;
     775             : 
     776        4667 :   FD_VM_INTERP_BRANCH_BEGIN(0x8d) { /* FD_SBPF_OP_CALL_REG */
     777             : 
     778        1189 :     FD_VM_INTERP_STACK_PUSH;
     779             : 
     780        1189 :     ulong vaddr = fd_sbpf_callx_uses_src_reg_enabled( sbpf_version ) ? reg_src : reg[ imm & 15U ];
     781             : 
     782             :     /* Notes: Agave checks region and target_pc before updating the pc.
     783             :        To match their state, we do the same, even though we could simply
     784             :        update the pc and let BRANCH_END fail.
     785             :        Also, Agave doesn't check alignment. */
     786             : 
     787        1189 :     ulong region = vaddr >> 32;
     788             :     /* ulong align  = vaddr & 7UL; */
     789        1189 :     ulong target_pc = ((vaddr & FD_VM_OFFSET_MASK) - vm->text_off) / 8UL;
     790        1189 :     if( FD_UNLIKELY( (region!=1UL) | (target_pc>=text_cnt) ) ) goto sigtextbr; /* Note: untaken branches don't consume BTB */
     791        1187 :     pc = target_pc - 1;
     792             : 
     793        1187 :   } FD_VM_INTERP_BRANCH_END;
     794             : 
     795             :   FD_VM_INTERP_INSTR_BEGIN(0x8e) /* FD_SBPF_OP_LMUL32_REG */
     796           1 :     reg[ dst ] = (ulong)( (uint)reg_dst * (uint)reg_src );
     797           1 :   FD_VM_INTERP_INSTR_END;
     798             : 
     799       39837 :   FD_VM_INTERP_INSTR_BEGIN(0x8f) { /* FD_SBPF_OP_STXW */
     800       39837 :     ulong vaddr    = reg_dst + offset;
     801       39837 :     ulong haddr    = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL );
     802       39837 :     int   sigsegv  = !haddr;
     803       39837 :     if( FD_UNLIKELY( sigsegv ) ) {
     804          16 :       vm->segv_vaddr       = vaddr;
     805          16 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     806          16 :       vm->segv_access_len  = 4UL;
     807          16 :       goto sigsegv;
     808          16 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     809       39821 :     fd_vm_mem_st_4( haddr, (uint)reg_src );
     810       39821 :   }
     811       39821 :   FD_VM_INTERP_INSTR_END;
     812             : 
     813             :   /* 0x90 - 0x9f ******************************************************/
     814             : 
     815             :   FD_VM_INTERP_INSTR_BEGIN(0x94) /* FD_SBPF_OP_MOD_IMM */
     816        2593 :     reg[ dst ] = (ulong)( (uint)reg_dst % imm );
     817        2593 :   FD_VM_INTERP_INSTR_END;
     818             : 
     819             :   FD_VM_INTERP_BRANCH_BEGIN(0x95) /* FD_SBPF_OP_EXIT */
     820       86021 :       /* Agave JIT VM exit implementation analysis below.
     821       86021 : 
     822       86021 :        Agave references:
     823       86021 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L503-L509
     824       86021 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L697-L702 */
     825       86021 :     if( FD_UNLIKELY( !frame_cnt ) ) goto sigexit; /* Exit program */
     826       84777 :     frame_cnt--;
     827       84777 :     reg[6]   = shadow[ frame_cnt ].r6;
     828       84777 :     reg[7]   = shadow[ frame_cnt ].r7;
     829       84777 :     reg[8]   = shadow[ frame_cnt ].r8;
     830       84777 :     reg[9]   = shadow[ frame_cnt ].r9;
     831       84777 :     reg[10]  = shadow[ frame_cnt ].r10;
     832       84777 :     pc       = shadow[ frame_cnt ].pc;
     833       84777 :   FD_VM_INTERP_BRANCH_END;
     834             : 
     835             :   FD_VM_INTERP_INSTR_BEGIN(0x96) /* FD_SBPF_OP_LMUL64_IMM */
     836           0 :     reg[ dst ] = reg_dst * (ulong)(long)(int)imm;
     837           0 :   FD_VM_INTERP_INSTR_END;
     838             : 
     839         348 :   FD_VM_INTERP_INSTR_BEGIN(0x97) { /* FD_SBPF_OP_STQ */
     840         348 :     ulong vaddr   = reg_dst + offset;
     841         348 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL );
     842         348 :     int   sigsegv = !haddr;
     843         348 :     if( FD_UNLIKELY( sigsegv ) ) {
     844           7 :       vm->segv_vaddr       = vaddr;
     845           7 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     846           7 :       vm->segv_access_len  = 8UL;
     847           7 :       goto sigsegv;
     848           7 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     849         341 :     fd_vm_mem_st_8( haddr, (ulong)(long)(int)imm );
     850         341 :   }
     851         341 :   FD_VM_INTERP_INSTR_END;
     852             : 
     853      671392 :   FD_VM_INTERP_INSTR_BEGIN(0x9c) { /* FD_SBPF_OP_LDXQ */
     854      671392 :     ulong vaddr   = reg_src + offset;
     855      671392 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_ld_sz, 0, 0UL );
     856      671392 :     int   sigsegv = !haddr;
     857      671392 :     if( FD_UNLIKELY( sigsegv ) ) {
     858          84 :       vm->segv_vaddr       = vaddr;
     859          84 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_LD;
     860          84 :       vm->segv_access_len  = 8UL;
     861          84 :       goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     862          84 :     }
     863      671308 :     reg[ dst ] = fd_vm_mem_ld_8( haddr );
     864      671308 :   }
     865      671308 :   FD_VM_INTERP_INSTR_END;
     866             : 
     867             :   FD_VM_INTERP_INSTR_BEGIN(0x9e) /* FD_SBPF_OP_LMUL64_REG */
     868           4 :     reg[ dst ] = reg_dst * reg_src;
     869           4 :   FD_VM_INTERP_INSTR_END;
     870             : 
     871     1037084 :   FD_VM_INTERP_INSTR_BEGIN(0x9f) { /* FD_SBPF_OP_STXQ */
     872     1037084 :     ulong vaddr   = reg_dst + offset;
     873     1037084 :     ulong haddr   = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL );
     874     1037084 :     int   sigsegv = !haddr;
     875     1037084 :     if( FD_UNLIKELY( sigsegv ) ) {
     876          14 :       vm->segv_vaddr       = vaddr;
     877          14 :       vm->segv_access_type = FD_VM_ACCESS_TYPE_ST;
     878          14 :       vm->segv_access_len  = 8UL;
     879          14 :       goto sigsegv;
     880          14 :     } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     881     1037070 :     fd_vm_mem_st_8( haddr, reg_src );
     882     1037070 :   }
     883     1037070 :   FD_VM_INTERP_INSTR_END;
     884             : 
     885             :   FD_VM_INTERP_INSTR_BEGIN(0x97depr) /* FD_SBPF_OP_MOD64_IMM */
     886       56173 :     reg[ dst ] = reg_dst % (ulong)(long)(int)imm;
     887       56173 :   FD_VM_INTERP_INSTR_END;
     888             : 
     889             :   FD_VM_INTERP_INSTR_BEGIN(0x9cdepr) /* FD_SBPF_OP_MOD_REG */
     890       35714 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     891       35714 :     reg[ dst ] = (ulong)( ((uint)reg_dst % (uint)reg_src) );
     892       35714 :   FD_VM_INTERP_INSTR_END;
     893             : 
     894             :   FD_VM_INTERP_INSTR_BEGIN(0x9fdepr) /* FD_SBPF_OP_MOD64_REG */
     895       36260 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     896       36259 :     reg[ dst ] = reg_dst % reg_src;
     897       36259 :   FD_VM_INTERP_INSTR_END;
     898             : 
     899             :   /* 0xa0 - 0xaf ******************************************************/
     900             : 
     901             :   FD_VM_INTERP_INSTR_BEGIN(0xa4) /* FD_SBPF_OP_XOR_IMM */
     902      120121 :     reg[ dst ] = (ulong)( (uint)reg_dst ^ imm );
     903      120121 :   FD_VM_INTERP_INSTR_END;
     904             : 
     905             :   FD_VM_INTERP_BRANCH_BEGIN(0xa5) /* FD_SBPF_OP_JLT_IMM */
     906      132491 :     pc += fd_ulong_if( reg_dst<(ulong)(long)(int)imm, offset, 0UL );
     907      132491 :   FD_VM_INTERP_BRANCH_END;
     908             : 
     909             :   FD_VM_INTERP_INSTR_BEGIN(0xa7) /* FD_SBPF_OP_XOR64_IMM */
     910        4269 :     reg[ dst ] = reg_dst ^ (ulong)(long)(int)imm;
     911        4269 :   FD_VM_INTERP_INSTR_END;
     912             : 
     913             :   FD_VM_INTERP_INSTR_BEGIN(0xac) /* FD_SBPF_OP_XOR_REG */
     914       24851 :     reg[ dst ] = (ulong)(uint)( reg_dst ^ reg_src );
     915       24851 :   FD_VM_INTERP_INSTR_END;
     916             : 
     917             :   FD_VM_INTERP_BRANCH_BEGIN(0xad) /* FD_SBPF_OP_JLT_REG */
     918         892 :     pc += fd_ulong_if( reg_dst<reg_src, offset, 0UL );
     919         892 :   FD_VM_INTERP_BRANCH_END;
     920             : 
     921             :   FD_VM_INTERP_INSTR_BEGIN(0xaf) /* FD_SBPF_OP_XOR64_REG */
     922      291452 :     reg[ dst ] = reg_dst ^ reg_src;
     923      291452 :   FD_VM_INTERP_INSTR_END;
     924             : 
     925             :   /* 0xb0 - 0xbf ******************************************************/
     926             : 
     927             :   FD_VM_INTERP_INSTR_BEGIN(0xb4) /* FD_SBPF_OP_MOV_IMM */
     928      374545 :     reg[ dst ] = (ulong)imm;
     929      374545 :   FD_VM_INTERP_INSTR_END;
     930             : 
     931             :   FD_VM_INTERP_BRANCH_BEGIN(0xb5) /* FD_SBPF_OP_JLE_IMM */
     932      379531 :     pc += fd_ulong_if( reg_dst<=(ulong)(long)(int)imm, offset, 0UL );
     933      379531 :   FD_VM_INTERP_BRANCH_END;
     934             : 
     935             :   FD_VM_INTERP_INSTR_BEGIN(0xb6) /* FD_SBPF_OP_SHMUL64_IMM */
     936           2 :     reg[ dst ] = (ulong)(( (int128)(long)reg_dst * (int128)(long)(int)imm ) >> 64 );
     937           2 :   FD_VM_INTERP_INSTR_END;
     938             : 
     939             :   FD_VM_INTERP_INSTR_BEGIN(0xb7) /* FD_SBPF_OP_MOV64_IMM */
     940     1017453 :     reg[ dst ] = (ulong)(long)(int)imm;
     941     1017453 :   FD_VM_INTERP_INSTR_END;
     942             : 
     943             :   FD_VM_INTERP_INSTR_BEGIN(0xbc) /* FD_SBPF_OP_MOV_REG */
     944           1 :     reg[ dst ] = (ulong)(long)(int)reg_src;
     945           1 :   FD_VM_INTERP_INSTR_END;
     946             : 
     947             :   FD_VM_INTERP_INSTR_BEGIN(0xbcdepr) /* FD_SBPF_OP_MOV_REG deprecated SIMD-1074 */
     948      212971 :     reg[ dst ] = (ulong)(uint)reg_src;
     949      212971 :   FD_VM_INTERP_INSTR_END;
     950             : 
     951             :   FD_VM_INTERP_BRANCH_BEGIN(0xbd) /* FD_SBPF_OP_JLE_REG */
     952          54 :     pc += fd_ulong_if( reg_dst<=reg_src, offset, 0UL );
     953          54 :   FD_VM_INTERP_BRANCH_END;
     954             : 
     955             :   FD_VM_INTERP_INSTR_BEGIN(0xbe) /* FD_SBPF_OP_SHMUL64_REG */
     956           0 :     reg[ dst ] = (ulong)(( (int128)(long)reg_dst * (int128)(long)reg_src ) >> 64 );
     957           0 :   FD_VM_INTERP_INSTR_END;
     958             : 
     959             :   FD_VM_INTERP_INSTR_BEGIN(0xbf) /* FD_SBPF_OP_MOV64_REG */
     960      687547 :     reg[ dst ] = reg_src;
     961      687547 :   FD_VM_INTERP_INSTR_END;
     962             : 
     963             :   /* 0xc0 - 0xcf ******************************************************/
     964             : 
     965             :   FD_VM_INTERP_INSTR_BEGIN(0xc4) /* FD_SBPF_OP_ARSH_IMM */
     966          16 :     reg[ dst ] = (ulong)(uint)( FD_RUST_INT_WRAPPING_SHR( (int)reg_dst, imm ) );
     967          16 :   FD_VM_INTERP_INSTR_END;
     968             : 
     969             :   FD_VM_INTERP_BRANCH_BEGIN(0xc5) /* FD_SBPF_OP_JSLT_IMM */ /* FIXME: CHECK IMM SIGN EXTENSION */
     970        2775 :     pc += fd_ulong_if( (long)reg_dst<(long)(int)imm, offset, 0UL );
     971        2775 :   FD_VM_INTERP_BRANCH_END;
     972             : 
     973             :   FD_VM_INTERP_INSTR_BEGIN(0xc6) /* FD_SBPF_OP_SDIV32_IMM */
     974           0 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)imm==-1) ) ) goto sigfpeof;
     975           0 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst / (int)imm );
     976           0 :   FD_VM_INTERP_INSTR_END;
     977             : 
     978             :   FD_VM_INTERP_INSTR_BEGIN(0xc7) /* FD_SBPF_OP_ARSH64_IMM */
     979        7266 :     reg[ dst ] = (ulong)( FD_RUST_LONG_WRAPPING_SHR( (long)reg_dst, imm ) );
     980        7266 :   FD_VM_INTERP_INSTR_END;
     981             : 
     982             :   FD_VM_INTERP_INSTR_BEGIN(0xcc) /* FD_SBPF_OP_ARSH_REG */
     983       22459 :     reg[ dst ] = (ulong)(uint)( FD_RUST_INT_WRAPPING_SHR( (int)reg_dst, (uint)reg_src ) );
     984       22459 :   FD_VM_INTERP_INSTR_END;
     985             : 
     986             :   FD_VM_INTERP_BRANCH_BEGIN(0xcd) /* FD_SBPF_OP_JSLT_REG */
     987      607015 :     pc += fd_ulong_if( (long)reg_dst<(long)reg_src, offset, 0UL );
     988      607015 :   FD_VM_INTERP_BRANCH_END;
     989             : 
     990             :   FD_VM_INTERP_INSTR_BEGIN(0xce) /* FD_SBPF_OP_SDIV32_REG */
     991           0 :     if( FD_UNLIKELY( !(int)reg_src ) ) goto sigfpe;
     992           0 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)reg_src==-1) ) ) goto sigfpeof;
     993           0 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst / (int)reg_src );
     994           0 :   FD_VM_INTERP_INSTR_END;
     995             : 
     996             :   FD_VM_INTERP_INSTR_BEGIN(0xcf) /* FD_SBPF_OP_ARSH64_REG */
     997          13 :     reg[ dst ] = (ulong)( FD_RUST_LONG_WRAPPING_SHR( (long)reg_dst, reg_src ) );
     998          13 :   FD_VM_INTERP_INSTR_END;
     999             : 
    1000             :   /* 0xd0 - 0xdf ******************************************************/
    1001             : 
    1002             :   FD_VM_INTERP_INSTR_BEGIN(0xd4) /* FD_SBPF_OP_END_LE */
    1003      112065 :     switch( imm ) {
    1004           3 :     case 16U: reg[ dst ] = (ushort)reg_dst; break;
    1005       36231 :     case 32U: reg[ dst ] = (uint)  reg_dst; break;
    1006       75831 :     case 64U:                               break;
    1007           0 :     default: goto siginv;
    1008      112065 :     }
    1009      112065 :   FD_VM_INTERP_INSTR_END;
    1010             : 
    1011             :   FD_VM_INTERP_BRANCH_BEGIN(0xd5) /* FD_SBPF_OP_JSLE_IMM */
    1012        4983 :     pc += fd_ulong_if( (long)reg_dst<=(long)(int)imm, offset, 0UL );
    1013        4983 :   FD_VM_INTERP_BRANCH_END;
    1014             : 
    1015             :   FD_VM_INTERP_INSTR_BEGIN(0xd6) /* FD_SBPF_OP_SDIV64_IMM */
    1016           0 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)(int)imm==-1L) ) ) goto sigfpeof;
    1017           0 :     reg[ dst ] = (ulong)( (long)reg_dst / (long)(int)imm );
    1018           0 :   FD_VM_INTERP_INSTR_END;
    1019             : 
    1020             :   FD_VM_INTERP_INSTR_BEGIN(0xdc) /* FD_SBPF_OP_END_BE */
    1021      143615 :     switch( imm ) {
    1022       33121 :     case 16U: reg[ dst ] = (ulong)fd_ushort_bswap( (ushort)reg_dst ); break;
    1023       66150 :     case 32U: reg[ dst ] = (ulong)fd_uint_bswap  ( (uint)  reg_dst ); break;
    1024       44344 :     case 64U: reg[ dst ] =        fd_ulong_bswap ( (ulong) reg_dst ); break;
    1025           0 :     default: goto siginv;
    1026      143615 :     }
    1027      143615 :   FD_VM_INTERP_INSTR_END;
    1028             : 
    1029             :   FD_VM_INTERP_BRANCH_BEGIN(0xdd) /* FD_SBPF_OP_JSLE_REG */
    1030      369526 :     pc += fd_ulong_if( (long)reg_dst<=(long)reg_src, offset, 0UL );
    1031      369526 :   FD_VM_INTERP_BRANCH_END;
    1032             : 
    1033             :   FD_VM_INTERP_INSTR_BEGIN(0xde) /* FD_SBPF_OP_SDIV64_REG */
    1034           0 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
    1035           0 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)reg_src==-1L) ) ) goto sigfpeof;
    1036           0 :     reg[ dst ] = (ulong)( (long)reg_dst / (long)reg_src );
    1037           0 :   FD_VM_INTERP_INSTR_END;
    1038             : 
    1039             :   /* 0xe0 - 0xef ******************************************************/
    1040             : 
    1041             :   FD_VM_INTERP_INSTR_BEGIN(0xe6) /* FD_SBPF_OP_SREM32_IMM */
    1042           0 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)imm==-1) ) ) goto sigfpeof;
    1043           0 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst % (int)imm );
    1044           0 :   FD_VM_INTERP_INSTR_END;
    1045             : 
    1046             :   FD_VM_INTERP_INSTR_BEGIN(0xee) /* FD_SBPF_OP_SREM32_REG */
    1047           0 :     if( FD_UNLIKELY( !(int)reg_src ) ) goto sigfpe;
    1048           0 :     if( FD_UNLIKELY( ((int)reg_dst==INT_MIN) & ((int)reg_src==-1) ) ) goto sigfpeof;
    1049           0 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst % (int)reg_src );
    1050           0 :   FD_VM_INTERP_INSTR_END;
    1051             : 
    1052             :   /* 0xf0 - 0xff ******************************************************/
    1053             : 
    1054             :   FD_VM_INTERP_INSTR_BEGIN(0xf6) /* FD_SBPF_OP_SREM64_IMM */
    1055           1 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)(int)imm==-1L) ) ) goto sigfpeof;
    1056           1 :     reg[ dst ] = (ulong)( (long)reg_dst % (long)(int)imm );
    1057           1 :   FD_VM_INTERP_INSTR_END;
    1058             : 
    1059             :   FD_VM_INTERP_INSTR_BEGIN(0xf7) /* FD_SBPF_OP_HOR64 */
    1060      139452 :     reg[ dst ] = reg_dst | (((ulong)imm) << 32);
    1061      139452 :   FD_VM_INTERP_INSTR_END;
    1062             : 
    1063             :   FD_VM_INTERP_INSTR_BEGIN(0xfe) /* FD_SBPF_OP_SREM64_REG */
    1064           0 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
    1065           0 :     if( FD_UNLIKELY( ((long)reg_dst==LONG_MIN) & ((long)reg_src==-1L) ) ) goto sigfpeof;
    1066           0 :     reg[ dst ] = (ulong)( (long)reg_dst % (long)reg_src );
    1067           0 :   FD_VM_INTERP_INSTR_END;
    1068             : 
    1069             :   /* FIXME: sigbus/sigrdonly are mapped to sigsegv for simplicity
    1070             :      currently but could be enabled if desired. */
    1071             : 
    1072             :   /* Note: sigtextbr is for sigtext errors that occur on branching
    1073             :      instructions (i.e., prefixed with FD_VM_INTERP_BRANCH_BEGIN).
    1074             :      We skip a repeat ic accumulation in FD_VM_INTERP_FAULT */
    1075             : 
    1076             :   /* FD_VM_INTERP_FAULT accumulates to ic and cu all non-faulting
    1077             :      instructions preceeding a fault generated by a non-branching
    1078             :      instruction.  When a non-branching instruction faults, pc is at the
    1079             :      instruction and the number of non-branching instructions that have
    1080             :      not yet been reflected in ic and cu is:
    1081             : 
    1082             :        pc - pc0 + 1 - ic_correction
    1083             : 
    1084             :      as per the accounting described above. +1 to include the faulting
    1085             :      instruction itself.
    1086             : 
    1087             :      Note that, for a sigtext caused by a branch instruction, pc0==pc
    1088             :      (from the BRANCH_END) and ic_correction==0 (from the BRANCH_BEGIN)
    1089             :      such that the below does not change the already current values in
    1090             :      ic and cu.  Thus it also "does the right thing" in both the
    1091             :      non-branching and branching cases for sigtext.  The same applies to
    1092             :      sigsplit. */
    1093             : 
    1094           0 : #define FD_VM_INTERP_FAULT                                                                 \
    1095         207 :   ic_correction = pc - pc0 + 1UL - ic_correction;                                          \
    1096         207 :   ic += ic_correction;                                                                     \
    1097         207 :   if ( FD_UNLIKELY( ic_correction > cu ) ) err = FD_VM_ERR_EBPF_EXCEEDED_MAX_INSTRUCTIONS; \
    1098         207 :   cu -= fd_ulong_min( ic_correction, cu )
    1099             : 
    1100           1 : sigtext:     err = FD_VM_ERR_EBPF_EXECUTION_OVERRUN;                                     FD_VM_INTERP_FAULT;                    goto interp_halt;
    1101           2 : sigtextbr:   err = FD_VM_ERR_EBPF_CALL_OUTSIDE_TEXT_SEGMENT;                             /* ic current */     /* cu current */  goto interp_halt;
    1102           1 : sigstack:    err = FD_VM_ERR_EBPF_CALL_DEPTH_EXCEEDED;                                   /* ic current */     /* cu current */  goto interp_halt;
    1103          22 : sigill:      err = FD_VM_ERR_EBPF_UNSUPPORTED_INSTRUCTION;                               FD_VM_INTERP_FAULT;                    goto interp_halt;
    1104           6 : sigillbr:    err = FD_VM_ERR_EBPF_UNSUPPORTED_INSTRUCTION;                               /* ic current */     /* cu current */  goto interp_halt;
    1105           0 : siginv:      err = FD_VM_ERR_EBPF_INVALID_INSTRUCTION;                                   /* ic current */     /* cu current */  goto interp_halt;
    1106         183 : sigsegv:     err = fd_vm_generate_access_violation( vm->segv_vaddr, vm->sbpf_version );  FD_VM_INTERP_FAULT;                    goto interp_halt;
    1107        1332 : sigcost:     err = FD_VM_ERR_EBPF_EXCEEDED_MAX_INSTRUCTIONS;                             /* ic current */     cu = 0UL;         goto interp_halt;
    1108        3562 : sigsyscall:  err = FD_VM_ERR_EBPF_SYSCALL_ERROR;                                         /* ic current */     /* cu current */  goto interp_halt;
    1109           1 : sigfpe:      err = FD_VM_ERR_EBPF_DIVIDE_BY_ZERO;                                        FD_VM_INTERP_FAULT;                    goto interp_halt;
    1110           0 : sigfpeof:    err = FD_VM_ERR_EBPF_DIVIDE_OVERFLOW;                                       FD_VM_INTERP_FAULT;                    goto interp_halt;
    1111        1244 : sigexit:     /* err current */                                                           /* ic current */     /* cu current */  goto interp_halt;
    1112             : 
    1113           0 : #undef FD_VM_INTERP_FAULT
    1114             : 
    1115        6354 : interp_halt:
    1116             : 
    1117             :   /* Pack the unpacked execution state into vm to give a precise view of
    1118             :      the execution when the vm halted. */
    1119             : 
    1120        6354 :   vm->pc        = pc;
    1121        6354 :   vm->ic        = ic;
    1122        6354 :   vm->cu        = cu;
    1123        6354 :   vm->frame_cnt = frame_cnt;
    1124             : 
    1125        6354 : # undef FD_VM_INTERP_STACK_PUSH
    1126             : 
    1127        6354 : # undef FD_VM_INTERP_BRANCH_END
    1128        6354 : # undef FD_VM_INTERP_BRANCH_BEGIN
    1129             : 
    1130        6354 : # undef FD_VM_INTERP_INSTR_END
    1131        6354 : # undef FD_VM_INTERP_INSTR_BEGIN
    1132        6354 : # undef FD_VM_INTERP_INSTR_EXEC
    1133             : 
    1134        6354 : # if defined(__clang__)
    1135        6354 : # pragma clang diagnostic pop
    1136        6354 : # endif
    1137             : 
    1138        6354 : # if defined(__GNUC__)
    1139        6354 : # pragma GCC diagnostic pop
    1140        6354 : # endif
    1141             : 
    1142             : /*   Agave/JIT CU model analysis (and why we are conformant!):
    1143             : 
    1144             :      The Agave JIT employs a similar strategy of accumulating instructions
    1145             :      in a linear run and processing them at the start of a new linear
    1146             :      run/branch (side note: the JIT treats the LDQ instruction as a "branch"
    1147             :      that jumps pc + 2).
    1148             : 
    1149             :      In what is assumed to be an act of register conservation, the JIT
    1150             :      uses a catch-all "instruction meter" (IM) register (REGISTER_INSTRUCTION_METER)
    1151             :      that represents two different interpretations of the question
    1152             :      "how many instructions can I execute?".
    1153             : 
    1154             :      The IM, depending on where we are in the execution, either represents:
    1155             :         1. IM => The number of instructions remaining before exhausting CU
    1156             :         budget. This is analagous to vm->cu in our interpreter.
    1157             :         2. IM' => The last pc you can execute in the current linear run before
    1158             :         exhausting CU budget.  Mathematically, IM' = IM + pc0
    1159             :         where pc0, just like our definition, is the start of the linear run.
    1160             : 
    1161             :         Note: IM' can go past the actual basic block/segment. In-fact,
    1162             :         it typically does, and implies we can execute the full block without
    1163             :         exhausting CU budget (reminder that LDQ is treated as a branch).
    1164             : 
    1165             :       By default, the IM' form is used during execution. The IM form is used:
    1166             :         - (transiently) during the processing of a branch instruction
    1167             :         - in post-VM cleanup (updates EbpfVm::previous_instruction_meter).
    1168             : 
    1169             :       When a branch instruction is encountered, the JIT checks
    1170             :       for CU exhaustion with pc > IM', and throws an exception if so. This is valid,
    1171             :       because as described above, IM' is the largest PC you can reach.
    1172             : 
    1173             :       If we haven't exhausted our CU limit, it updates IM':
    1174             :         1. IM = IM' - (pc + 1)  # Note that IM' at this point is IM + pc0',
    1175             :                                 # where pc0' is the start of the current linear run.
    1176             :         2. IM' = IM + pc0       # pc0 is the start of the new linear run (typically the target pc)
    1177             : 
    1178             :       Code (that does the above in one ALU instruction):
    1179             :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L891
    1180             : 
    1181             : 
    1182             :       ### How does this relate to our interpreter?
    1183             : 
    1184             :       This process is similar to FD_VM_INTERP_BRANCH_BEGIN.
    1185             :       We just deal with the IM form throughout (with vm->cu and ic_correction).
    1186             :       If we break down step 1 from above with what we know about IM and IM',
    1187             :       we get the following:
    1188             :         1. IM = IM' - (pc + 1)
    1189             :            IM = (IM + pc0') - (pc + 1)
    1190             :            IM = IM + (pc0' - (pc + 1))
    1191             :            IM = IM - ((pc + 1) - pc0')
    1192             :            IM = IM - ic_correction
    1193             :       Here, ((pc + 1) - pc0') is the number of instrutions executed in the current
    1194             :       linear run. This is the same as our ic_correction(*) in FD_VM_INTERP_BRANCH_BEGIN.
    1195             : 
    1196             :       If we replace IM with cu, this effectively becomes the
    1197             :            cu -= ic_correction
    1198             :       line in FD_VM_INTERP_BRANCH_BEGIN.
    1199             : 
    1200             :       (*) Note: ic_correction (also) takes two forms. It is either the instruction
    1201             :       accumulator or the number of instructions executed in the current linear run.
    1202             :       It (transiently) takes the latter form during FD_VM_INTERP_BRANCH_BEGIN and
    1203             :       FD_VM_INTERP_FAULT, and the former form otherwise.
    1204             : */
    1205             : 
    1206             : /* (WIP) Precise faulting and the Agave JIT:
    1207             : 
    1208             :    Since the cost model is a part of consensus, we need to conform with the Agave/JIT
    1209             :    cost model 1:1. To achieve that, our faulting model also needs to match precisely. This
    1210             :    section covers the various faults that the respective VMs implement and how they match.
    1211             : 
    1212             :    # Normal VM exit (sigexit):
    1213             :    VM exit instruction entrypoint: https://github.com/solana-labs/rbpf/blob/12237895305ab38514be865ebed6268553e4f589/src/jit.rs#L698-L708
    1214             : 
    1215             :    Pseudocode (with FD semantics):
    1216             :    ```
    1217             :     # pc is at the exit instruction
    1218             :     # pc0 is the start of the current linear run
    1219             :     if (frame_cnt == 0) {
    1220             :         goto sigexit;
    1221             :     }
    1222             :     ...
    1223             : 
    1224             :     sigexit:
    1225             :     if IM' <= pc {
    1226             :       goto sigcost;
    1227             :     } else {
    1228             :       goto interp_halt;
    1229             :     }
    1230             :     ```
    1231             : 
    1232             :     Breaking down the IM' < pc check:
    1233             :     - IM' = IM + pc0
    1234             :     - pc  = ic + pc0, where (ic + 1) is the number of instructions executed in the current linear run
    1235             : 
    1236             :     IM' <= pc
    1237             :     IM + pc0 <= ic + pc0
    1238             :     IM <= ic
    1239             :     IM <= pc - pc0
    1240             :     IM < pc - pc0 + 1 # all unsigned integers
    1241             :     IM < ic_correction
    1242             : 
    1243             :     This is analagous to the ic_correction>cu check in VM_INTERP_BRANCH_BEGIN.
    1244             : 
    1245             :    # (TODO) Text Overrun (sigtext/sigsplit):
    1246             : 
    1247             : */

Generated by: LCOV version 1.14