userland: start refactor to show failing values on failure!

aarch64 basically done, but missing:

- other archs
- maybe convert main.c into C++ to use templates?
- full review of ASSERT_EQ calling convention issues not seen by tests
  by chance
- documentation
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-05-05 00:00:00 +00:00
parent 5711e85e70
commit f176d04500
14 changed files with 240 additions and 163 deletions

View File

@@ -4,31 +4,55 @@
#define COMMON_ARCH_H #define COMMON_ARCH_H
#define ASSERT_EQ(reg, const) \ #define ASSERT_EQ(reg, const) \
ldr x11, =const; \ mov x0, reg; \
cmp reg, x11; \ ldr x1, =const; \
ASSERT_EQ_DO(64); \
;
#define ASSERT_EQ_DO(bits) \
bl assert_eq_ ## bits; \
cmp x0, 0; \
ASSERT(beq); \ ASSERT(beq); \
; ;
#define ASSERT_MEMCMP(s1, s2, n) \ #define ASSERT_EQ_REG(reg1, reg2) \
MEMCMP(s1, s2, n); \ str reg2, [sp, -8]!; \
ASSERT_EQ(x0, 0); \ mov x0, reg1; \
ldr x1, [sp], 8; \
ASSERT_EQ_DO(64); \
;
#define ASSERT_EQ_REG_32(reg1, reg2) \
str reg2, [sp, -4]!; \
mov w0, reg1; \
ldr w1, [sp], 4; \
ASSERT_EQ_DO(32); \
;
#define ASSERT_MEMCMP(label1, label2, const_size) \
adr x0, label1; \
adr x1, label2; \
ldr x2, =const_size; \
bl assert_memcmp; \
cmp x0, 0; \
ASSERT(beq); \
; ;
#define ENTRY \ #define ENTRY \
.text; \ .text; \
.global asm_main; \ .global asm_main; \
asm_main: \ asm_main: \
sub sp, sp, 0xA0; \ sub sp, sp, 0xA0; \
stp x29, x30, [sp]; \ stp x29, x30, [sp]; \
stp x27, x28, [sp, 0x10]; \ stp x27, x28, [sp, 0x10]; \
stp x25, x26, [sp, 0x20]; \ stp x25, x26, [sp, 0x20]; \
stp x23, x24, [sp, 0x30]; \ stp x23, x24, [sp, 0x30]; \
stp x21, x22, [sp, 0x40]; \ stp x21, x22, [sp, 0x40]; \
stp x19, x20, [sp, 0x50]; \ stp x19, x20, [sp, 0x50]; \
stp x6, x7, [sp, 0x60]; \ stp x6, x7, [sp, 0x60]; \
stp x4, x5, [sp, 0x70]; \ stp x4, x5, [sp, 0x70]; \
stp x2, x3, [sp, 0x80]; \ stp x2, x3, [sp, 0x80]; \
stp x0, x1, [sp, 0x90]; \ stp x0, x1, [sp, 0x90]; \
asm_main_after_prologue: \ asm_main_after_prologue: \
; ;
@@ -56,11 +80,4 @@ pass: \
b fail; \ b fail; \
; ;
#define MEMCMP(s1, s2, n) \
adr x0, s1; \
adr x1, s2; \
ldr x2, =n; \
bl memcmp; \
;
#endif #endif

View File

@@ -4,25 +4,25 @@
ENTRY ENTRY
/* Test values. */ /* Test values. */
mov x0, 0 mov x19, 0
mov x1, 1 mov x20, 1
/* eq is true, set x2 = 1. */ /* eq is true, set x21 = 1. */
cmp x0, x0 cmp x19, x19
cset x2, eq cset x21, eq
ASSERT_EQ(x2, 1) ASSERT_EQ(x21, 1)
/* eq is false, set x2 = 0. */ /* eq is false, set x21 = 0. */
cmp x0, x1 cmp x19, x20
cset x2, eq cset x21, eq
ASSERT_EQ(x2, 0) ASSERT_EQ(x21, 0)
/* Same for ne. */ /* Same for ne. */
cmp x0, x0 cmp x19, x19
cset x2, ne cset x21, ne
ASSERT_EQ(x2, 0) ASSERT_EQ(x21, 0)
cmp x0, x1 cmp x19, x20
cset x2, ne cset x21, ne
ASSERT_EQ(x2, 1) ASSERT_EQ(x21, 1)
EXIT EXIT

View File

