kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

aa64_isa_test.c (8255B)


      1 /* Phase-2 unit test for arch/aa64_isa.{h,c}.
      2  *
      3  * Asserts that aa64_disasm_find recognizes a representative word for
      4  * every AA64Format the descriptor table covers, that the resolved row's
      5  * mnemonic matches the expected string, and that aa64_print_operands
      6  * produces non-empty output.  Also exercises the alias-precedence
      7  * invariant: an alias-bearing word (e.g. ORR Rd, ZR, Rm) resolves to
      8  * the alias spelling (MOV) rather than the canonical row.
      9  *
     10  * Builds against the internal arch/aa64/isa.h surface (mk/test.mk passes
     11  * -Isrc).  No public-API dependency — this is a unit test of the
     12  * descriptor table itself. */
     13 
     14 #include <stdio.h>
     15 #include <string.h>
     16 
     17 #include "arch/aa64/isa.h"
     18 #include "core/strbuf.h"
     19 #include "lib/kit_unit.h"
     20 
     21 /* Shared test context replaces the per-file fails/cases counter globals; each
     22  * check() invocation records exactly one case on g_u (one ++g_u.checks, plus
     23  * ++g_u.fails on a miss) so the "N cases ok" tally and exit status are
     24  * unchanged. */
     25 static KitUnit g_u;
     26 
     27 static void check(u32 word, const char* want_mnem,
     28                   const char* want_ops_substr) {
     29   ++g_u.checks;
     30   const AA64InsnDesc* d = aa64_disasm_find(word);
     31   if (!d) {
     32     fprintf(stderr, "FAIL 0x%08x: aa64_disasm_find returned NULL\n", word);
     33     ++g_u.fails;
     34     return;
     35   }
     36   if (!slice_eq_cstr(d->mnemonic, want_mnem)) {
     37     fprintf(stderr, "FAIL 0x%08x: mnemonic = %.*s, want %s\n", word,
     38             SLICE_ARG(d->mnemonic), want_mnem);
     39     ++g_u.fails;
     40     return;
     41   }
     42   char buf[128];
     43   StrBuf sb;
     44   strbuf_init(&sb, buf, sizeof buf);
     45   aa64_print_operands(&sb, d, word, /*vaddr=*/0);
     46   if (sb.truncated) {
     47     fprintf(stderr, "FAIL 0x%08x %s: operand print truncated\n", word,
     48             want_mnem);
     49     ++g_u.fails;
     50     return;
     51   }
     52   if (want_ops_substr && !strstr(buf, want_ops_substr)) {
     53     fprintf(stderr, "FAIL 0x%08x %s: operands=%s\n  expected substring %s\n",
     54             word, want_mnem, buf, want_ops_substr);
     55     ++g_u.fails;
     56     return;
     57   }
     58 }
     59 
     60 int main(void) {
     61   kit_unit_init(&g_u);
     62 
     63   /* ---- per-format coverage ---- */
     64 
     65   /* MOVEWIDE: movz x0, #0x1234 → sf=1, opc=10, hw=0, imm16=0x1234, Rd=0 */
     66   check(aa64_movz(1, /*Rd=*/0, /*imm16=*/0x1234, /*hw=*/0), "movz", "x0");
     67 
     68   /* MOVEWIDE alias: movk w3, #0xABCD, lsl #16 */
     69   check(aa64_movk(0, /*Rd=*/3, /*imm16=*/0xABCD, /*hw=*/1), "movk", "lsl");
     70 
     71   /* LOG_SR alias: MOV X1, X2 ≡ ORR X1, XZR, X2 */
     72   check(aa64_mov_reg(1, /*Rd=*/1, /*Rm=*/2), "mov", "x1");
     73 
     74   /* LOG_SR canonical: AND X1, X2, X3 */
     75   check(aa64_and(1, 1, 2, 3), "and", "x1, x2, x3");
     76 
     77   /* LOG_SR canonical: EOR W4, W5, W6 */
     78   check(aa64_eor(0, 4, 5, 6), "eor", "w4, w5, w6");
     79 
     80   /* LOG_SR alias: MVN X1, X2 ≡ ORN X1, XZR, X2 */
     81   check(aa64_mvn(1, 1, 2), "mvn", "x1");
     82 
     83   /* ADDSUB_SR alias: NEG X1, X2 ≡ SUB X1, XZR, X2 */
     84   check(aa64_neg(1, 1, 2), "neg", "x1");
     85 
     86   /* ADDSUB_SR canonical: ADD X3, X4, X5 */
     87   check(aa64_add(1, 3, 4, 5), "add", "x3, x4, x5");
     88   check(aa64_sub(0, 3, 4, 5), "sub", "w3, w4, w5");
     89 
     90   /* DP3 alias: MUL X1, X2, X3 ≡ MADD X1, X2, X3, XZR */
     91   check(aa64_mul(1, 1, 2, 3), "mul", "x1, x2, x3");
     92   /* DP3 canonical: MADD X1, X2, X3, X4 */
     93   check(aa64_madd(1, 1, 2, 3, 4), "madd", "x1, x2, x3, x4");
     94 
     95   /* DP2: UDIV X1, X2, X3 */
     96   check(aa64_udiv(1, 1, 2, 3), "udiv", "x1, x2, x3");
     97   check(aa64_lslv(0, 1, 2, 3), "lslv", "w1, w2, w3");
     98 
     99   /* CONDSEL: CSEL X1, X2, X3, EQ and CSET W4, NE. */
    100   check(aa64_csel_enc(1, 1, 2, 3, 0), "csel", "x1, x2, x3, eq");
    101   check(aa64_csinc_enc(0, 4, AA64_ZR, AA64_ZR, 0), "cset", "w4, ne");
    102   check(aa64_csinv_enc(1, 5, AA64_ZR, AA64_ZR, 1), "csetm", "x5, eq");
    103 
    104   /* BR_REG alias: RET (with implicit X30). */
    105   check(aa64_ret(/*Rn=*/30), "ret", NULL);
    106   /* BR_REG: BR X16 */
    107   check(aa64_br(16), "br", "x16");
    108   check(aa64_blr(17), "blr", "x17");
    109 
    110   /* PCREL_ADR: ADR / ADRP — encode imm halves as zero. */
    111   check(aa64_adr(/*Rd=*/9, 0, 0), "adr", "x9");
    112   check(aa64_adrp(/*Rd=*/9, 0, 0), "adrp", "x9");
    113 
    114   /* ADDSUB_IMM: ADD X1, X2, #0x10 */
    115   check(aa64_add_imm(1, 1, 2, 0x10, 0), "add", "x1, x2, #16");
    116   check(aa64_sub_imm(1, 1, 2, 0x10, 0), "sub", "x1, x2, #16");
    117   check(aa64_subs_imm12(1, AA64_ZR, 2, 0x10, 0), "cmp", "x2, #16");
    118   check(aa64_addsubimm_pack((AA64AddSubImm){.sf = 0,
    119                                             .op = 0,
    120                                             .S = 1,
    121                                             .sh = 1,
    122                                             .imm12 = 1,
    123                                             .Rn = AA64_SP,
    124                                             .Rd = AA64_ZR}),
    125         "cmn", "sp, #1, lsl #12");
    126 
    127   /* LDST_UIMM (size=11, V=0): LDR X1, [X2, #8]   (encoded imm12=1, scale=8) */
    128   check(aa64_ldr64_uimm12(/*Rt=*/1, /*Rn=*/2, /*imm12_scaled=*/1), "ldr",
    129         "x1, [x2, #8]");
    130   check(aa64_str64_uimm12(/*Rt=*/1, /*Rn=*/2, /*imm12_scaled=*/0), "str",
    131         "x1, [x2]");
    132 
    133   /* LDSTP_PRE: STP X29, X30, [SP, #-16]! (imm7=-2) */
    134   check(aa64_stp64_pre(/*Rt=*/29, /*Rt2=*/30, /*Rn=*/31, /*imm7=*/-2), "stp",
    135         "x29, x30, [sp, #-16]!");
    136   check(aa64_ldp64_pre(/*Rt=*/29, /*Rt2=*/30, /*Rn=*/31, /*imm7=*/2), "ldp",
    137         "x29, x30, [sp, #16]!");
    138 
    139   /* LDSTP_SOFF: STP X29, X30, [SP, #16]  (signed-offset variant of pair) */
    140   check(
    141       aa64_ldstp_soff_pack((AA64LdStPSOff){
    142           .opc = 2, .V = 0, .L = 0, .imm7 = 2, .Rt2 = 30, .Rn = 31, .Rt = 29}),
    143       "stp", "x29, x30, [sp, #16]");
    144 
    145   /* LDST_SIMM9 (V=0, size=3): STUR X1, [X2, #-8] */
    146   check(aa64_ldst_simm9_pack((AA64LdStSimm9){.size = 3,
    147                                              .V = 0,
    148                                              .opc = 0,
    149                                              .imm9 = (u32)(-8) & 0x1ffu,
    150                                              .Rn = 2,
    151                                              .Rt = 1}),
    152         "stur", "x1, [x2, #-8]");
    153   check(aa64_ldst_simm9_pack((AA64LdStSimm9){
    154             .size = 3, .V = 0, .opc = 1, .imm9 = 0, .Rn = 2, .Rt = 1}),
    155         "ldur", "x1, [x2]");
    156 
    157   /* BR_IMM: B / BL.  imm26=0 (relocated) — print should still resolve. */
    158   check(aa64_b(0), "b", NULL);
    159   check(aa64_bl(0), "bl", NULL);
    160 
    161   /* BR_COND: B.eq #0 — cond=EQ=0, imm19=0. */
    162   check(0x54000000u, "b.cond", "eq");
    163 
    164   /* CB: CBZ W1, #0  /  CBNZ X2, #0 */
    165   check(aa64_cbz(0, 1, 0), "cbz", "w1");
    166   check(aa64_cbnz_imm(1, 2, 0), "cbnz", "x2");
    167 
    168   /* EXCEPT: BRK #0, SVC #0 */
    169   check(aa64_brk(0), "brk", "0x0");
    170   check(aa64_svc(0), "svc", "0x0");
    171 
    172   /* HINT: NOP */
    173   check(aa64_nop(), "nop", NULL);
    174 
    175   /* ---- alias precedence: ensure first-match wins ---- */
    176   {
    177     /* ORR X1, XZR, X2 with shift=0,imm6=0 should resolve to "mov", not "orr".
    178      */
    179     u32 w = aa64_mov_reg(1, 1, 2);
    180     const AA64InsnDesc* d = aa64_disasm_find(w);
    181     if (d == NULL || !slice_eq_cstr(d->mnemonic, "mov")) {
    182       fprintf(
    183           stderr,
    184           "FAIL: alias precedence — ORR-as-MOV resolved to %.*s (want mov)\n",
    185           SLICE_ARG(d ? d->mnemonic : SLICE_LIT("(null)")));
    186       ++g_u.fails;
    187     } else if (!(d->flags & AA64_ASMFL_ALIAS)) {
    188       fprintf(stderr, "FAIL: alias precedence — resolved row missing ALIAS\n");
    189       ++g_u.fails;
    190     }
    191     ++g_u.checks;
    192   }
    193 
    194   /* ORR X1, X3, X2 (Rn != ZR) must not resolve to "mov". */
    195   {
    196     u32 w = aa64_orr(1, 1, 3, 2);
    197     const AA64InsnDesc* d = aa64_disasm_find(w);
    198     if (d == NULL || !slice_eq_cstr(d->mnemonic, "orr")) {
    199       fprintf(stderr, "FAIL: non-alias ORR resolved to %.*s (want orr)\n",
    200               SLICE_ARG(d ? d->mnemonic : SLICE_LIT("(null)")));
    201       ++g_u.fails;
    202     }
    203     ++g_u.checks;
    204   }
    205 
    206   /* Logical immediates: 0xff00 must not be encoded as the rotated
    207    * 0xff000000 mask. */
    208   {
    209     u32 N = 0, immr = 0, imms = 0;
    210     ++g_u.checks;
    211     if (!aa64_logimm_encode(0xff00u, 0, &N, &immr, &imms)) {
    212       fprintf(stderr, "FAIL: aa64_logimm_encode rejected 0xff00\n");
    213       ++g_u.fails;
    214     } else {
    215       u32 w = aa64_and_imm(0, 19, 19, N, immr, imms);
    216       if (w != 0x12181e73u) {
    217         fprintf(stderr, "FAIL: and w19,w19,#0xff00 encoded 0x%08x\n", w);
    218         ++g_u.fails;
    219       }
    220     }
    221   }
    222 
    223   if (g_u.fails) {
    224     fprintf(stderr, "%d / %d failed\n", g_u.fails, g_u.checks);
    225     return 1;
    226   }
    227   printf("aa64_isa_test: %d cases ok\n", g_u.checks);
    228   return 0;
    229 }