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 }