@@ -3,26 +3,26 @@
#include "common.h" #include "common.h"
ENTRY ENTRY
mov x0, 1 mov x19, 1
bl inc bl inc
ASSERT_EQ(x0, 2) ASSERT_EQ(x19, 2)
bl inc2 bl inc2
ASSERT_EQ(x0, 3) ASSERT_EQ(x19, 3)
bl inc3 bl inc3
ASSERT_EQ(x0, 4) ASSERT_EQ(x19, 4)
EXIT EXIT
/* void inc(uint64_t *i) { (*i)++ } */ /* void inc(uint64_t *i) { (*i)++ } */
inc: inc:
add x0, x0, 1 add x19, x19, 1
ret ret
/* Same but explicit return register. */ /* Same but explicit return register. */
inc2: inc2:
add x0, x0, 1 add x19, x19, 1
ret x30 ret x30
/* Same but with br. */ /* Same but with br. */
inc3: inc3:
add x0, x0, 1 add x19, x19, 1
br x30 br x30

View File

@@ -3,15 +3,15 @@
#include "common.h" #include "common.h"
ENTRY ENTRY
ldr x0, =0x1122334455667788 ldr x19, =0x1122334455667788
// lsr alias: imms == 63 // lsr alias: imms == 63
ldr x1, =0xFFFFFFFFFFFFFFFF ldr x20, =0xFFFFFFFFFFFFFFFF
ubfm x1, x0, 16, 63 ubfm x20, x19, 16, 63
ASSERT_EQ(x1, 0x0000112233445566) ASSERT_EQ(x20, 0x0000112233445566)
ldr x1, =0xFFFFFFFFFFFFFFFF ldr x20, =0xFFFFFFFFFFFFFFFF
ubfm x1, x0, 32, 63 ubfm x20, x19, 32, 63
ASSERT_EQ(x1, 0x0000000011223344) ASSERT_EQ(x20, 0x0000000011223344)
EXIT EXIT

View File

@@ -3,13 +3,13 @@
#include "common.h" #include "common.h"
ENTRY ENTRY
ldr x0, =0x1122334455667788 ldr x19, =0x1122334455667788
ldr x1, =0xFFFFFFFFFFFFFFFF ldr x20, =0xFFFFFFFFFFFFFFFF
ubfx x1, x0, 8, 16 ubfx x20, x19, 8, 16
ASSERT_EQ(x1, 0x0000000000006677) ASSERT_EQ(x20, 0x0000000000006677)
ldr x1, =0xFFFFFFFFFFFFFFFF ldr x20, =0xFFFFFFFFFFFFFFFF
ubfx x1, x0, 8, 32 ubfx x20, x19, 8, 32
ASSERT_EQ(x1, 0x0000000044556677) ASSERT_EQ(x20, 0x0000000044556677)
EXIT EXIT

View File

@@ -9,14 +9,14 @@ ENTRY
#endif #endif
/* mov (register) is an alias for ORR, which accepts xzr. */ /* mov (register) is an alias for ORR, which accepts xzr. */
mov x0, 1 mov x19, 1
mov x0, xzr mov x19, xzr
ASSERT_EQ(x0, 0) ASSERT_EQ(x19, 0)
/* Same encoding as the mov version. */ /* Same encoding as the mov version. */
mov x0, 1 mov x19, 1
orr x0, xzr, xzr orr x19, xzr, xzr
ASSERT_EQ(x0, 0) ASSERT_EQ(x19, 0)
/* So, orr, which is not an alias, can only take xzr, not sp. */ /* So, orr, which is not an alias, can only take xzr, not sp. */
#if 0 #if 0
@@ -24,19 +24,19 @@ ENTRY
#endif #endif
/* Zero register discards result if written to. */ /* Zero register discards result if written to. */
mov x0, 1 mov x19, 1
orr xzr, x0, x0 orr xzr, x19, x19
ASSERT_EQ(xzr, 0) ASSERT_EQ(xzr, 0)
/* MOV (to/from SP) is an alias for ADD (immediate). */ /* MOV (to/from SP) is an alias for ADD (immediate). */
mov x0, sp mov x19, sp
mov sp, 1 mov sp, 1
/* Alias to add. */ /* Alias to add. */
mov x1, sp mov x20, sp
/* Exact same encoding as above. */ /* Exact same encoding as above. */
add x1, sp, 0 add x20, sp, 0
ASSERT_EQ(x1, 1) mov sp, x19
mov sp, x0 ASSERT_EQ(x20, 1)
/* So, ADD (immediate), which is not an alias, can only take sp, not xzr. */ /* So, ADD (immediate), which is not an alias, can only take sp, not xzr. */
#if 0 #if 0

