LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_interp_core.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 436 445 98.0 %
Date: 2024-11-13 11:58:15 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        3660 : # if defined(__GNUC__) /* -Wpedantic rejects labels as values and rejects goto *expr */
      12        3660 : # pragma GCC diagnostic push
      13        3660 : # pragma GCC diagnostic ignored "-Wpedantic"
      14        3660 : # endif
      15             : 
      16        3660 : # if defined(__clang__) /* Clang is differently picky about labels as values and goto *expr */
      17        3660 : # pragma clang diagnostic push
      18        3660 : # pragma clang diagnostic ignored "-Wpedantic"
      19        3660 : # pragma clang diagnostic ignored "-Wgnu-label-as-value"
      20        3660 : # endif
      21             : 
      22             :   /* Include the jump table */
      23             : 
      24        3660 : # include "fd_vm_interp_jump_table.c"
      25             : 
      26             :   /* Unpack the VM state */
      27             : 
      28        3660 :   ulong pc        = vm->pc;
      29        3660 :   ulong ic        = vm->ic;
      30        3660 :   ulong cu        = vm->cu;
      31        3660 :   ulong frame_cnt = vm->frame_cnt;
      32             : 
      33             :   /* FD_VM_INTERP_INSTR_EXEC loads the first word of the instruction at
      34             :      pc, parses it, fetches the associated register values and then
      35             :      jumps to the code that executes the instruction.  On normal
      36             :      instruction execution, the pc will be updated and
      37             :      FD_VM_INTERP_INSTR_EXEC will be invoked again to do the next
      38             :      instruction.  After a normal halt, this will branch to interp_halt.
      39             :      Otherwise, it will branch to the appropriate normal termination. */
      40             : 
      41        3660 :   ulong instr;
      42        3660 :   ulong opcode;
      43        3660 :   ulong dst;
      44        3660 :   ulong src;
      45        3660 :   short offset;
      46        3660 :   uint  imm;
      47        3660 :   ulong reg_dst;
      48        3660 :   ulong reg_src;
      49             : 
      50             : /* These mimic the exact Rust semantics for wrapping_shl and wrapping_shr. */
      51             : 
      52             : /* u64::wrapping_shl: a.unchecked_shl(b & (64 - 1))
      53             :    
      54             :    https://doc.rust-lang.org/std/primitive.u64.html#method.wrapping_shl
      55             :  */
      56      141627 : #define FD_RUST_ULONG_WRAPPING_SHL( a, b ) (a << ( b & ( 63 ) ))
      57             : 
      58             : /* u64::wrapping_shr: a.unchecked_shr(b & (64 - 1))
      59             :    
      60             :    https://doc.rust-lang.org/std/primitive.u64.html#method.wrapping_shr
      61             :  */
      62      205515 : #define FD_RUST_ULONG_WRAPPING_SHR( a, b ) (a >> ( b & ( 63 ) ))
      63             : 
      64             : /* u32::wrapping_shl: a.unchecked_shl(b & (32 - 1))
      65             :    
      66             :    https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_shl
      67             :  */
      68        3660 : #define FD_RUST_UINT_WRAPPING_SHL( a, b ) (a << ( b & ( 31 ) ))
      69             : 
      70             : /* u32::wrapping_shr: a.unchecked_shr(b & (32 - 1))
      71             :    
      72             :    https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_shr
      73             :  */
      74        3660 : #define FD_RUST_UINT_WRAPPING_SHR( a, b ) (a >> ( b & ( 31 ) ))
      75             : 
      76             : 
      77        3660 : # define FD_VM_INTERP_INSTR_EXEC                                                                 \
      78     5799753 :   if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigtext; /* Note: untaken branches don't consume BTB */ \
      79     5799753 :   instr   = text[ pc ];                  /* Guaranteed in-bounds */                              \
      80     5799636 :   opcode  = fd_vm_instr_opcode( instr ); /* in [0,256) even if malformed */                      \
      81     5799636 :   dst     = fd_vm_instr_dst   ( instr ); /* in [0, 16) even if malformed */                      \
      82     5799636 :   src     = fd_vm_instr_src   ( instr ); /* in [0, 16) even if malformed */                      \
      83     5799636 :   offset  = fd_vm_instr_offset( instr ); /* in [-2^15,2^15) even if malformed */                 \
      84     5799636 :   imm     = fd_vm_instr_imm   ( instr ); /* in [0,2^32) even if malformed */                     \
      85     5799636 :   reg_dst = reg[ dst ];                  /* Guaranteed in-bounds */                              \
      86     5799636 :   reg_src = reg[ src ];                  /* Guaranteed in-bounds */                              \
      87     5799636 :   goto *interp_jump_table[ opcode ]      /* Guaranteed in-bounds */
      88             : 
      89             :   /* FD_VM_INTERP_INSTR_BEGIN / FD_VM_INTERP_INSTR_END bracket opcode's
      90             :      implementation for an opcode that does not branch.  On entry, the
      91             :      instruction word has been unpacked into dst / src / offset / imm
      92             :      and reg[dst] / reg[src] has been prefetched into reg_dst / reg_src. */
      93             : 
      94     4763745 : # define FD_VM_INTERP_INSTR_BEGIN(opcode) interp_##opcode:
      95             : 
      96             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~0.3% faster in some benchmarks, slower in others but more code footprint */
      97     4763403 : # define FD_VM_INTERP_INSTR_END pc++; FD_VM_INTERP_INSTR_EXEC
      98             : # else /* Use this version when tracing or optimizing code footprint */
      99           0 : # define FD_VM_INTERP_INSTR_END pc++; goto interp_exec
     100             : # endif
     101             : 
     102             :   /* Instead of doing a lot of compute budget calcs and tests every
     103             :      instruction, we note that the program counter increases
     104             :      monotonically after a branch (or a program start) until the next
     105             :      branch (or program termination).  We save the program counter of
     106             :      the start of such a segment in pc0.  Whenever we encounter a branch
     107             :      (or a program termination) at pc, we know we processed pc-pc0+1
     108             :      text words (including the text word for the branch instruction
     109             :      itself as all branch instructions are single word).
     110             : 
     111             :      Each instruction costs 1 cu (syscalls can cost extra on top of
     112             :      this that is accounted separately in CALL_IMM below).  Since there
     113             :      could have been multiword instructions in this segment, at start of
     114             :      such a segment, we zero out the accumulator ic_correction and have
     115             :      every multiword instruction in the segment accumulate the number of
     116             :      extra text words it has to this variable.  (Sigh ... it would be a
     117             :      lot simpler to bill based on text words processed but this would be
     118             :      very difficult to make this protocol change at this point.)
     119             : 
     120             :      When we encounter a branch at pc, the number of instructions
     121             :      processed (and thus the number of compute units to bill for that
     122             :      segment) is thus:
     123             : 
     124             :        pc - pc0 + 1 - ic_correction
     125             : 
     126             :      IMPORTANT SAFETY TIP!  This implies the worst case interval before
     127             :      checking the cu budget is the worst case text_cnt.  But since all
     128             :      such instructions are cheap 1 cu instructions and processed fast
     129             :      and text max is limited in size, this should be acceptable in
     130             :      practice.  FIXME: DOUBLE CHECK THE MATH ABOVE AGAINST PROTOCOL
     131             :      LIMITS. */
     132             : 
     133        3660 :   ulong pc0           = pc;
     134        3660 :   ulong ic_correction = 0UL;
     135             : 
     136        3660 : # define FD_VM_INTERP_BRANCH_BEGIN(opcode)                                                              \
     137     1035888 :   interp_##opcode:                                                                                      \
     138             :     /* Bill linear text segment and this branch instruction as per the above */                         \
     139     1035888 :     ic_correction = pc - pc0 + 1UL - ic_correction;                                                     \
     140     1035888 :     ic += ic_correction;                                                                                \
     141     1035888 :     if( FD_UNLIKELY( ic_correction>cu ) ) goto sigcost; /* Note: untaken branches don't consume BTB */  \
     142     1035888 :     cu -= ic_correction;                                                                                \
     143             :     /* At this point, cu>=0 */                                                                          \
     144     1035543 :     ic_correction = 0UL;
     145             : 
     146             :   /* FIXME: debatable if it is better to do pc++ here or have the
     147             :      instruction implementations do it in their code path. */
     148             : 
     149             : # ifndef FD_VM_INTERP_EXE_TRACING_ENABLED /* Non-tracing path only, ~4% faster in some benchmarks, slower in others but more code footprint */
     150             : # define FD_VM_INTERP_BRANCH_END               \
     151     1032576 :     pc++;                                      \
     152     1032576 :     pc0 = pc; /* Start a new linear segment */ \
     153     1032690 :     FD_VM_INTERP_INSTR_EXEC
     154             : # else /* Use this version when tracing or optimizing code footprint */
     155             : # define FD_VM_INTERP_BRANCH_END               \
     156           0 :     pc++;                                      \
     157           0 :     pc0 = pc; /* Start a new linear segment */ \
     158             :     /* FIXME: TEST sigsplit HERE */            \
     159           0 :     goto interp_exec
     160             : # endif
     161             : 
     162             :   /* FD_VM_INTERP_STACK_PUSH pushes reg[6:9] onto the shadow stack and
     163             :      advances reg[10] to a new user stack frame.  If there are no more
     164             :      stack frames available, will do a SIGSTACK. */
     165             : 
     166             :   /* FIXME: double check faulting is desired on stack overflow. */
     167             : 
     168             :   /* FIXME: a pre-belt-sanding FIXME implied the TLB should be updated
     169             :      to prevent byte code from accessing the stack outside its current
     170             :      stack frame.  But this would break the common practice of a
     171             :      function passing a pointer to something on its stack into a
     172             :      function that it calls:
     173             : 
     174             :        void foo( ... ) {
     175             :          ...
     176             :          int ret;
     177             :          bar( &ret );
     178             :          ...
     179             :        }
     180             : 
     181             :      So this probably shouldn't be done.  But, if it is in fact
     182             :      necessary, the TLB updates would be here and in pop. */
     183             : 
     184             :   /* FIXME: unvalidated code mucking with r10 */
     185             : 
     186        3660 : # define FD_VM_INTERP_STACK_PUSH                                                                            \
     187      132246 :   shadow[ frame_cnt ].r6 = reg[6];                                                                          \
     188      132246 :   shadow[ frame_cnt ].r7 = reg[7];                                                                          \
     189      132246 :   shadow[ frame_cnt ].r8 = reg[8];                                                                          \
     190      132246 :   shadow[ frame_cnt ].r9 = reg[9];                                                                          \
     191      132246 :   shadow[ frame_cnt ].pc = pc;                                                                              \
     192      132246 :   if( FD_UNLIKELY( ++frame_cnt>=frame_max ) ) goto sigstack; /* Note: untaken branches don't consume BTB */ \
     193      132246 :   reg[10] += vm->stack_frame_size
     194             : 
     195             :   /* We subtract the heap cost in the BPF loader */
     196             : 
     197        3660 :   goto interp_exec; /* Silly but to avoid unused label warning in some configurations */
     198        3660 : interp_exec:
     199             : 
     200             : # ifdef FD_VM_INTERP_EXE_TRACING_ENABLED
     201             :   /* Note: when tracing or optimizing for code footprint, all
     202             :      instruction execution starts here such that this is only point
     203             :      where exe tracing diagnostics are needed. */
     204           3 :   if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigtext;
     205           3 :   fd_vm_trace_event_exe( vm->trace, pc, ic + ( pc - pc0 - ic_correction ), cu, reg, vm->text + pc, vm->text_cnt - pc, ic_correction, frame_cnt );
     206           3 : # endif
     207             : 
     208        3660 :   FD_VM_INTERP_INSTR_EXEC;
     209             : 
     210             :   /* 0x00 - 0x0f ******************************************************/
     211             : 
     212             : /* FIXME: MORE THINKING AROUND LDQ HANDLING HERE (see below) */
     213             :   FD_VM_INTERP_INSTR_BEGIN(0x04) /* FD_SBPF_OP_ADD_IMM */
     214          36 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)imm );
     215          36 :   FD_VM_INTERP_INSTR_END;
     216             : 
     217             :   FD_VM_INTERP_BRANCH_BEGIN(0x05) /* FD_SBPF_OP_JA */
     218       74154 :     pc += (ulong)(long)offset;
     219       74154 :   FD_VM_INTERP_BRANCH_END;
     220             : 
     221             :   FD_VM_INTERP_INSTR_BEGIN(0x07) /* FD_SBPF_OP_ADD64_IMM */
     222      503868 :     reg[ dst ] = reg_dst + (ulong)(long)(int)imm;
     223      503868 :   FD_VM_INTERP_INSTR_END;
     224             : 
     225             :   FD_VM_INTERP_INSTR_BEGIN(0x0c) /* FD_SBPF_OP_ADD_REG */
     226          30 :     reg[ dst ] = (ulong)(long)( (int)reg_dst + (int)reg_src );
     227          30 :   FD_VM_INTERP_INSTR_END;
     228             : 
     229             :   FD_VM_INTERP_INSTR_BEGIN(0x0f) /* FD_SBPF_OP_ADD64_REG */
     230      425913 :     reg[ dst ] = reg_dst + reg_src;
     231      425913 :   FD_VM_INTERP_INSTR_END;
     232             : 
     233             :   /* 0x10 - 0x1f ******************************************************/
     234             : 
     235             :   FD_VM_INTERP_INSTR_BEGIN(0x14) /* FD_SBPF_OP_SUB_IMM */
     236          30 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)imm );
     237          30 :   FD_VM_INTERP_INSTR_END;
     238             : 
     239             :   FD_VM_INTERP_BRANCH_BEGIN(0x15) /* FD_SBPF_OP_JEQ_IMM */
     240       94215 :     pc += fd_ulong_if( reg_dst==(ulong)(long)(int)imm, (ulong)(long)offset, 0UL );
     241       94215 :   FD_VM_INTERP_BRANCH_END;
     242             : 
     243             :   FD_VM_INTERP_INSTR_BEGIN(0x17) /* FD_SBPF_OP_SUB64_IMM */
     244          36 :     reg[ dst ] = reg_dst - (ulong)(long)(int)imm;
     245          36 :   FD_VM_INTERP_INSTR_END;
     246             : 
     247             :   FD_VM_INTERP_INSTR_BEGIN(0x18) /* FD_SBPF_OP_LDQ */ /* FIXME: MORE THINKING AROUND LDQ HANDLING HERE */
     248       82281 :     pc++;
     249       82281 :     ic_correction++;
     250       82281 :     if( FD_UNLIKELY( pc>=text_cnt ) ) goto sigsplit; /* Note: untaken branches don't consume BTB */
     251       82281 :     reg[ dst ] = (ulong)((ulong)imm | ((ulong)fd_vm_instr_imm( text[ pc ] ) << 32));
     252       82281 :   FD_VM_INTERP_INSTR_END;
     253             : 
     254             :   FD_VM_INTERP_INSTR_BEGIN(0x1c) /* FD_SBPF_OP_SUB_REG */
     255          30 :     reg[ dst ] = (ulong)(long)( (int)reg_dst - (int)reg_src );
     256          30 :   FD_VM_INTERP_INSTR_END;
     257             : 
     258             :   FD_VM_INTERP_BRANCH_BEGIN(0x1d) /* FD_SBPF_OP_JEQ_REG */
     259       22026 :     pc += fd_ulong_if( reg_dst==reg_src, (ulong)(long)offset, 0UL );
     260       22026 :   FD_VM_INTERP_BRANCH_END;
     261             : 
     262             :   FD_VM_INTERP_INSTR_BEGIN(0x1f) /* FD_SBPF_OP_SUB64_REG */
     263       97155 :     reg[ dst ] = reg_dst - reg_src;
     264       97155 :   FD_VM_INTERP_INSTR_END;
     265             : 
     266             :   /* 0x20 - 0x2f ******************************************************/
     267             : 
     268             :   FD_VM_INTERP_INSTR_BEGIN(0x24) /* FD_SBPF_OP_MUL_IMM */
     269          33 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)imm );
     270          33 :   FD_VM_INTERP_INSTR_END;
     271             : 
     272             :   FD_VM_INTERP_BRANCH_BEGIN(0x25) /* FD_SBPF_OP_JGT_IMM */
     273       38337 :     pc += fd_ulong_if( reg_dst>(ulong)(long)(int)imm, (ulong)(long)offset, 0UL );
     274       38337 :   FD_VM_INTERP_BRANCH_END;
     275             : 
     276             :   FD_VM_INTERP_INSTR_BEGIN(0x27) /* FD_SBPF_OP_MUL64_IMM */
     277       46377 :     reg[ dst ] = (ulong)( (long)reg_dst * (long)(int)imm );
     278       46377 :   FD_VM_INTERP_INSTR_END;
     279             : 
     280             :   FD_VM_INTERP_INSTR_BEGIN(0x2c) /* FD_SBPF_OP_MUL_REG */
     281          39 :     reg[ dst ] = (ulong)(long)( (int)reg_dst * (int)reg_src );
     282          39 :   FD_VM_INTERP_INSTR_END;
     283             : 
     284             :   FD_VM_INTERP_BRANCH_BEGIN(0x2d) /* FD_SBPF_OP_JGT_REG */
     285      215838 :     pc += fd_ulong_if( reg_dst>reg_src, (ulong)(long)offset, 0UL );
     286      215838 :   FD_VM_INTERP_BRANCH_END;
     287             : 
     288             :   FD_VM_INTERP_INSTR_BEGIN(0x2f) /* FD_SBPF_OP_MUL64_REG */
     289      103869 :     reg[ dst ] = reg_dst * reg_src;
     290      103869 :   FD_VM_INTERP_INSTR_END;
     291             : 
     292             :   /* 0x30 - 0x3f ******************************************************/
     293             : 
     294             :   FD_VM_INTERP_INSTR_BEGIN(0x34) /* FD_SBPF_OP_DIV_IMM */
     295          33 :     /* FIXME: convert to a multiply at validation time (usually probably
     296          33 :        not worth it) */
     297          33 :     reg[ dst ] = (ulong)((uint)reg_dst / imm);
     298          33 :   FD_VM_INTERP_INSTR_END;
     299             : 
     300             :   FD_VM_INTERP_BRANCH_BEGIN(0x35) /* FD_SBPF_OP_JGE_IMM */
     301        3027 :     pc += fd_ulong_if( reg_dst>=(ulong)(long)(int)imm, (ulong)(long)offset, 0UL );
     302        3027 :   FD_VM_INTERP_BRANCH_END;
     303             : 
     304             :   FD_VM_INTERP_INSTR_BEGIN(0x37) /* FD_SBPF_OP_DIV64_IMM */
     305       10278 :     reg[ dst ] = reg_dst / (ulong)imm;
     306       10278 :   FD_VM_INTERP_INSTR_END;
     307             : 
     308             :   FD_VM_INTERP_INSTR_BEGIN(0x3c) /* FD_SBPF_OP_DIV_REG */
     309          48 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     310          36 :     reg[ dst ] = (ulong)((uint)reg_dst / (uint)reg_src);
     311          36 :   FD_VM_INTERP_INSTR_END;
     312             : 
     313             :   FD_VM_INTERP_BRANCH_BEGIN(0x3d) /* FD_SBPF_OP_JGE_REG */
     314       42669 :     pc += fd_ulong_if( reg_dst>=reg_src, (ulong)(long)offset, 0UL );
     315       42669 :   FD_VM_INTERP_BRANCH_END;
     316             : 
     317             :   FD_VM_INTERP_INSTR_BEGIN(0x3f) /* FD_SBPF_OP_DIV64_REG */
     318       31701 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     319       31692 :     reg[ dst ] = reg_dst / reg_src;
     320       31692 :   FD_VM_INTERP_INSTR_END;
     321             : 
     322             :   /* 0x40 - 0x4f ******************************************************/
     323             : 
     324             :   FD_VM_INTERP_INSTR_BEGIN(0x44) /* FD_SBPF_OP_OR_IMM */
     325          18 :     reg[ dst ] = (ulong)( (uint)reg_dst | imm );
     326          18 :   FD_VM_INTERP_INSTR_END;
     327             : 
     328             :   FD_VM_INTERP_BRANCH_BEGIN(0x45) /* FD_SBPF_OP_JSET_IMM */
     329         621 :     pc += fd_ulong_if( !!(reg_dst & (ulong)(long)(int)imm), (ulong)(long)offset, 0UL );
     330         621 :   FD_VM_INTERP_BRANCH_END;
     331             : 
     332             :   FD_VM_INTERP_INSTR_BEGIN(0x47) /* FD_SBPF_OP_OR64_IMM */
     333          87 :     reg[ dst ] = reg_dst | (ulong)(long)(int)imm;
     334          87 :   FD_VM_INTERP_INSTR_END;
     335             : 
     336             :   FD_VM_INTERP_INSTR_BEGIN(0x4c) /* FD_SBPF_OP_OR_REG */
     337          21 :     reg[ dst ] = (ulong)(uint)( reg_dst | reg_src );
     338          21 :   FD_VM_INTERP_INSTR_END;
     339             : 
     340             :   FD_VM_INTERP_BRANCH_BEGIN(0x4d) /* FD_SBPF_OP_JSET_REG */
     341         324 :     pc += fd_ulong_if( !!(reg_dst & reg_src), (ulong)(long)offset, 0UL );
     342         324 :   FD_VM_INTERP_BRANCH_END;
     343             : 
     344             :   FD_VM_INTERP_INSTR_BEGIN(0x4f) /* FD_SBPF_OP_OR64_REG */
     345       62184 :     reg[ dst ] = reg_dst | reg_src;
     346       62184 :   FD_VM_INTERP_INSTR_END;
     347             : 
     348             :   /* 0x50 - 0x5f ******************************************************/
     349             : 
     350             :   FD_VM_INTERP_INSTR_BEGIN(0x54) /* FD_SBPF_OP_AND_IMM */
     351          21 :     reg[ dst ] = (ulong)( (uint)reg_dst & imm );
     352          21 :   FD_VM_INTERP_INSTR_END;
     353             : 
     354             :   FD_VM_INTERP_BRANCH_BEGIN(0x55) /* FD_SBPF_OP_JNE_IMM */
     355      160914 :     pc += fd_ulong_if( reg_dst!=(ulong)(long)(int)imm, (ulong)(long)offset, 0UL );
     356      160914 :   FD_VM_INTERP_BRANCH_END;
     357             : 
     358             :   FD_VM_INTERP_INSTR_BEGIN(0x57) /* FD_SBPF_OP_AND64_IMM */
     359       32649 :     reg[ dst ] = reg_dst & (ulong)(long)(int)imm;
     360       32649 :   FD_VM_INTERP_INSTR_END;
     361             : 
     362             :   FD_VM_INTERP_INSTR_BEGIN(0x5c) /* FD_SBPF_OP_AND_REG */
     363          27 :     reg[ dst ] = (ulong)(uint)( reg_dst & reg_src );
     364          27 :   FD_VM_INTERP_INSTR_END;
     365             : 
     366             :   FD_VM_INTERP_BRANCH_BEGIN(0x5d) /* FD_SBPF_OP_JNE_REG */
     367       28482 :     pc += fd_ulong_if( reg_dst!=reg_src, (ulong)(long)offset, 0UL );
     368       28482 :   FD_VM_INTERP_BRANCH_END;
     369             : 
     370             :   FD_VM_INTERP_INSTR_BEGIN(0x5f) /* FD_SBPF_OP_AND64_REG */
     371       47619 :     reg[ dst ] = reg_dst & reg_src;
     372       47619 :   FD_VM_INTERP_INSTR_END;
     373             : 
     374             :   /* 0x60 - 0x6f ******************************************************/
     375             : 
     376             :   /* FIXME: CHECK THE CU COST MODEL FOR THESE (IS IT LIKE
     377             :      FD_VM_CONSUME_MEM AND NOT JUST FIXED) */
     378             :   /* FIXME: MEM TRACING DIAGNOSTICS GO IN HERE */
     379             : 
     380       19488 :   FD_VM_INTERP_INSTR_BEGIN(0x61) { /* FD_SBPF_OP_LDXW */
     381       19488 :     uchar is_multi_region = 0;
     382       19488 :     ulong vaddr           = reg_src + (ulong)(long)offset;
     383       19488 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     384       19488 :     int   sigsegv         = !haddr;
     385       19488 :     if( FD_UNLIKELY( sigsegv ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     386       19479 :     reg[ dst ] = fd_vm_mem_ld_4( vm, vaddr, haddr, is_multi_region );
     387       19479 :   }
     388       19479 :   FD_VM_INTERP_INSTR_END;
     389             : 
     390           6 :   FD_VM_INTERP_INSTR_BEGIN(0x62) { /* FD_SBPF_OP_STW */
     391           6 :     uchar is_multi_region = 0;
     392           6 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     393           6 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     394           6 :     int   sigsegv         = !haddr;
     395           6 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     396           3 :     fd_vm_mem_st_4( vm, vaddr, haddr, imm, is_multi_region );
     397           3 :   }
     398           3 :   FD_VM_INTERP_INSTR_END;
     399             : 
     400       14925 :   FD_VM_INTERP_INSTR_BEGIN(0x63) { /* FD_SBPF_OP_STXW */
     401       14925 :     uchar is_multi_region = 0;
     402       14925 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     403       14925 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uint), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     404       14925 :     int   sigsegv         = !haddr;
     405       14925 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus/rdonly */
     406       14919 :     fd_vm_mem_st_4( vm, vaddr, haddr, (uint)reg_src, is_multi_region );
     407       14919 :   }
     408       14919 :   FD_VM_INTERP_INSTR_END;
     409             : 
     410             :   FD_VM_INTERP_INSTR_BEGIN(0x64) /* FD_SBPF_OP_LSH_IMM */
     411         207 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L291 */
     412         207 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, (uint)imm ) );
     413         207 :   FD_VM_INTERP_INSTR_END;
     414             : 
     415             :   FD_VM_INTERP_BRANCH_BEGIN(0x65) /* FD_SBPF_OP_JSGT_IMM */
     416       11784 :     pc += fd_ulong_if( (long)reg_dst>(long)(int)imm, (ulong)(long)offset, 0UL );
     417       11784 :   FD_VM_INTERP_BRANCH_END;
     418             : 
     419             :   FD_VM_INTERP_INSTR_BEGIN(0x67) /* FD_SBPF_OP_LSH64_IMM */
     420      136914 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L376 */
     421      136914 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, imm );
     422      136914 :   FD_VM_INTERP_INSTR_END;
     423             : 
     424        1968 :   FD_VM_INTERP_INSTR_BEGIN(0x69) { /* FD_SBPF_OP_LDXH */
     425        1968 :     uchar is_multi_region = 0;
     426        1968 :     ulong vaddr           = reg_src + (ulong)(long)offset;
     427        1968 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     428        1968 :     int   sigsegv         = !haddr;
     429        1968 :     if( FD_UNLIKELY( sigsegv ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     430        1956 :     reg[ dst ] = fd_vm_mem_ld_2( vm, vaddr, haddr, is_multi_region );
     431        1956 :   }
     432        1956 :   FD_VM_INTERP_INSTR_END;
     433             : 
     434           6 :   FD_VM_INTERP_INSTR_BEGIN(0x6a) { /* FD_SBPF_OP_STH */
     435           6 :     uchar is_multi_region = 0;
     436           6 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     437           6 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     438           6 :     int   sigsegv         = !haddr;
     439           6 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     440           3 :     fd_vm_mem_st_2( vm, vaddr, haddr, (ushort)imm, is_multi_region );
     441           3 :   }
     442           3 :   FD_VM_INTERP_INSTR_END;
     443             : 
     444        2037 :   FD_VM_INTERP_INSTR_BEGIN(0x6b) { /* FD_SBPF_OP_STXH */
     445        2037 :     uchar is_multi_region = 0;
     446        2037 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     447        2037 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ushort), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     448        2037 :     int   sigsegv         = !haddr;
     449        2037 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus/rdonly */
     450        2031 :     fd_vm_mem_st_2( vm, vaddr, haddr, (ushort)reg_src, is_multi_region );
     451        2031 :   }
     452        2031 :   FD_VM_INTERP_INSTR_END;
     453             : 
     454             :   FD_VM_INTERP_INSTR_BEGIN(0x6c) /* FD_SBPF_OP_LSH_REG */
     455         219 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L292 */
     456         219 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHL( (uint)reg_dst, reg_src ) );
     457         219 :   FD_VM_INTERP_INSTR_END;
     458             : 
     459             :   FD_VM_INTERP_BRANCH_BEGIN(0x6d) /* FD_SBPF_OP_JSGT_REG */
     460       25608 :     pc += fd_ulong_if( (long)reg_dst>(long)reg_src, (ulong)(long)offset, 0UL );
     461       25608 :   FD_VM_INTERP_BRANCH_END;
     462             : 
     463             :   FD_VM_INTERP_INSTR_BEGIN(0x6f) /* FD_SBPF_OP_LSH64_REG */
     464        4713 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L377 */
     465        4713 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHL( reg_dst, reg_src );
     466        4713 :   FD_VM_INTERP_INSTR_END;
     467             : 
     468             :   /* 0x70 - 0x7f ******************************************************/
     469             : 
     470       96999 :   FD_VM_INTERP_INSTR_BEGIN(0x71) { /* FD_SBPF_OP_LDXB */
     471       96999 :     uchar is_multi_region = 0;
     472       96999 :     ulong vaddr           = reg_src + (ulong)(long)offset;
     473       96999 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     474       96999 :     if( FD_UNLIKELY( !haddr ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */
     475       96990 :     reg[ dst ] = fd_vm_mem_ld_1( haddr );
     476       96990 :   }
     477       96990 :   FD_VM_INTERP_INSTR_END;
     478             : 
     479           6 :   FD_VM_INTERP_INSTR_BEGIN(0x72) { /* FD_SBPF_OP_STB */
     480           6 :     uchar is_multi_region = 0;
     481           6 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     482           6 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     483           6 :     if( FD_UNLIKELY( !haddr ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */
     484           3 :     fd_vm_mem_st_1( haddr, (uchar)imm );
     485           3 :   }
     486           3 :   FD_VM_INTERP_INSTR_END;
     487             : 
     488       65121 :   FD_VM_INTERP_INSTR_BEGIN(0x73) { /* FD_SBPF_OP_STXB */
     489       65121 :     uchar is_multi_region = 0;
     490       65121 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     491       65121 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(uchar), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     492       65121 :     if( FD_UNLIKELY( !haddr ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigrdonly */
     493       65112 :     fd_vm_mem_st_1( haddr, (uchar)reg_src );
     494       65112 :   }
     495       65112 :   FD_VM_INTERP_INSTR_END;
     496             : 
     497             :   FD_VM_INTERP_INSTR_BEGIN(0x74) /* FD_SBPF_OP_RSH_IMM */
     498           6 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L293 */
     499           6 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, imm ) );
     500           6 :   FD_VM_INTERP_INSTR_END;
     501             : 
     502             :   FD_VM_INTERP_BRANCH_BEGIN(0x75) /* FD_SBPF_OP_JSGE_IMM */
     503        3315 :     pc += fd_ulong_if( (long)reg_dst>=(long)(int)imm, (ulong)(long)offset, 0UL );
     504        3315 :   FD_VM_INTERP_BRANCH_END;
     505             : 
     506             :   FD_VM_INTERP_INSTR_BEGIN(0x77) /* FD_SBPF_OP_RSH64_IMM */
     507      200655 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L378 */
     508      200655 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, imm );
     509      200655 :   FD_VM_INTERP_INSTR_END;
     510             : 
     511      631881 :   FD_VM_INTERP_INSTR_BEGIN(0x79) { /* FD_SBPF_OP_LDXQ */
     512      631881 :     uchar is_multi_region = 0;
     513      631881 :     ulong vaddr           = reg_src + (ulong)(long)offset;
     514      631881 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_ld_sz, 0, 0UL, &is_multi_region );
     515      631881 :     int   sigsegv         = !haddr;
     516      631881 :     if( FD_UNLIKELY( sigsegv ) ) goto sigsegv; /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     517      631647 :     reg[ dst ] = fd_vm_mem_ld_8( vm, vaddr, haddr, is_multi_region );
     518      631647 :   }
     519      631647 :   FD_VM_INTERP_INSTR_END;
     520             : 
     521           6 :   FD_VM_INTERP_INSTR_BEGIN(0x7a) { /* FD_SBPF_OP_STQ */
     522           6 :     uchar is_multi_region = 0;
     523           6 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     524           6 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     525           6 :     int   sigsegv         = !haddr;
     526           6 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus */
     527           3 :     fd_vm_mem_st_8( vm, vaddr, haddr, (ulong)imm, is_multi_region );
     528           3 :   }
     529           3 :   FD_VM_INTERP_INSTR_END;
     530             : 
     531      540246 :   FD_VM_INTERP_INSTR_BEGIN(0x7b) { /* FD_SBPF_OP_STXQ */
     532      540246 :     uchar is_multi_region = 0;
     533      540246 :     ulong vaddr           = reg_dst + (ulong)(long)offset;
     534      540246 :     ulong haddr           = fd_vm_mem_haddr( vm, vaddr, sizeof(ulong), region_haddr, region_st_sz, 1, 0UL, &is_multi_region );
     535      540246 :     int   sigsegv         = !haddr;
     536      540246 :     if( FD_UNLIKELY( sigsegv ) ) { vm->segv_store_vaddr = vaddr; goto sigsegv; } /* Note: untaken branches don't consume BTB */ /* FIXME: sigbus/rdonly */
     537      540243 :     fd_vm_mem_st_8( vm, vaddr, haddr, reg_src, is_multi_region );
     538      540243 :   }
     539      540243 :   FD_VM_INTERP_INSTR_END;
     540             : 
     541             :   FD_VM_INTERP_INSTR_BEGIN(0x7c) /* FD_SBPF_OP_RSH_REG */
     542           6 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L294 */
     543           6 :     reg[ dst ] = (ulong)( FD_RUST_UINT_WRAPPING_SHR( (uint)reg_dst, (uint)reg_src ) );
     544           6 :   FD_VM_INTERP_INSTR_END;
     545             : 
     546             :   FD_VM_INTERP_BRANCH_BEGIN(0x7d) /* FD_SBPF_OP_JSGE_REG */
     547       26676 :     pc += fd_ulong_if( (long)reg_dst>=(long)reg_src, (ulong)(long)offset, 0UL );
     548       26676 :   FD_VM_INTERP_BRANCH_END;
     549             : 
     550             :   FD_VM_INTERP_INSTR_BEGIN(0x7f) /* FD_SBPF_OP_RSH64_REG */
     551        4860 :     /* https://github.com/solana-labs/rbpf/blob/8d36530b7071060e2837ebb26f25590db6816048/src/interpreter.rs#L379 */
     552        4860 :     reg[ dst ] = FD_RUST_ULONG_WRAPPING_SHR( reg_dst, reg_src );
     553        4860 :   FD_VM_INTERP_INSTR_END;
     554             : 
     555             :   /* 0x80-0x8f ********************************************************/
     556             : 
     557             :   FD_VM_INTERP_INSTR_BEGIN(0x84) /* FD_SBPF_OP_NEG */
     558           3 :     reg[ dst ] = (ulong)( -(uint)reg_dst );
     559           3 :   FD_VM_INTERP_INSTR_END;
     560             : 
     561             :   FD_VM_INTERP_BRANCH_BEGIN(0x85) /* FD_SBPF_OP_CALL_IMM */
     562      136740 : 
     563      136740 :     /* Note we do the stack push before updating the pc.  This implies
     564      136740 :        that the call stack frame gets allocated _before_ checking if the
     565      136740 :        call target is valid.  It would be fine to switch the order
     566      136740 :        though such would change the precise faulting semantics of
     567      136740 :        sigcall and sigstack. */
     568      136740 : 
     569      136740 :     fd_sbpf_syscalls_t const * syscall = fd_sbpf_syscalls_query_const( syscalls, imm, NULL );
     570      136740 :     if( FD_UNLIKELY( !syscall ) ) { /* Optimize for the syscall case */
     571             : 
     572             :       /* Note the original implementation had the imm magic number
     573             :           check after the calldest check.  But fd_pchash_inverse of the
     574             :           magic number is 0xb00c380U.  This is beyond possible text_cnt
     575             :           values.  So we do it first to simplify the code and clean up
     576             :           fault handling. */
     577             : 
     578      129015 :       FD_VM_INTERP_STACK_PUSH;
     579             : 
     580      129012 :       if( FD_UNLIKELY( imm==0x71e3cf81U ) ) pc = entry_pc; /* FIXME: MAGIC NUMBER */
     581      129012 :       else {
     582      129012 :         pc = (ulong)fd_pchash_inverse( imm );
     583      129012 :         if( FD_UNLIKELY( pc>=text_cnt ) || FD_UNLIKELY( !fd_sbpf_calldests_test( calldests, pc ) ) ) goto sigcall;
     584      129012 :       }
     585      129009 :       pc--;
     586             : 
     587      129009 :     } else {
     588             : 
     589             :       /* Update the vm with the current vm execution state for the
     590             :           syscall.  Note that BRANCH_BEGIN has pc at the syscall and
     591             :           already updated ic and cu to reflect all instructions up to
     592             :           and including the syscall instruction itself. */
     593             : 
     594        7725 :       vm->pc        = pc;
     595        7725 :       vm->ic        = ic;
     596        7725 :       vm->cu        = cu;
     597        7725 :       vm->frame_cnt = frame_cnt;
     598             : 
     599             :       /* Do the syscall.  We use ret reduce the risk of the syscall
     600             :           accidentally modifying other registers (note however since a
     601             :           syscall has the vm handle it still do arbitrary modifications
     602             :           to the vm state) and the risk of a pointer escape on reg from
     603             :           inhibiting compiler optimizations (this risk is likely low in
     604             :           as this is the only point in the whole interpreter core that
     605             :           calls outside this translation unit). */
     606             : 
     607             :       /* At this point, vm->cu is positive */
     608             : 
     609        7725 :       ulong ret[1];
     610        7725 :       err = syscall->func( vm, reg[1], reg[2], reg[3], reg[4], reg[5], ret );
     611        7725 :       reg[0] = ret[0];
     612             : 
     613             :       /* If we trust syscall implementations to handle the vm state
     614             :           correctly, the below could be implemented as unpacking the vm
     615             :           state and jumping to sigsys on error.  But we provide some
     616             :           extra protection to make various strong guarantees:
     617             : 
     618             :           - We do not let the syscall modify pc currently as nothing
     619             :             requires this and it reduces risk of a syscall bug mucking
     620             :             up the interpreter.  If there ever was a syscall that
     621             :             needed to modify the pc (e.g. a syscall that has execution
     622             :             resume from a different location than the instruction
     623             :             following the syscall), do "pc = vm->pc" below.
     624             : 
     625             :           - We do not let the syscall modify ic currently as nothing
     626             :             requires this and it keeps the ic precise.  If a future
     627             :             syscall needs this, do "ic = vm->ic" below.
     628             : 
     629             :           - We do not let the syscall increase cu as nothing requires
     630             :             this and it guarantees the interpreter will halt in a
     631             :             reasonable finite amount of time.  If a future syscall
     632             :             needs this, do "cu = vm->cu" below.
     633             : 
     634             :           - A syscall that returns SIGCOST is always treated as though
     635             :             it also zerod cu. */
     636             : 
     637             :       /* At this point, vm->cu is whatever the syscall tried to set
     638             :           and cu is positive */
     639             : 
     640        7725 :       ulong cu_req = vm->cu;
     641        7725 :       cu = fd_ulong_min( cu_req, cu );
     642        7725 :       if( FD_UNLIKELY( err ) ) {
     643         108 :         if( err==FD_VM_ERR_SIGCOST ) cu = 0UL; /* cmov */
     644         108 :         goto sigsyscall;
     645         108 :       }
     646             : 
     647             :       /* At this point, cu is positive and err is clear */
     648        7725 :     }
     649             : 
     650      136626 :   FD_VM_INTERP_BRANCH_END;
     651             : 
     652             :   FD_VM_INTERP_INSTR_BEGIN(0x87) /* FD_SBPF_OP_NEG64 */
     653       14265 :     reg[ dst ] = -reg_dst;
     654       14265 :   FD_VM_INTERP_INSTR_END;
     655             : 
     656       14265 :   FD_VM_INTERP_BRANCH_BEGIN(0x8d) { /* FD_SBPF_OP_CALL_REG */
     657             : 
     658        3231 :     FD_VM_INTERP_STACK_PUSH;
     659             : 
     660             :     /* FIXME: REALLY??  SBPF USES IMM TO INDEX THE REG FILE??  DOUBLE
     661             :        CHECK THIS.  AT LEAST MASKING THE LSB IN THE MEANTIME TO
     662             :        GUARANTEE TO OVERFLOW. */
     663        3231 :     ulong vaddr = reg[ imm & 15U ];
     664             : 
     665        3231 :     ulong region = vaddr >> 32;
     666        3231 :     ulong align  = vaddr & 7UL;
     667        3231 :     pc = ((vaddr & 0xffffffffUL)/8UL) - text_word_off;
     668             : 
     669             :     /* Note: BRANCH_END will implicitly handle a pc that fell outside
     670             :        the text section (below via unsigned wraparoud or above) as
     671             :        sigtext */
     672             : 
     673             :     /* FIXME: when static_syscalls are enabled, check that the call destination is valid */
     674             :     /* FIXME: sigbus for misaligned? */
     675             : 
     676        3231 :     if( FD_UNLIKELY( (region!=1UL) | (!!align) ) ) goto sigcall; /* Note: untaken branches don't consume BTB */
     677             : 
     678        3219 :     pc--;
     679             : 
     680        3219 :   } FD_VM_INTERP_BRANCH_END;
     681             : 
     682             :   /* 0x90 - 0x9f ******************************************************/
     683             : 
     684             :   FD_VM_INTERP_INSTR_BEGIN(0x94) /* FD_SBPF_OP_MOD_IMM */
     685          36 :     reg[ dst ] = (ulong)( (uint)reg_dst % imm );
     686          36 :   FD_VM_INTERP_INSTR_END;
     687             : 
     688             :   FD_VM_INTERP_BRANCH_BEGIN(0x95) /* FD_SBPF_OP_EXIT */
     689      134166 :       /* Agave JIT VM exit implementation analysis below.
     690      134166 : 
     691      134166 :        Agave references:
     692      134166 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/interpreter.rs#L503-L509
     693      134166 :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L697-L702 */
     694      134166 :     if( FD_UNLIKELY( !frame_cnt ) ) goto sigexit; /* Exit program */
     695      131325 :     frame_cnt--;
     696      131325 :     reg[6]   = shadow[ frame_cnt ].r6;
     697      131325 :     reg[7]   = shadow[ frame_cnt ].r7;
     698      131325 :     reg[8]   = shadow[ frame_cnt ].r8;
     699      131325 :     reg[9]   = shadow[ frame_cnt ].r9;
     700      131325 :     pc       = shadow[ frame_cnt ].pc;
     701      131325 :     reg[10] -= vm->stack_frame_size;
     702      131325 :   FD_VM_INTERP_BRANCH_END;
     703             : 
     704             :   FD_VM_INTERP_INSTR_BEGIN(0x97) /* FD_SBPF_OP_MOD64_IMM */
     705          33 :     reg[ dst ] = reg_dst % (ulong)(long)(int)imm;
     706          33 :   FD_VM_INTERP_INSTR_END;
     707             : 
     708             :   FD_VM_INTERP_INSTR_BEGIN(0x9c) /* FD_SBPF_OP_MOD_REG */
     709          48 :     if( FD_UNLIKELY( !(uint)reg_src ) ) goto sigfpe;
     710          36 :     reg[ dst ] = (ulong)( ((uint)reg_dst % (uint)reg_src) );
     711          36 :   FD_VM_INTERP_INSTR_END;
     712             : 
     713             :   FD_VM_INTERP_INSTR_BEGIN(0x9f) /* FD_SBPF_OP_MOD64_REG */
     714          45 :     if( FD_UNLIKELY( !reg_src ) ) goto sigfpe;
     715          36 :     reg[ dst ] = reg_dst % reg_src;
     716          36 :   FD_VM_INTERP_INSTR_END;
     717             : 
     718             :   /* 0xa0 - 0xaf ******************************************************/
     719             : 
     720             :   FD_VM_INTERP_INSTR_BEGIN(0xa4) /* FD_SBPF_OP_XOR_IMM */
     721           6 :     reg[ dst ] = (ulong)( (uint)reg_dst ^ imm );
     722           6 :   FD_VM_INTERP_INSTR_END;
     723             : 
     724             :   FD_VM_INTERP_BRANCH_BEGIN(0xa5) /* FD_SBPF_OP_JLT_IMM */
     725        1542 :     pc += fd_ulong_if( reg_dst<(ulong)(long)(int)imm, (ulong)(long)offset, 0UL );
     726        1542 :   FD_VM_INTERP_BRANCH_END;
     727             : 
     728             :   FD_VM_INTERP_INSTR_BEGIN(0xa7) /* FD_SBPF_OP_XOR64_IMM */
     729       14781 :     reg[ dst ] = reg_dst ^ (ulong)(long)(int)imm;
     730       14781 :   FD_VM_INTERP_INSTR_END;
     731             : 
     732             :   FD_VM_INTERP_INSTR_BEGIN(0xac) /* FD_SBPF_OP_XOR_REG */
     733          18 :     reg[ dst ] = (ulong)(uint)( reg_dst ^ reg_src );
     734          18 :   FD_VM_INTERP_INSTR_END;
     735             : 
     736             :   FD_VM_INTERP_BRANCH_BEGIN(0xad) /* FD_SBPF_OP_JLT_REG */
     737        1242 :     pc += fd_ulong_if( reg_dst<reg_src, (ulong)(long)offset, 0UL );
     738        1242 :   FD_VM_INTERP_BRANCH_END;
     739             : 
     740             :   FD_VM_INTERP_INSTR_BEGIN(0xaf) /* FD_SBPF_OP_XOR64_REG */
     741        1335 :     reg[ dst ] = reg_dst ^ reg_src;
     742        1335 :   FD_VM_INTERP_INSTR_END;
     743             : 
     744             :   /* 0xb0 - 0xbf ******************************************************/
     745             : 
     746             :   FD_VM_INTERP_INSTR_BEGIN(0xb4) /* FD_SBPF_OP_MOV_IMM */
     747         327 :     reg[ dst ] = (ulong)imm;
     748         327 :   FD_VM_INTERP_INSTR_END;
     749             : 
     750             :   FD_VM_INTERP_BRANCH_BEGIN(0xb5) /* FD_SBPF_OP_JLE_IMM */
     751        3027 :     pc += fd_ulong_if( reg_dst<=(ulong)(long)(int)imm, (ulong)(long)offset, 0UL );
     752        3027 :   FD_VM_INTERP_BRANCH_END;
     753             : 
     754             :   FD_VM_INTERP_INSTR_BEGIN(0xb7) /* FD_SBPF_OP_MOV64_IMM */
     755      491817 :     reg[ dst ] = (ulong)(long)(int)imm;
     756      491817 :   FD_VM_INTERP_INSTR_END;
     757             : 
     758             :   FD_VM_INTERP_INSTR_BEGIN(0xbc) /* FD_SBPF_OP_MOV_REG */
     759          15 :     reg[ dst ] = (ulong)(uint)reg_src;
     760          15 :   FD_VM_INTERP_INSTR_END;
     761             : 
     762             :   FD_VM_INTERP_BRANCH_BEGIN(0xbd) /* FD_SBPF_OP_JLE_REG */
     763        2430 :     pc += fd_ulong_if( reg_dst<=reg_src, (ulong)(long)offset, 0UL );
     764        2430 :   FD_VM_INTERP_BRANCH_END;
     765             : 
     766             :   FD_VM_INTERP_INSTR_BEGIN(0xbf) /* FD_SBPF_OP_MOV64_REG */
     767     1054218 :     reg[ dst ] = reg_src;
     768     1054218 :   FD_VM_INTERP_INSTR_END;
     769             : 
     770             :   /* 0xc0 - 0xcf ******************************************************/
     771             : 
     772             :   FD_VM_INTERP_INSTR_BEGIN(0xc4) /* FD_SBPF_OP_ARSH_IMM */
     773           6 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst >> imm ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
     774           6 :   FD_VM_INTERP_INSTR_END;
     775             : 
     776             :   FD_VM_INTERP_BRANCH_BEGIN(0xc5) /* FD_SBPF_OP_JSLT_IMM */ /* FIXME: CHECK IMM SIGN EXTENSION */
     777        1527 :     pc += fd_ulong_if( (long)reg_dst<(long)(int)imm, (ulong)(long)offset, 0UL );
     778        1527 :   FD_VM_INTERP_BRANCH_END;
     779             : 
     780             :   FD_VM_INTERP_INSTR_BEGIN(0xc7) /* FD_SBPF_OP_ARSH64_IMM */
     781       22050 :     reg[ dst ] = (ulong)( (long)reg_dst >> imm ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
     782       22050 :   FD_VM_INTERP_INSTR_END;
     783             : 
     784             :   FD_VM_INTERP_INSTR_BEGIN(0xcc) /* FD_SBPF_OP_ARSH_REG */
     785           9 :     reg[ dst ] = (ulong)(uint)( (int)reg_dst >> (uint)reg_src ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
     786           9 :   FD_VM_INTERP_INSTR_END;
     787             : 
     788             :   FD_VM_INTERP_BRANCH_BEGIN(0xcd) /* FD_SBPF_OP_JSLT_REG */
     789        1530 :     pc += fd_ulong_if( (long)reg_dst<(long)reg_src, (ulong)(long)offset, 0UL );
     790        1530 :   FD_VM_INTERP_BRANCH_END;
     791             : 
     792             :   FD_VM_INTERP_INSTR_BEGIN(0xcf) /* FD_SBPF_OP_ARSH64_REG */
     793           6 :     reg[ dst ] = (ulong)( (long)reg_dst >> reg_src ); /* FIXME: WIDE SHIFTS, STRICT SIGN EXTENSION */
     794           6 :   FD_VM_INTERP_INSTR_END;
     795             : 
     796             :   /* 0xd0 - 0xdf ******************************************************/
     797             : 
     798             :   FD_VM_INTERP_INSTR_BEGIN(0xd4) /* FD_SBPF_OP_END_LE */
     799          12 :     /* Note: since fd_vm_validate rejects LE with strange immediates, we
     800          12 :        sigill if we encouter such in unvalidated code to match (FIXME:
     801          12 :        IS THIS THE DESIRED BEHAVIOR?) */
     802          12 :     switch( imm ) {
     803           6 :     case 16U: reg[ dst ] = (ushort)reg_dst; break;
     804           3 :     case 32U: reg[ dst ] = (uint)  reg_dst; break;
     805           3 :     case 64U:                               break;
     806           0 :     default: goto sigill;
     807          12 :     }
     808          12 :   FD_VM_INTERP_INSTR_END;
     809             : 
     810             :   FD_VM_INTERP_BRANCH_BEGIN(0xd5) /* FD_SBPF_OP_JSLE_IMM */
     811        1209 :     pc += fd_ulong_if( (long)reg_dst<=(long)(int)imm, (ulong)(long)offset, 0UL );
     812        1209 :   FD_VM_INTERP_BRANCH_END;
     813             : 
     814             :   FD_VM_INTERP_INSTR_BEGIN(0xdc) /* FD_SBPF_OP_END_BE */
     815          63 :     switch( imm ) {
     816          12 :     case 16U: reg[ dst ] = (ulong)fd_ushort_bswap( (ushort)reg_dst ); break;
     817           9 :     case 32U: reg[ dst ] = (ulong)fd_uint_bswap  ( (uint)  reg_dst ); break;
     818          42 :     case 64U: reg[ dst ] =        fd_ulong_bswap ( (ulong) reg_dst ); break;
     819           0 :     default: goto sigill;
     820          63 :     }
     821          63 :   FD_VM_INTERP_INSTR_END;
     822             : 
     823             :   FD_VM_INTERP_BRANCH_BEGIN(0xdd) /* FD_SBPF_OP_JSLE_REG */
     824         909 :     pc += fd_ulong_if( (long)reg_dst<=(long)reg_src, (ulong)(long)offset, 0UL );
     825         909 :   FD_VM_INTERP_BRANCH_END;
     826             : 
     827             :   /* FIXME: sigsplit is only partially implemented (needs the bit vector
     828             :      of invalid jump targets in BRANCH_END) */
     829             : 
     830             :   /* FIXME: sigbus/sigrdonly are mapped to sigsegv for simplicity
     831             :      currently but could be enabled if desired. */
     832             : 
     833             :   /* FD_VM_INTERP_FAULT accumulates to ic and cu all non-faulting
     834             :      instructions preceeding a fault generated by a non-branching
     835             :      instruction.  When a non-branching instruction faults, pc is at the
     836             :      instruction and the number of non-branching instructions that have
     837             :      not yet been reflected in ic and cu is:
     838             : 
     839             :        pc - pc0 + 1 - ic_correction
     840             : 
     841             :      as per the accounting described above. +1 to include the faulting
     842             :      instruction itself.
     843             : 
     844             :      Note that, for a sigtext caused by a branch instruction, pc0==pc
     845             :      (from the BRANCH_END) and ic_correction==0 (from the BRANCH_BEGIN)
     846             :      such that the below does not change the already current values in
     847             :      ic and cu.  Thus it also "does the right thing" in both the
     848             :      non-branching and branching cases for sigtext.  The same applies to
     849             :      sigsplit. */
     850             : 
     851           0 : #define FD_VM_INTERP_FAULT                                          \
     852         348 :   ic_correction = pc - pc0 + 1UL - ic_correction;                   \
     853         348 :   ic += ic_correction;                                              \
     854         348 :   if ( FD_UNLIKELY( ic_correction > cu ) ) err = FD_VM_ERR_SIGCOST; \
     855         348 :   cu -= fd_ulong_min( ic_correction, cu )
     856             : 
     857           3 : sigtext:     err = FD_VM_ERR_SIGTEXT;  FD_VM_INTERP_FAULT;                     goto interp_halt;
     858           0 : sigsplit:    err = FD_VM_ERR_SIGSPLIT; FD_VM_INTERP_FAULT;                     goto interp_halt;
     859          15 : sigcall:     err = FD_VM_ERR_SIGCALL;  /* ic current */      /* cu current */  goto interp_halt;
     860           3 : sigstack:    err = FD_VM_ERR_SIGSTACK; /* ic current */      /* cu current */  goto interp_halt;
     861           3 : sigill:      err = FD_VM_ERR_SIGILL;   FD_VM_INTERP_FAULT;                     goto interp_halt;
     862         300 : sigsegv:     err = FD_VM_ERR_SIGSEGV;  FD_VM_INTERP_FAULT;                     goto interp_halt;
     863         345 : sigcost:     err = FD_VM_ERR_SIGCOST;  /* ic current */      cu = 0UL;         goto interp_halt;
     864         108 : sigsyscall:  /* err current */         /* ic current */      /* cu current */  goto interp_halt;
     865          42 : sigfpe:      err = FD_VM_ERR_SIGFPE;   FD_VM_INTERP_FAULT;                     goto interp_halt;
     866        2841 : sigexit:     /* err current */         /* ic current */      /* cu current */  goto interp_halt;
     867             : 
     868           0 : #undef FD_VM_INTERP_FAULT
     869             : 
     870        3660 : interp_halt:
     871             : 
     872             :   /* Pack the unpacked execution state into vm to give a precise view of
     873             :      the execution when the vm halted. */
     874             : 
     875        3660 :   vm->pc        = pc;
     876        3660 :   vm->ic        = ic;
     877        3660 :   vm->cu        = cu;
     878        3660 :   vm->frame_cnt = frame_cnt;
     879             : 
     880        3660 : # undef FD_VM_INTERP_STACK_PUSH
     881             : 
     882        3660 : # undef FD_VM_INTERP_BRANCH_END
     883        3660 : # undef FD_VM_INTERP_BRANCH_BEGIN
     884             : 
     885        3660 : # undef FD_VM_INTERP_INSTR_END
     886        3660 : # undef FD_VM_INTERP_INSTR_BEGIN
     887        3660 : # undef FD_VM_INTERP_INSTR_EXEC
     888             : 
     889        3660 : # if defined(__clang__)
     890        3660 : # pragma clang diagnostic pop
     891        3660 : # endif
     892             : 
     893        3660 : # if defined(__GNUC__)
     894        3660 : # pragma GCC diagnostic pop
     895        3660 : # endif
     896             : 
     897             : /*   Agave/JIT CU model analysis (and why we are conformant!):
     898             : 
     899             :      The Agave JIT employs a similar strategy of accumulating instructions
     900             :      in a linear run and processing them at the start of a new linear 
     901             :      run/branch (side note: the JIT treats the LDQ instruction as a "branch" 
     902             :      that jumps pc + 2). 
     903             :      
     904             :      In what is assumed to be an act of register conservation, the JIT 
     905             :      uses a catch-all "instruction meter" (IM) register (REGISTER_INSTRUCTION_METER)
     906             :      that represents two different interpretations of the question
     907             :      "how many instructions can I execute?".
     908             : 
     909             :      The IM, depending on where we are in the execution, either represents:
     910             :         1. IM => The number of instructions remaining before exhausting CU 
     911             :         budget. This is analagous to vm->cu in our interpreter.
     912             :         2. IM' => The last pc you can execute in the current linear run before
     913             :         exhausting CU budget.  Mathematically, IM' = IM + pc0
     914             :         where pc0, just like our definition, is the start of the linear run.
     915             :         
     916             :         Note: IM' can go past the actual basic block/segment. In-fact, 
     917             :         it typically does, and implies we can execute the full block without
     918             :         exhausting CU budget (reminder that LDQ is treated as a branch).
     919             :       
     920             :       By default, the IM' form is used during execution. The IM form is used:
     921             :         - (transiently) during the processing of a branch instruction 
     922             :         - in post-VM cleanup (updates EbpfVm::previous_instruction_meter).
     923             : 
     924             :       When a branch instruction is encountered, the JIT checks
     925             :       for CU exhaustion with pc > IM', and throws an exception if so. This is valid,
     926             :       because as described above, IM' is the largest PC you can reach.
     927             :       
     928             :       If we haven't exhausted our CU limit, it updates IM':
     929             :         1. IM = IM' - (pc + 1)  # Note that IM' at this point is IM + pc0', 
     930             :                                 # where pc0' is the start of the current linear run.
     931             :         2. IM' = IM + pc0       # pc0 is the start of the new linear run (typically the target pc)
     932             :       
     933             :       Code (that does the above in one ALU instruction):
     934             :        https://github.com/solana-labs/rbpf/blob/v0.8.5/src/jit.rs#L891
     935             : 
     936             : 
     937             :       ### How does this relate to our interpreter?
     938             : 
     939             :       This process is similar to FD_VM_INTERP_BRANCH_BEGIN.
     940             :       We just deal with the IM form throughout (with vm->cu and ic_correction).
     941             :       If we break down step 1 from above with what we know about IM and IM',
     942             :       we get the following:
     943             :         1. IM = IM' - (pc + 1)
     944             :            IM = (IM + pc0') - (pc + 1)
     945             :            IM = IM + (pc0' - (pc + 1))
     946             :            IM = IM - ((pc + 1) - pc0')
     947             :            IM = IM - ic_correction
     948             :       Here, ((pc + 1) - pc0') is the number of instrutions executed in the current
     949             :       linear run. This is the same as our ic_correction(*) in FD_VM_INTERP_BRANCH_BEGIN.
     950             : 
     951             :       If we replace IM with cu, this effectively becomes the
     952             :            cu -= ic_correction 
     953             :       line in FD_VM_INTERP_BRANCH_BEGIN.
     954             : 
     955             :       (*) Note: ic_correction (also) takes two forms. It is either the instruction 
     956             :       accumulator or the number of instructions executed in the current linear run. 
     957             :       It (transiently) takes the latter form during FD_VM_INTERP_BRANCH_BEGIN and 
     958             :       FD_VM_INTERP_FAULT, and the former form otherwise.
     959             : */
     960             : 
     961             : /* (WIP) Precise faulting and the Agave JIT:
     962             :    
     963             :    Since the cost model is a part of consensus, we need to conform with the Agave/JIT
     964             :    cost model 1:1. To achieve that, our faulting model also needs to match precisely. This
     965             :    section covers the various faults that the respective VMs implement and how they match.
     966             : 
     967             :    # Normal VM exit (sigexit):
     968             :    VM exit instruction entrypoint: https://github.com/solana-labs/rbpf/blob/12237895305ab38514be865ebed6268553e4f589/src/jit.rs#L698-L708
     969             : 
     970             :    Pseudocode (with FD semantics):
     971             :    ```
     972             :     # pc is at the exit instruction
     973             :     # pc0 is the start of the current linear run
     974             :     if (frame_cnt == 0) {
     975             :         goto sigexit;
     976             :     }
     977             :     ...
     978             : 
     979             :     sigexit:
     980             :     if IM' <= pc {
     981             :       goto sigcost;
     982             :     } else {
     983             :       goto interp_halt;
     984             :     }
     985             :     ```
     986             : 
     987             :     Breaking down the IM' < pc check:
     988             :     - IM' = IM + pc0
     989             :     - pc  = ic + pc0, where (ic + 1) is the number of instructions executed in the current linear run
     990             : 
     991             :     IM' <= pc
     992             :     IM + pc0 <= ic + pc0
     993             :     IM <= ic
     994             :     IM <= pc - pc0
     995             :     IM < pc - pc0 + 1 # all unsigned integers
     996             :     IM < ic_correction
     997             : 
     998             :     This is analagous to the ic_correction>cu check in VM_INTERP_BRANCH_BEGIN.
     999             :    
    1000             :    # (TODO) Text Overrun (sigtext/sigsplit):
    1001             : 
    1002             : */

Generated by: LCOV version 1.14