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

Generated by: LCOV version 1.14