View File

@@ -4,48 +4,48 @@
ENTRY ENTRY
/* Offset mode with immediate. Add 4 to the address register, which ends up /* Offset mode with immediate. Add 4 to the address register,
* reading myvar2 instead of myvar. * which ends up * reading myvar6 instead of myvar.
*/ */
adr r0, myvar adr r4, myvar
ldr r1, [r0, 4] ldr r5, [r4, 4]
ASSERT_EQ(r1, 0x9ABCDEF0) ASSERT_EQ(r5, 0x9ABCDEF0)
/* r0 was not modified. */ /* r4 was not modified. */
ASSERT_EQ(r0, myvar) ASSERT_EQ(r4, myvar)
/* Pre-indexed mode */ /* Pre-indexed mode: modify register, then use it. */
adr r0, myvar adr r4, myvar
ldr r1, [r0, 4]! ldr r5, [r4, 4]!
ASSERT_EQ(r1, 0x9ABCDEF0) ASSERT_EQ(r5, 0x9ABCDEF0)
/* r0 was modified. */ /* r4 was modified. */
ASSERT_EQ(r0, myvar2) ASSERT_EQ(r4, myvar6)
/* Post-indexed mode */ /* Post-indexed mode: use register, then modify it. */
adr r0, myvar adr r4, myvar
ldr r1, [r0], 4 ldr r5, [r4], 4
ASSERT_EQ(r1, 0x12345678) ASSERT_EQ(r5, 0x12345678)
/* r0 was modified. */ /* r4 was modified. */
ASSERT_EQ(r0, myvar2) ASSERT_EQ(r4, myvar6)
/* Offset in register. */ /* Offset in register. */
adr r0, myvar adr r4, myvar
mov r1, 4 mov r5, 4
ldr r2, [r0, r1] ldr r6, [r4, r5]
ASSERT_EQ(r2, 0x9ABCDEF0) ASSERT_EQ(r6, 0x9ABCDEF0)
/* Offset in shifted register: /* Offset in shifted register:
* r2 = * r6 =
* (r0 + (r1 << 1)) * (r4 + (r5 << 1))
* == *(myvar + (2 << 1)) * == *(myvar + (2 << 1))
* == *(myvar + 4) * == *(myvar + 4)
*/ */
adr r0, myvar adr r4, myvar
mov r1, 2 mov r5, 2
ldr r2, [r0, r1, lsl 1] ldr r6, [r4, r5, lsl 1]
ASSERT_EQ(r2, 0x9ABCDEF0) ASSERT_EQ(r6, 0x9ABCDEF0)
EXIT EXIT
myvar: myvar:
.word 0x12345678 .word 0x12345678
myvar2: myvar6:
.word 0x9ABCDEF0 .word 0x9ABCDEF0

View File

@@ -6,16 +6,16 @@
data_label: data_label:
.word 0x1234678 .word 0x1234678
ENTRY ENTRY
adr r0, label adr r4, label
/* objdump tells us that this uses the literal pool, /* objdump tells us that this uses the literal pool,
* it does not get converted to adr, which is the better * it does not get converted to adr, which is the better
* alternative here. * alternative here.
*/ */
ldr r1, =label ldr r5, =label
adrl r2, label adrl r6, label
label: label:
ASSERT_EQ_REG(r0, r1) ASSERT_EQ_REG(r4, r5)
ASSERT_EQ_REG(r0, r2) ASSERT_EQ_REG(r4, r6)
#if 0 #if 0
/* Error: symbol .data is in a different section. /* Error: symbol .data is in a different section.
@@ -28,6 +28,6 @@ label:
* If you have no idea what I'm talking about, read this: * If you have no idea what I'm talking about, read this:
* https://stackoverflow.com/questions/3322911/what-do-linkers-do/33690144#33690144 * https://stackoverflow.com/questions/3322911/what-do-linkers-do/33690144#33690144
*/ */
adr r1, data_label adr r5, data_label
#endif #endif
EXIT EXIT

View File

