LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_disasm.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 152 216 70.4 %
Date: 2024-11-13 11:58:15 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "fd_vm_private.h"
       2             : 
       3             : /* fd_vm_disasm_printf appends to the *_len string in the max byte
       4             :    buffer buf the printf of the remaining args.  On input, assumes *_len
       5             :    is strlen(buf) and *_len is in [0,max).  On output, even on error
       6             :    cases, the leading string in buf will be unchanged, *len will be
       7             :    strlen(buf) and *len will be [*len_as_it_was_on_input,max).
       8             : 
       9             :    Returns:
      10             : 
      11             :    FD_VM_SUCCESS - success.  buf and *len updated.
      12             : 
      13             :    FD_VM_ERR_FULL - not enough room in buf to hold result.  As many
      14             :    bytes as possible were written to buf and *len==max-1 on return.
      15             : 
      16             :    FD_VM_ERR_IO - printf format parse error.  buf and *len unchanged but
      17             :    trailing bytes of buf might have been clobbered. */
      18             : 
      19             : /* FIXME: REWORK API TO USE FD_CSTR_PRINTF HERE?  (OR CONSIDER ADDING
      20             :    FD_VM_DISASM_PRINTF AS A FD_CSTR API) */
      21             : 
      22             : #include <stdio.h>
      23             : #include <stdarg.h>
      24             : 
      25             : static int
      26             : fd_vm_disasm_printf( char *       buf,
      27             :                      ulong        max,
      28             :                      ulong *      _len,
      29             :                      char const * fmt, ... ) __attribute__((format(printf,4,5)));
      30             : 
      31             : static int
      32             : fd_vm_disasm_printf( char *       buf,
      33             :                      ulong        max,
      34             :                      ulong *      _len,
      35    28108308 :                      char const * fmt, ... ) {
      36    28108308 :   ulong len = *_len;     /* In [0,max) */
      37    28108308 :   ulong rem = max - len; /* In (0,max] */
      38             : 
      39    28108308 :   va_list ap;
      40    28108308 :   va_start( ap, fmt );
      41    28108308 :   int ret = vsnprintf( buf + len, rem, fmt, ap );
      42    28108308 :   va_end( ap );
      43             : 
      44    28108308 :   if( FD_UNLIKELY( ret<0 ) ) { /* Parse error */
      45           0 :     buf[len] = '\0'; /* Guarantee '\0' termination */
      46           0 :     return FD_VM_ERR_IO;
      47           0 :   }
      48             : 
      49    28108308 :   ulong append_len = (ulong)ret; /* Guaranteed safe */
      50             : 
      51    28108308 :   if( FD_UNLIKELY( append_len>=rem ) ) { /* Truncated output */
      52           0 :     buf[max-1UL] = '\0'; /* Guarantee '\0' termination */
      53           0 :     *_len = max-1UL;
      54           0 :     return FD_VM_ERR_FULL;
      55           0 :   }
      56             : 
      57    28108308 :   *_len = len + append_len;
      58    28108308 :   return FD_VM_SUCCESS;
      59    28108308 : }
      60             : 
      61             : /* OUT_PRINTF is a convenience macro to do boilerplate error trapping
      62             :    on fd_vm_disasm_printf. */
      63             : 
      64    28108308 : #define OUT_PRINTF( ... ) do {                                             \
      65    28108308 :     int _err = fd_vm_disasm_printf( out, out_max, _out_len, __VA_ARGS__ ); \
      66    28108308 :     if( FD_UNLIKELY( _err ) ) return _err;                                 \
      67    28108308 :   } while(0)
      68             : 
      69             : /* fd_vm_disasm_instr_* are pretty printers for single word instructions.
      70             :    They do not validate their input arguments.  Return out, out_max,
      71             :    _out_len and return error code have the same interpretation as their
      72             :    public facing wrappers. */
      73             : 
      74             : static int
      75             : fd_vm_disasm_instr_alu( fd_sbpf_instr_t instr,
      76             :                         char const *    suffix,
      77             :                         char *          out,
      78             :                         ulong           out_max,
      79     7491759 :                         ulong *         _out_len ) {
      80             : 
      81     7491759 :   char * op_name;
      82     7491759 :   switch( instr.opcode.normal.op_mode ) {
      83      469560 :   case FD_SBPF_OPCODE_ALU_OP_MODE_ADD:  op_name = "add";  break;
      84      466773 :   case FD_SBPF_OPCODE_ALU_OP_MODE_SUB:  op_name = "sub";  break;
      85      467028 :   case FD_SBPF_OPCODE_ALU_OP_MODE_MUL:  op_name = "mul";  break;
      86      468531 :   case FD_SBPF_OPCODE_ALU_OP_MODE_DIV:  op_name = "div";  break;
      87      468021 :   case FD_SBPF_OPCODE_ALU_OP_MODE_OR:   op_name = "or";   break;
      88      467013 :   case FD_SBPF_OPCODE_ALU_OP_MODE_AND:  op_name = "and";  break;
      89      469251 :   case FD_SBPF_OPCODE_ALU_OP_MODE_LSH:  op_name = "lsh";  break;
      90      467364 :   case FD_SBPF_OPCODE_ALU_OP_MODE_RSH:  op_name = "rsh";  break;
      91      467880 :   case FD_SBPF_OPCODE_ALU_OP_MODE_NEG:  op_name = "neg";  break;
      92      469461 :   case FD_SBPF_OPCODE_ALU_OP_MODE_MOD:  op_name = "mod";  break;
      93      468939 :   case FD_SBPF_OPCODE_ALU_OP_MODE_XOR:  op_name = "xor";  break;
      94      468300 :   case FD_SBPF_OPCODE_ALU_OP_MODE_MOV:  op_name = "mov";  break;
      95      468492 :   case FD_SBPF_OPCODE_ALU_OP_MODE_ARSH: op_name = "arsh"; break;
      96      468588 :   case FD_SBPF_OPCODE_ALU_OP_MODE_END:  op_name = "end";  break;
      97      936558 :   default: return FD_VM_ERR_INVAL;
      98     7491759 :   }
      99             : 
     100     6555201 :   if( FD_UNLIKELY( instr.opcode.normal.op_mode==FD_SBPF_OPCODE_ALU_OP_MODE_NEG ) ) {
     101      467880 :     OUT_PRINTF( "%s%s r%d", op_name, suffix, instr.dst_reg );
     102      467880 :     return FD_VM_SUCCESS;
     103      467880 :   }
     104             : 
     105     6087321 :   switch( instr.opcode.normal.op_src ) {
     106     3043260 :   case FD_SBPF_OPCODE_SOURCE_MODE_IMM:
     107     3043260 :     OUT_PRINTF( "%s%s r%d, %d",  op_name, suffix, instr.dst_reg, (int)instr.imm     );
     108     3043260 :     return FD_VM_SUCCESS;
     109     3044061 :   case FD_SBPF_OPCODE_SOURCE_MODE_REG:
     110     3044061 :     OUT_PRINTF( "%s%s r%d, r%d", op_name, suffix, instr.dst_reg, instr.src_reg );
     111     3044061 :     return FD_VM_SUCCESS;
     112           0 :   default: break;
     113     6087321 :   }
     114             : 
     115           0 :   return FD_VM_ERR_INVAL;
     116     6087321 : }
     117             : 
     118             : static int
     119             : fd_vm_disasm_instr_jmp( fd_sbpf_instr_t            instr,
     120             :                         ulong                      pc,
     121             :                         char const *               suffix,
     122             :                         fd_sbpf_syscalls_t const * syscalls,
     123             :                         char *                     out,
     124             :                         ulong                      out_max,
     125     7502163 :                         ulong *                    _out_len ) {
     126             : 
     127     7502163 :   char * op_name;
     128     7502163 :   switch( instr.opcode.normal.op_mode ) {
     129      469752 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JA:   op_name = "ja";   break;
     130      466641 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JEQ:  op_name = "jeq";  break;
     131      471039 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JGT:  op_name = "jgt";  break;
     132      467022 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JGE:  op_name = "jge";  break;
     133      470964 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JSET: op_name = "jset"; break;
     134      466776 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JNE:  op_name = "jne";  break;
     135      467802 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JSGT: op_name = "jsgt"; break;
     136      469074 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JSGE: op_name = "jsge"; break;
     137      469824 :   case FD_SBPF_OPCODE_JMP_OP_MODE_CALL: op_name = "call"; break;
     138      469584 :   case FD_SBPF_OPCODE_JMP_OP_MODE_EXIT: op_name = "exit"; break;
     139      470478 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JLT:  op_name = "jlt";  break;
     140      467139 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JLE:  op_name = "jle";  break;
     141      467706 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JSLT: op_name = "jslt"; break;
     142      468159 :   case FD_SBPF_OPCODE_JMP_OP_MODE_JSLE: op_name = "jsle"; break;
     143      940203 :   default: return FD_VM_ERR_INVAL;
     144     7502163 :   }
     145             : 
     146     6561960 :   if( FD_UNLIKELY( instr.opcode.normal.op_mode==FD_SBPF_OPCODE_JMP_OP_MODE_CALL ) ) {
     147      469824 :     switch ( instr.opcode.normal.op_src ) {
     148      234804 :     case FD_SBPF_OPCODE_SOURCE_MODE_IMM: {
     149      234804 :       fd_sbpf_syscalls_t const * syscall = syscalls ? fd_sbpf_syscalls_query_const( syscalls, instr.imm, NULL ) : NULL;
     150      234804 :       if( syscall ) { /* FIXME: THESE CODE PATHS CURRENTLY NOT EXERCISED BY UNIT TEST */
     151           0 :         char const * name = syscall->name;
     152           0 :         if( name ) OUT_PRINTF( "syscall%s %s",     suffix, name      );
     153           0 :         else       OUT_PRINTF( "syscall%s 0x%08x", suffix, instr.imm );
     154      234804 :       } else {
     155      234804 :         uint pc = fd_pchash_inverse( instr.imm ); /* FIXME: is pchash in the right place? */
     156      234804 :         if( pc<(10<<17) ) OUT_PRINTF( "%s%s function_%u",  op_name, suffix, pc        ); /* FIXME: hardcoded constant */
     157       88047 :         else              OUT_PRINTF( "%s%s function_%#x", op_name, suffix, instr.imm );
     158      234804 :       }
     159      234804 :       return FD_VM_SUCCESS;
     160      234804 :     }
     161      235020 :     case FD_SBPF_OPCODE_SOURCE_MODE_REG:
     162      235020 :       OUT_PRINTF( "%sx%s r%u", op_name, suffix, instr.imm );
     163      235020 :       return FD_VM_SUCCESS;
     164           0 :     default: break;
     165      469824 :     }
     166           0 :     return FD_VM_ERR_INVAL;
     167      469824 :   }
     168             : 
     169     6092136 :   if( FD_UNLIKELY( instr.opcode.normal.op_mode==FD_SBPF_OPCODE_JMP_OP_MODE_EXIT ) ) {
     170      469584 :     OUT_PRINTF( "%s%s", op_name, suffix );
     171      469584 :     return FD_VM_SUCCESS;
     172      469584 :   }
     173             : 
     174     5622552 :   if( FD_UNLIKELY( instr.opcode.normal.op_mode==FD_SBPF_OPCODE_JMP_OP_MODE_JA ) ) {
     175      469752 :     OUT_PRINTF( "%s%s lbb_%ld", op_name, suffix, (long)pc+(long)instr.offset+1L );
     176      469752 :     return FD_VM_SUCCESS;
     177      469752 :   }
     178             : 
     179     5152800 :   switch( instr.opcode.normal.op_src ) {
     180     2577315 :   case FD_SBPF_OPCODE_SOURCE_MODE_IMM:
     181     2577315 :     OUT_PRINTF( "%s%s r%d, %d, lbb_%ld",  op_name, suffix, instr.dst_reg, (int)instr.imm, (long)pc+(long)instr.offset+1L );
     182     2577315 :     return FD_VM_SUCCESS;
     183     2575485 :   case FD_SBPF_OPCODE_SOURCE_MODE_REG:
     184     2575485 :     OUT_PRINTF( "%s%s r%d, r%d, lbb_%ld", op_name, suffix, instr.dst_reg, instr.src_reg, (long)pc+(long)instr.offset+1L );
     185     2575485 :     return FD_VM_SUCCESS;
     186           0 :     break;
     187           0 :   default: break;
     188     5152800 :   }
     189             : 
     190           0 :   return FD_VM_ERR_INVAL;
     191     5152800 : }
     192             : 
     193             : static int
     194             : fd_vm_disasm_instr_ldx( fd_sbpf_instr_t instr,
     195             :                         char *          out,
     196             :                         ulong           out_max,
     197     3749811 :                         ulong *         _out_len ) {
     198             : 
     199     3749811 :   char * op_name;
     200     3749811 :   switch( instr.opcode.mem.op_size ) {
     201      937140 :   case FD_SBPF_OPCODE_SIZE_MODE_WORD: op_name = "ldxw";  break;
     202      935406 :   case FD_SBPF_OPCODE_SIZE_MODE_HALF: op_name = "ldxh";  break;
     203      938931 :   case FD_SBPF_OPCODE_SIZE_MODE_BYTE: op_name = "ldxb";  break;
     204      938334 :   case FD_SBPF_OPCODE_SIZE_MODE_DOUB: op_name = "ldxdw"; break;
     205           0 :   default: return FD_VM_ERR_INVAL;
     206     3749811 :   }
     207             : 
     208     3749811 :   if( instr.offset<0 ) OUT_PRINTF( "%s r%d, [r%d-0x%x]", op_name, instr.dst_reg, instr.src_reg, (ushort)-instr.offset );
     209     1876233 :   else                 OUT_PRINTF( "%s r%d, [r%d+0x%x]", op_name, instr.dst_reg, instr.src_reg, (ushort) instr.offset );
     210     3749811 :   return FD_VM_SUCCESS;
     211     3749811 : }
     212             : 
     213             : static int
     214             : fd_vm_disasm_instr_stx( fd_sbpf_instr_t instr,
     215             :                         char *          out,
     216             :                         ulong           out_max,
     217     3752208 :                         ulong *         _out_len ) {
     218             : 
     219     3752208 :   char * op_name;
     220     3752208 :   switch( instr.opcode.mem.op_size ) {
     221      939231 :   case FD_SBPF_OPCODE_SIZE_MODE_WORD: op_name = "stxw";  break;
     222      937776 :   case FD_SBPF_OPCODE_SIZE_MODE_HALF: op_name = "stxh";  break;
     223      938136 :   case FD_SBPF_OPCODE_SIZE_MODE_BYTE: op_name = "stxb";  break;
     224      937065 :   case FD_SBPF_OPCODE_SIZE_MODE_DOUB: op_name = "stxdw"; break;
     225           0 :   default: return FD_VM_ERR_INVAL;
     226     3752208 :   }
     227             : 
     228     3752208 :   if( instr.offset<0 ) OUT_PRINTF( "%s [r%d-0x%x], r%d", op_name, instr.dst_reg, (ushort)-instr.offset, instr.src_reg );
     229     1872705 :   else                 OUT_PRINTF( "%s [r%d+0x%x], r%d", op_name, instr.dst_reg, (ushort) instr.offset, instr.src_reg );
     230     3752208 :   return FD_VM_SUCCESS;
     231     3752208 : }
     232             : 
     233             : int
     234             : fd_vm_disasm_instr( ulong const *              text,
     235             :                     ulong                      text_cnt,
     236             :                     ulong                      pc,
     237             :                     fd_sbpf_syscalls_t const * syscalls,
     238             :                     char *                     out,
     239             :                     ulong                      out_max,
     240    30000033 :                     ulong *                    _out_len ) {
     241             : 
     242    30000033 :   if( FD_UNLIKELY( (!text) | (!text_cnt) | (!out) | (!out_max) | (!_out_len) ) ) return FD_VM_ERR_INVAL;
     243    30000018 :   if( FD_UNLIKELY( (*_out_len)>=out_max ) ) return FD_VM_ERR_INVAL;
     244             : 
     245    30000015 :   fd_sbpf_instr_t i0 = fd_sbpf_instr( text[0] );
     246             : 
     247    30000015 :   switch( i0.opcode.any.op_class ) {
     248             : 
     249     3753156 :   case FD_SBPF_OPCODE_CLASS_LD: {
     250     3753156 :     if( FD_UNLIKELY( text_cnt<2UL ) ) return FD_VM_ERR_INVAL;
     251     3738210 :     fd_sbpf_instr_t i1 = fd_sbpf_instr( text[1] );
     252             :     /* FIXME: VALIDATE I1 IS PROPER */
     253     3738210 :     OUT_PRINTF( "lddw r%d, 0x%lx", i0.dst_reg, (ulong)((ulong)i0.imm | (ulong)((ulong)i1.imm << 32UL)) );
     254     3738210 :     return FD_VM_SUCCESS;
     255     3738210 :   }
     256             : 
     257     3750918 :   case FD_SBPF_OPCODE_CLASS_ST: { /* FIXME: FIGURE OUT WHAT'S UP HERE */
     258     3750918 :     OUT_PRINTF( "FIXME: %016lx (ST)", text[0] );
     259     3750918 :     return FD_VM_SUCCESS;
     260     3750918 :   }
     261             : 
     262     3749811 :   case FD_SBPF_OPCODE_CLASS_LDX:   return fd_vm_disasm_instr_ldx( i0,                     out, out_max, _out_len);
     263     3752208 :   case FD_SBPF_OPCODE_CLASS_STX:   return fd_vm_disasm_instr_stx( i0,                     out, out_max, _out_len );
     264     3747018 :   case FD_SBPF_OPCODE_CLASS_ALU:   return fd_vm_disasm_instr_alu( i0, "",                 out, out_max, _out_len );
     265     3751272 :   case FD_SBPF_OPCODE_CLASS_JMP:   return fd_vm_disasm_instr_jmp( i0, pc, "",   syscalls, out, out_max, _out_len );
     266     3750891 :   case FD_SBPF_OPCODE_CLASS_JMP32: return fd_vm_disasm_instr_jmp( i0, pc, "32", syscalls, out, out_max, _out_len );
     267     3744741 :   case FD_SBPF_OPCODE_CLASS_ALU64: return fd_vm_disasm_instr_alu( i0, "64",               out, out_max, _out_len );
     268           0 :   default: break;
     269    30000015 :   }
     270           0 :   return FD_VM_ERR_INVAL;
     271    30000015 : }
     272             : 
     273             : int
     274             : fd_vm_disasm_program( ulong const *              text,
     275             :                       ulong                      text_cnt,
     276             :                       fd_sbpf_syscalls_t const * syscalls,
     277             :                       char *                     out,
     278             :                       ulong                      out_max,
     279          15 :                       ulong *                    _out_len ) {
     280             : 
     281          15 :   if( FD_UNLIKELY( ((!text) & (!!text_cnt)) | (!out) | (!out_max) | (!_out_len) ) ) return FD_VM_ERR_INVAL;
     282           3 :   if( FD_UNLIKELY( (*_out_len)>=out_max ) ) return FD_VM_ERR_INVAL;
     283             : 
     284             :   /* Construct the mapping of pc to labels and functions.  FIXME: This
     285             :      is currently not an algo efficient implementation.  Note: if the
     286             :      same instruction is the targeted by multiple calls / exits / jmps,
     287             :      it will appear multiple times in the label_pc and/or func_pc
     288             :      arrays.  But that's okay because use the target instruction as the
     289             :      label and function name. */
     290             : 
     291           0 :   ulong func_pc [ 65536 ]; ulong func_cnt  = 0UL;
     292           0 :   ulong label_pc[ 65536 ]; ulong label_cnt = 0UL;
     293             : 
     294           0 :   for( ulong i=0UL; i<text_cnt; i++ ) {
     295           0 :     fd_sbpf_instr_t instr = fd_sbpf_instr( text[i] );
     296           0 :     if     ( instr.opcode.raw==FD_SBPF_OP_CALL_IMM ) func_cnt++;
     297           0 :     else if( instr.opcode.raw==FD_SBPF_OP_EXIT     ) func_cnt++;
     298           0 :     else if( instr.opcode.raw==FD_SBPF_OP_CALL_REG ) continue;
     299           0 :     else if( ( (instr.opcode.any.op_class==FD_SBPF_OPCODE_CLASS_JMP  ) |
     300           0 :                (instr.opcode.any.op_class==FD_SBPF_OPCODE_CLASS_JMP32) ) ) label_cnt++;
     301           0 :   }
     302             : 
     303           0 :   if( FD_UNLIKELY( (func_cnt>65536UL) | (label_cnt>65536UL) ) ) return FD_VM_ERR_UNSUP;
     304             : 
     305           0 :   func_cnt  = 0UL;
     306           0 :   label_cnt = 0UL;
     307             : 
     308           0 :   for( ulong i=0UL; i<text_cnt; i++ ) {
     309           0 :     fd_sbpf_instr_t instr = fd_sbpf_instr( text[i] );
     310           0 :     if     ( instr.opcode.raw==FD_SBPF_OP_CALL_IMM ) func_pc[ func_cnt++ ] = i + instr.imm + 1UL; /* FIXME: what if out of bounds? */
     311           0 :     else if( instr.opcode.raw==FD_SBPF_OP_EXIT     ) func_pc[ func_cnt++ ] = i + instr.imm + 1UL; /* FIXME: what if out of bounds? */
     312           0 :     else if( instr.opcode.raw==FD_SBPF_OP_CALL_REG ) continue;
     313           0 :     else if( ( (instr.opcode.any.op_class==FD_SBPF_OPCODE_CLASS_JMP  ) |
     314           0 :                (instr.opcode.any.op_class==FD_SBPF_OPCODE_CLASS_JMP32) ) )
     315           0 :       label_pc[ label_cnt++ ] = (ulong)((long)i + (long)instr.offset + 1L); /* FIXME: casting and what if out of bounds? */
     316           0 :   }
     317             : 
     318             :   /* Output the program */
     319             : 
     320           0 :   OUT_PRINTF( "function_0:\n" );
     321             : 
     322           0 :   for( ulong i=0UL; i<text_cnt; i++ ) {
     323             : 
     324             :     /* Print functions / labels */
     325             :     /* FIXME: What if there is a func_pc and a label_pc that target
     326             :        for the same instruction?  It is possible given the above logic.
     327             :        Probably should print both. */
     328             :     /* FIXME: Algo efficiency! */
     329             : 
     330           0 :     int found = 0;
     331           0 :     for( ulong j=0UL; j<label_cnt; j++ ) if( label_pc[j]==i ) { found = 1; OUT_PRINTF( "lbb_%lu:\n", i ); break; }
     332           0 :     if( !found ) for( ulong j=0UL; j<func_cnt; j++ ) if( func_pc[j]==i ) { OUT_PRINTF( "\nfunction_%lu:\n", i ); break; }
     333             : 
     334             :     /* Print instruction */
     335             : 
     336             :     /* FIXME: WHAT ABOUT LABELS IN THE MIDDLE OF MULTIWORD INSTRUCTIONS!
     337             :        AND NOT JUST FOR DISASSEMBLY ... POTENTIAL CONSENSUS FAILURE
     338             :        MECHANISM! */
     339             : 
     340           0 :     fd_sbpf_instr_t instr = fd_sbpf_instr( text[i] );
     341           0 :     ulong extra_cnt = fd_ulong_if( instr.opcode.any.op_class==FD_SBPF_OPCODE_CLASS_LD, 1UL, 0UL );
     342           0 :     if( FD_UNLIKELY( (i+extra_cnt)>=text_cnt ) ) return FD_VM_ERR_INVAL; /* Truncated multiword instruction at end of text */
     343             : 
     344           0 :     OUT_PRINTF( "    " );
     345           0 :     int err = fd_vm_disasm_instr( text+i, text_cnt-i, i, syscalls, out, out_max, _out_len );
     346           0 :     if( FD_UNLIKELY( err ) ) return err;
     347           0 :     OUT_PRINTF( "\n" );
     348             : 
     349           0 :     i += extra_cnt;
     350             : 
     351             :     /* Print any trailing function */
     352             :     /* FIXME: this is probably not necessary if the function/label print
     353             :        above just prints both, unless trying print a function label that
     354             :        happens to immediately after the end of a program. */
     355             :     /* FIXME: Algo efficiency? */
     356             : 
     357           0 :     if( FD_UNLIKELY( (instr.opcode.raw==FD_SBPF_OP_JA) & ((i+1UL)<text_cnt) ) ) {
     358           0 :       found = 0;
     359           0 :       for( ulong j=0UL; j<label_cnt; j++ ) if( label_pc[j]==(i+1UL) ) { found = 1; break; }
     360           0 :       if( !found ) OUT_PRINTF( "\nfunction_%lu:\n", i+1UL );
     361           0 :     }
     362           0 :   }
     363             : 
     364           0 :   return FD_VM_SUCCESS;
     365           0 : }
     366             : 
     367             : #undef OUT_PRINTF

Generated by: LCOV version 1.14