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

Generated by: LCOV version 1.14