@@ -6,20 +6,37 @@
.syntax unified .syntax unified
/* Assert that a register equals a constant. /* Assert that a register equals a constant.
* * reg: the register to check. Can be r0-r10, but not r11. r11 is overwritten. * * reg: the register to check
* * const: the constant to compare to. Only works for literals or labels, not for registers. * * const: the constant to compare to. Only works for literals or labels, not for registers.
* For register / register comparision, use ASSERT_EQ_REG. * For register / register comparison, use ASSERT_EQ_REG.
*/ */
#define ASSERT_EQ(reg, const) \ #define ASSERT_EQ(reg, const) \
ldr r11, =const; \ mov r0, reg; \
cmp reg, r11; \ ldr r1, =const; \
ASSERT_EQ_DO; \
;
#define ASSERT_EQ_DO \
bl assert_eq_32; \
cmp r0, 0; \
ASSERT(beq); \ ASSERT(beq); \
; ;
#define ASSERT_EQ_REG(reg1, reg2) \
str reg2, [sp, -4]!; \
mov r0, reg1; \
ldr r1, [sp], 4; \
ASSERT_EQ_DO; \
;
/* Assert that two arrays are the same. */ /* Assert that two arrays are the same. */
#define ASSERT_MEMCMP(s1, s2, n) \ #define ASSERT_MEMCMP(label1, label2, const_size) \
MEMCMP(s1, s2, n); \ ldr r0, =label1; \
ASSERT_EQ(r0, 0); \ ldr r1, =label2; \
ldr r2, =const_size; \
bl assert_memcmp; \
cmp r0, 0; \
ASSERT(beq); \
; ;
/* Store all callee saved registers, and LR in case we make further BL calls. /* Store all callee saved registers, and LR in case we make further BL calls.

View File

@@ -13,20 +13,20 @@ my_array_1:
ENTRY ENTRY
/* Load r1, r2, r3 and r4 starting from the address in r0. Don't change r0 */ /* Load r5, r6, r7 and r8 starting from the address in r4. Don't change r4 */
ldr r0, =my_array_0 ldr r4, =my_array_0
ldr r1, =0 ldr r5, =0
ldr r2, =0 ldr r6, =0
ldr r3, =0 ldr r7, =0
ldr r4, =0 ldr r8, =0
ldmia r0, {r1-r4} ldmia r4, {r5-r8}
ASSERT_EQ(r0, my_array_0) ASSERT_EQ(r4, my_array_0)
ASSERT_EQ(r1, 0x11111111) ASSERT_EQ(r5, 0x11111111)
ASSERT_EQ(r2, 0x22222222) ASSERT_EQ(r6, 0x22222222)
ASSERT_EQ(r3, 0x33333333) ASSERT_EQ(r7, 0x33333333)
ASSERT_EQ(r4, 0x44444444) ASSERT_EQ(r8, 0x44444444)
/* Swapping the order of r1 and r2 on the mnemonic makes no difference to load order. /* Swapping the order of r5 and r6 on the mnemonic makes no difference to load order.
* *
* But it gives an assembler warning, so we won't do it by default: * But it gives an assembler warning, so we won't do it by default:
* *
@@ -34,29 +34,28 @@ ENTRY
* ldmia.S:32: Warning: register range not in ascending order * ldmia.S:32: Warning: register range not in ascending order
*/ */
#if 0 #if 0
ldr r0, =my_array_0 ldr r4, =my_array_0
ldr r1, =0 ldr r5, =0
ldr r2, =0 ldr r6, =0
ldmia r0, {r2,r1} ldmia r4, {r6,r5}
ASSERT_EQ(r1, 0x11111111) ASSERT_EQ(r5, 0x11111111)
ASSERT_EQ(r2, 0x22222222) ASSERT_EQ(r6, 0x22222222)
#endif #endif
/* Modify the array */ /* Modify the array */
ldr r0, =my_array_1 ldr r4, =my_array_1
ldr r1, =0x55555555 ldr r5, =0x55555555
ldr r2, =0x66666666 ldr r6, =0x66666666
ldr r3, =0x77777777 ldr r7, =0x77777777
ldr r4, =0x88888888 ldr r8, =0x88888888
stmdb r0, {r1-r4} stmdb r4, {r5-r8}
/* Verify that my_array_0 changed and is equal to my_array_1. */ /* Verify that my_array_0 changed and is equal to my_array_1. */
MEMCMP(my_array_0, my_array_1, 0x10) ASSERT_MEMCMP(my_array_0, my_array_1, 0x10)
ASSERT_EQ(r0, 0)
/* Load registers and increment r0. */ /* Load registers and increment r4. */
ldr r0, =my_array_0 ldr r4, =my_array_0
ldmia r0!, {r1-r4} ldmia r4!, {r5-r8}
ASSERT_EQ(r0, my_array_1) ASSERT_EQ(r4, my_array_1)
EXIT EXIT

View File

@@ -5,27 +5,27 @@
ENTRY ENTRY
/* Save sp before push. */ /* Save sp before push. */
mov r0, sp mov r4, sp
/* Push. */ /* Push. */
mov r1, 1 mov r5, 1
mov r2, 2 mov r6, 2
push {r1, r2} push {r5, r6}
/* Save sp after push. */ /* Save sp after push. */
mov r1, sp mov r5, sp
/* Restore. */ /* Restore. */
mov r3, 0 mov r7, 0
mov r4, 0 mov r8, 0
pop {r3, r4} pop {r7, r8}
ASSERT_EQ(r3, 1) ASSERT_EQ(r7, 1)
ASSERT_EQ(r4, 2) ASSERT_EQ(r8, 2)
/* Check that stack pointer moved down by 8 bytes /* Check that stack pointer moved down by 8 bytes
* (2 registers x 4 bytes each). * (2 registers x 4 bytes each).
*/ */
sub r0, r1 sub r4, r5
ASSERT_EQ(r0, 8) ASSERT_EQ(r4, 8)
EXIT EXIT

View File

@@ -8,21 +8,21 @@ myvar:
.word 0x12345678 .word 0x12345678
ENTRY ENTRY
/* r0 will contain the address. */
ldr r0, =myvar
/* Sanity check. */ /* Sanity check. */
ldr r0, =myvar
ldr r1, [r0] ldr r1, [r0]
movw r2, 0x5678 movw r2, 0x5678
movt r2, 0x1234 movt r2, 0x1234
ASSERT_EQ_REG(r1, r2) ASSERT_EQ_REG(r1, r2)
/* Modify the value. */ /* Modify the value. */
ldr r0, =myvar
movw r1, 0xDEF0 movw r1, 0xDEF0
movt r1, 0x9ABC movt r1, 0x9ABC
str r1, [r0] str r1, [r0]
/* Check that it changed. */ /* Check that it changed. */
ldr r0, =myvar
ldr r1, [r0] ldr r1, [r0]
movw r2, 0xDEF0 movw r2, 0xDEF0
movt r2, 0x9ABC movt r2, 0x9ABC

View File

@@ -21,10 +21,12 @@
1: \ 1: \
; ;
#ifndef ASSERT_EQ_REG
/* Assert that a register equals another register. */ /* Assert that a register equals another register. */
#define ASSERT_EQ_REG(reg1, reg2) \ #define ASSERT_EQ_REG(reg1, reg2) \
cmp reg1, reg2; \ cmp reg1, reg2; \
ASSERT(beq); \ ASSERT(beq); \
; ;
#endif
#endif #endif

View File

@@ -9,11 +9,53 @@
int asm_main(uint32_t *line); int asm_main(uint32_t *line);
#define ASSERT_EQ_DEFINE(bits) \
int assert_eq_ ## bits(uint ## bits ## _t val1, uint ## bits ## _t val2) { \
if (val1 != val2) { \
printf("%s failed\n", __func__); \
printf("val1 0x%" PRIX ## bits "\n", val1); \
printf("val2 0x%" PRIX ## bits "\n", val2); \
return 1; \
} \
return 0; \
}
ASSERT_EQ_DEFINE(32)
ASSERT_EQ_DEFINE(64)
int assert_memcmp(const void *s1, const void *s2, size_t n) {
int ret;
size_t i;
uint8_t *s1b, *s2b;
uint8_t b1, b2;
ret = 0;
s1b = (uint8_t *)s1;
s2b = (uint8_t *)s2;
for (i = 0; i < n; ++i) {
b1 = s1b[i];
b2 = s2b[i];
if (b1 != b2) {
printf(
"%s failed: "
"byte1, byte2, index: "
"0x%02" PRIX8 " 0x%02" PRIX8 " 0x%zx\n",
__func__,
b1,
b2,
i
);
ret = 1;
}
}
return ret;
}
int main(void) { int main(void) {
uint32_t ret, line; uint32_t ret, line;
ret = asm_main(&line); ret = asm_main(&line);
if (ret) { if (ret) {
printf("error %d at line %d\n", ret, line); printf("error: asm_main returned %d at line %d\n", ret, line);
} }
return ret; return ret;
} }