From ea823189d639eff744031ecbb03b293acefe9122 Mon Sep 17 00:00:00 2001 From: Benjamin Oldenburg Date: Sat, 4 Apr 2026 16:29:28 +0200 Subject: [PATCH] arm64-win32 support : tests --- tests/libtcc_test_mt.c | 13 + tests/tests2/138_arm64_encoding.c | 280 +++++++++++++++ tests/tests2/138_arm64_encoding.expect | 50 +++ tests/tests2/139_arm64_errors.c | 91 +++++ tests/tests2/139_arm64_errors.expect | 32 ++ tests/tests2/140_arm64_extasm.c | 451 +++++++++++++++++++++++++ tests/tests2/140_arm64_extasm.expect | 36 ++ tests/tests2/73_arm64.c | 15 + tests/tests2/73_arm64.expect | 4 + 9 files changed, 972 insertions(+) create mode 100644 tests/tests2/138_arm64_encoding.c create mode 100644 tests/tests2/138_arm64_encoding.expect create mode 100644 tests/tests2/139_arm64_errors.c create mode 100644 tests/tests2/139_arm64_errors.expect create mode 100644 tests/tests2/140_arm64_extasm.c create mode 100644 tests/tests2/140_arm64_extasm.expect diff --git a/tests/libtcc_test_mt.c b/tests/libtcc_test_mt.c index f9c90bd6..23764637 100644 --- a/tests/libtcc_test_mt.c +++ b/tests/libtcc_test_mt.c @@ -338,7 +338,16 @@ int main(int argc, char **argv) #else #include +#ifdef _WIN32 +# ifdef __i386__ +# define LIBTCC_TEST_WINAPI __attribute__((__stdcall__)) +# else +# define LIBTCC_TEST_WINAPI +# endif +void LIBTCC_TEST_WINAPI Sleep(unsigned int milliseconds); +#else unsigned int sleep(unsigned int seconds); +#endif int fib(n) { @@ -347,7 +356,11 @@ int fib(n) int main(int argc, char **argv) { +#ifdef _WIN32 + Sleep(1000); +#else sleep(1); +#endif printf(" %d", fib(atoi(argv[1]))); return 0; } diff --git a/tests/tests2/138_arm64_encoding.c b/tests/tests2/138_arm64_encoding.c new file mode 100644 index 00000000..20aeb390 --- /dev/null +++ b/tests/tests2/138_arm64_encoding.c @@ -0,0 +1,280 @@ +/* ARM64 instruction encoding verification test. + Exercises shift-by-immediate, load/store addressing modes, + conditional branches, and system register access. */ + +#include +#include + +/* ---- shift-by-immediate helpers ---- */ + +static uint32_t lsl32(uint32_t x, int n) { return x << n; } +static uint32_t lsr32(uint32_t x, int n) { return x >> n; } +static int32_t asr32(int32_t x, int n) { return x >> n; } +static uint64_t lsl64(uint64_t x, int n) { return x << n; } +static uint64_t lsr64(uint64_t x, int n) { return x >> n; } +static int64_t asr64(int64_t x, int n) { return x >> n; } + +static void test_shifts(void) +{ + printf("shift-imm:\n"); + printf("%x\n", lsl32(1, 0)); + printf("%x\n", lsl32(1, 15)); + printf("%x\n", lsl32(1, 31)); + printf("%x\n", lsr32(0x80000000U, 31)); + printf("%x\n", lsr32(0x80000000U, 16)); + printf("%x\n", asr32(-1, 1)); + printf("%x\n", asr32(-256, 4)); + printf("%llx\n", (unsigned long long)lsl64(1ULL, 0)); + printf("%llx\n", (unsigned long long)lsl64(1ULL, 32)); + printf("%llx\n", (unsigned long long)lsl64(1ULL, 63)); + printf("%llx\n", (unsigned long long)lsr64(0x8000000000000000ULL, 63)); + printf("%llx\n", (unsigned long long)asr64(-1LL, 1)); + printf("%llx\n", (unsigned long long)asr64(-256LL, 4)); +} + +/* ---- load/store with various addressing modes ---- */ + +static void test_ldr_str(void) +{ + int64_t buf[4] = { 0x1122334455667788LL, 0x99AABBCCDDEEFF00LL, + 0x0F1E2D3C4B5A6978LL, 0x0000000000000001LL }; + int64_t val; + int32_t wval; + int64_t *p; + + printf("ldr-str:\n"); + + /* LDR X, [Xn, #offset] */ + val = buf[0]; + printf("%llx\n", (unsigned long long)val); + val = buf[1]; + printf("%llx\n", (unsigned long long)val); + + /* LDR W, [Xn, #offset] (32-bit load) */ + wval = *(int32_t*)&buf[0]; + printf("%x\n", (unsigned)wval); + + /* Pre-indexed: store pointer, modify */ + p = &buf[0]; + val = *(p + 2); + printf("%llx\n", (unsigned long long)val); + + /* Post-indexed simulation via pointer arithmetic */ + p = &buf[0]; + val = *p; + p++; + printf("%llx %llx\n", (unsigned long long)val, + (unsigned long long)*p); +} + +/* ---- STP/LDP (store/load pair) ---- */ + +static void test_ldp_stp(void) +{ + int64_t src[2] = { 0xAAAABBBBCCCCDDDDLL, 0x1111222233334444LL }; + int64_t dst[2]; + + /* The compiler should use stp/ldp for this */ + dst[0] = src[0]; + dst[1] = src[1]; + + printf("ldp-stp:\n"); + printf("%llx %llx\n", + (unsigned long long)dst[0], + (unsigned long long)dst[1]); +} + +/* ---- CBZ / CBNZ via C patterns ---- */ + +static const char *cbz_test(int x) +{ + if (x == 0) + return "zero"; + return "nonzero"; +} + +static const char *cbnz_test(int x) +{ + if (x != 0) + return "nonzero"; + return "zero"; +} + +static void test_cond_branches(void) +{ + printf("cbz-cbnz:\n"); + printf("%s\n", cbz_test(0)); + printf("%s\n", cbz_test(42)); + printf("%s\n", cbnz_test(0)); + printf("%s\n", cbnz_test(42)); +} + +/* ---- conditional compare patterns (CCMP/CCMN) ---- */ + +static int cond_select(int a, int b) +{ + /* TCC should generate csel or equivalent */ + return a > b ? a : b; +} + +static void test_cond_select(void) +{ + printf("csel:\n"); + printf("%d\n", cond_select(5, 10)); + printf("%d\n", cond_select(10, 5)); + printf("%d\n", cond_select(0, 0)); + printf("%d\n", cond_select(-1, 1)); +} + +/* ---- MRS/MSR system registers (FPCR/FPSR) ---- */ + +static void test_sysregs(void) +{ + unsigned int fpcr, fpsr; + + printf("sysregs:\n"); + + /* Read FPCR */ + __asm__ volatile ("mrs %0, fpcr" : "=r"(fpcr)); + printf("%u\n", fpcr & 0x0F); + + /* Read FPSR */ + __asm__ volatile ("mrs %0, fpsr" : "=r"(fpsr)); + printf("%u\n", fpsr); + + /* Write/restore FPCR (should be same value) */ + __asm__ volatile ("msr fpcr, %0" : : "r"(fpcr)); + + /* Read back and verify */ + { + unsigned int check; + __asm__ volatile ("mrs %0, fpcr" : "=r"(check)); + printf("%s\n", check == fpcr ? "ok" : "fail"); + } +} + +/* ---- NOP encoding (should not crash) ---- */ + +static void test_nop(void) +{ + printf("nop:\n"); + __asm__ volatile ("nop"); + __asm__ volatile ("nop"); + __asm__ volatile ("nop"); + printf("ok\n"); +} + +/* ---- barrier instructions ---- */ + +static void test_barriers(void) +{ + printf("barriers:\n"); + __asm__ volatile ("dmb sy"); + __asm__ volatile ("dsb sy"); + __asm__ volatile ("isb"); + printf("ok\n"); +} + +/* ---- MOV (register) patterns ---- */ + +static int64_t mov_x0_x1(int64_t x) +{ + register int64_t r __asm__("x0") = x; + __asm__ volatile ("" : "=r"(r) : "0"(r)); + return r; +} + +static void test_mov_reg(void) +{ + printf("mov-reg:\n"); + printf("%lld\n", (long long)mov_x0_x1(42)); + printf("%lld\n", (long long)mov_x0_x1(-1)); +} + +/* ---- large struct passing (reference semantics) ---- */ + +struct large { int64_t a, b, c, d; }; + +static int64_t sum_large(struct large s) +{ + return s.a + s.b + s.c + s.d; +} + +static struct large make_large(int64_t a, int64_t b, int64_t c, int64_t d) +{ + struct large s = { a, b, c, d }; + return s; +} + +static void test_large_structs(void) +{ + struct large s = { 1, 2, 3, 4 }; + struct large t; + + printf("large-struct:\n"); + printf("%lld\n", (long long)sum_large(s)); + + t = make_large(10, 20, 30, 40); + printf("%lld %lld %lld %lld\n", + (long long)t.a, (long long)t.b, + (long long)t.c, (long long)t.d); +} + +/* ---- boundary-sized structs ---- */ + +struct s18 { char x[18]; }; +struct s24 { char x[24]; }; +struct s32 { char x[32]; }; + +static void print_s18(struct s18 s) { printf("%.18s\n", s.x); } +static void print_s24(struct s24 s) { printf("%.24s\n", s.x); } +static void print_s32(struct s32 s) { printf("%.32s\n", s.x); } + +static struct s18 ret_s18(void) +{ + struct s18 s = { "123456789012345678" }; + return s; +} + +static struct s24 ret_s24(void) +{ + struct s24 s = { "123456789012345678901234" }; + return s; +} + +static struct s32 ret_s32(void) +{ + struct s32 s = { "12345678901234567890123456789012" }; + return s; +} + +static void test_boundary_structs(void) +{ + struct s18 a; + struct s24 b; + struct s32 c; + + printf("boundary-structs:\n"); + a = ret_s18(); + printf("%.18s\n", a.x); + b = ret_s24(); + printf("%.24s\n", b.x); + c = ret_s32(); + printf("%.32s\n", c.x); +} + +int main(void) +{ + test_shifts(); + test_ldr_str(); + test_ldp_stp(); + test_cond_branches(); + test_cond_select(); + test_sysregs(); + test_nop(); + test_barriers(); + test_mov_reg(); + test_large_structs(); + test_boundary_structs(); + return 0; +} diff --git a/tests/tests2/138_arm64_encoding.expect b/tests/tests2/138_arm64_encoding.expect new file mode 100644 index 00000000..d0f4694f --- /dev/null +++ b/tests/tests2/138_arm64_encoding.expect @@ -0,0 +1,50 @@ +shift-imm: +1 +8000 +80000000 +1 +8000 +ffffffff +fffffff0 +1 +100000000 +8000000000000000 +1 +ffffffffffffffff +fffffffffffffff0 +ldr-str: +1122334455667788 +99aabbccddeeff00 +55667788 +f1e2d3c4b5a6978 +1122334455667788 99aabbccddeeff00 +ldp-stp: +aaaabbbbccccdddd 1111222233334444 +cbz-cbnz: +zero +nonzero +zero +nonzero +csel: +10 +10 +0 +1 +sysregs: +0 +0 +ok +nop: +ok +barriers: +ok +mov-reg: +42 +-1 +large-struct: +10 +10 20 30 40 +boundary-structs: +123456789012345678 +123456789012345678901234 +12345678901234567890123456789012 diff --git a/tests/tests2/139_arm64_errors.c b/tests/tests2/139_arm64_errors.c new file mode 100644 index 00000000..00deace5 --- /dev/null +++ b/tests/tests2/139_arm64_errors.c @@ -0,0 +1,91 @@ +/* ARM64 inline assembly error tests. + Verify that invalid assembly produces proper error messages. + Run with -dt; skipped on non-arm64 architectures. */ + +#include + +#if defined test_unknown_instruction +int main(void) +{ + __asm__("fubar x0, x1, x2"); + return 0; +} + +#elif defined test_shift_imm_range_32 +int main(void) +{ + /* LSL by 32 is out of range for 32-bit operand */ + __asm__("lsl w0, w1, #32"); + return 0; +} + +#elif defined test_shift_imm_range_64 +int main(void) +{ + /* LSL by 64 is out of range for 64-bit operand */ + __asm__("lsl x0, x1, #64"); + return 0; +} + +#elif defined test_invalid_sysreg +int main(void) +{ + /* Bogus system register name */ + __asm__("mrs x0, bogusreg"); + return 0; +} + +#elif defined test_invalid_barrier_option +int main(void) +{ + /* Invalid barrier scope name */ + __asm__("dmb xyz"); + return 0; +} + +#elif defined test_missing_third_operand +int main(void) +{ + __asm__("add x0, x1"); + return 0; +} + +#elif defined test_movz_imm_range +int main(void) +{ + __asm__("movz x0, #0x10000"); + return 0; +} + +#elif defined test_movz_shift_range +int main(void) +{ + __asm__("movz x0, #1, lsl #8"); + return 0; +} + +#elif defined test_invalid_muls +int main(void) +{ + __asm__("muls x0, x1, x2"); + return 0; +} + +#elif defined test_extended_inline_asm +int main(void) +{ + int x = 1; + /* Invalid operand reference in extended inline asm */ + __asm__("add %0, %1, #1" : "=r"(x) : "2"(x)); + return 0; +} + +#elif defined test_extended_inline_clobber +int main(void) +{ + /* Invalid clobber register name */ + __asm__ volatile ("nop" : : : "bogus"); + return 0; +} + +#endif diff --git a/tests/tests2/139_arm64_errors.expect b/tests/tests2/139_arm64_errors.expect new file mode 100644 index 00000000..53cb39b7 --- /dev/null +++ b/tests/tests2/139_arm64_errors.expect @@ -0,0 +1,32 @@ +[test_unknown_instruction] +139_arm64_errors.c:10: error: ARM64 instruction 'fubar' not implemented + +[test_shift_imm_range_32] +139_arm64_errors.c:17: error: shift immediate out of range + +[test_shift_imm_range_64] +139_arm64_errors.c:25: error: shift immediate out of range + +[test_invalid_sysreg] +139_arm64_errors.c:34: error: unsupported system register + +[test_invalid_barrier_option] +139_arm64_errors.c:42: error: invalid operand 'xyz' + +[test_missing_third_operand] +139_arm64_errors.c:48: error: missing third operand + +[test_movz_imm_range] +139_arm64_errors.c:55: error: move wide immediate out of range + +[test_movz_shift_range] +139_arm64_errors.c:62: error: move wide shift out of range + +[test_invalid_muls] +139_arm64_errors.c:70: error: ARM64 instruction 'muls' not implemented + +[test_extended_inline_asm] +139_arm64_errors.c:79: error: invalid reference in constraint 1 ('2') + +[test_extended_inline_clobber] +139_arm64_errors.c:87: error: invalid clobber register 'bogus' diff --git a/tests/tests2/140_arm64_extasm.c b/tests/tests2/140_arm64_extasm.c new file mode 100644 index 00000000..6a491874 --- /dev/null +++ b/tests/tests2/140_arm64_extasm.c @@ -0,0 +1,451 @@ +/* + * ARM64 Extended Inline Assembly Tests + * Tests for GCC-style extended inline assembly with operands, constraints, and clobbers + */ + +#include +#include +#include + +struct pair64 { + uint64_t a; + uint64_t b; +}; + +static int arm64_symbol_target(void) +{ + return 7; +} + +static void test_symbolic_address_constraint_compile_only(void) +{ + asm volatile("" : : "S"(arm64_symbol_target)); +} + +/* Test 1: Basic output operand */ +void test_basic_output(void) +{ + int x; + asm("mov %0, #42" : "=r"(x)); + assert(x == 42); + printf("Test 1 (basic output): PASSED\n"); +} + +/* Test 2: Input operand */ +void test_input_operand(void) +{ + int x = 10; + int y; + asm("add %0, %1, #5" : "=r"(y) : "r"(x)); + assert(y == 15); + printf("Test 2 (input operand): PASSED\n"); +} + +/* Test 3: Read-write operand */ +void test_read_write_operand(void) +{ + int x = 10; + asm("add %0, %0, #1" : "+r"(x)); + assert(x == 11); + printf("Test 3 (read-write operand): PASSED\n"); +} + +/* Test 4: Memory operand - load */ +void test_memory_load(void) +{ + int x = 42; + int y; + asm("ldr %w0, [%1]" : "=r"(y) : "r"(&x)); + assert(y == 42); + printf("Test 4 (memory load): PASSED\n"); +} + +/* Test 5: Memory operand - store */ +void test_memory_store(void) +{ + int x; + int val = 123; + asm("str %w1, [%0]" : : "r"(&x), "r"(val)); + assert(x == 123); + printf("Test 5 (memory store): PASSED\n"); +} + +/* Test 5a: Stack memory operand with frame-relative offset */ +void test_stack_memory_operand(void) +{ + long x = 0; + long val = 321; + + asm volatile("str %0, %1" : : "r"(val), "m"(x)); + assert(x == 321); + printf("Test 5a (stack memory operand): PASSED\n"); +} + +/* Test 5b: Symbol memory operand */ +static long arm64_symbol_mem; + +void test_symbol_memory_operand(void) +{ + long val = 654; + + arm64_symbol_mem = 0; + asm volatile("str %0, %1" : : "r"(val), "m"(arm64_symbol_mem)); + assert(arm64_symbol_mem == 654); + printf("Test 5b (symbol memory operand): PASSED\n"); +} + +/* Test 6: Clobber list */ +void test_clobber_list(void) +{ + int x = 10; + int y = 20; + int result; + asm("add %0, %1, %2" + : "=r"(result) + : "r"(x), "r"(y) + : "cc"); + assert(result == 30); + printf("Test 6 (clobber list): PASSED\n"); +} + +/* Test 7: Multiple outputs */ +void test_multiple_outputs(void) +{ + int a, b; + asm("mov %0, #1; mov %1, #2" + : "=r"(a), "=r"(b)); + assert(a == 1 && b == 2); + printf("Test 7 (multiple outputs): PASSED\n"); +} + +/* Test 8: Constraint reference */ +void test_constraint_reference(void) +{ + int x = 10; + int y; + asm("add %0, %1, #5" : "=r"(y) : "0"(x)); + assert(y == 15); + printf("Test 8 (constraint reference): PASSED\n"); +} + +/* Test 9: Early clobber */ +void test_early_clobber(void) +{ + int x = 10; + int y; + asm("mov %0, #42" : "=&r"(y) : "r"(x)); + assert(y == 42); + printf("Test 9 (early clobber): PASSED\n"); +} + +/* Test 10: 32-bit register modifier */ +void test_w_register(void) +{ + uint32_t x = 100; + uint32_t y; + asm("add %w0, %w1, #50" : "=r"(y) : "r"(x)); + assert(y == 150); + printf("Test 10 (w register modifier): PASSED\n"); +} + +/* Test 11: Immediate constraint 'I' (12-bit immediate) */ +void test_immediate_i_constraint(void) +{ + int x = 100; + int y; + asm("add %0, %1, #200" : "=r"(y) : "r"(x), "I"(200)); + assert(y == 300); + printf("Test 11 (immediate I constraint): PASSED\n"); +} + +/* Test 12: Register constraint */ +void test_general_operand_constraint(void) +{ + int x = 50; + int y; + asm("add %0, %1, #10" : "=r"(y) : "r"(x)); + assert(y == 60); + printf("Test 12 (general operand constraint): PASSED\n"); +} + +/* Test 13: Multiple inputs and outputs */ +void test_multiple_io(void) +{ + int a = 10, b = 20; + int sum, diff; + asm("add %0, %1, %2" : "=r"(sum) : "r"(a), "r"(b)); + asm("sub %0, %1, %2" : "=r"(diff) : "r"(a), "r"(b)); + assert(sum == 30 && diff == -10); + printf("Test 13 (multiple IO): PASSED\n"); +} + +/* Test 14: Register variable preservation */ +void test_regvar_preservation(void) +{ + register uint64_t keep asm("x19") = 0x123456789abcdef0ULL; + uint64_t out; + + asm volatile("mov x19, #7; add %0, x19, #1" + : "=r"(out) + : + : "x19"); + assert(keep == 0x123456789abcdef0ULL); + assert(out == 8); + printf("Test 14 (regvar preservation): PASSED\n"); +} + +/* Test 15: Complex arithmetic */ +void test_complex_arithmetic(void) +{ + int a = 100, b = 50, c = 25; + int result; + asm("add %0, %1, %2; sub %0, %0, %3" + : "=&r"(result) + : "r"(a), "r"(b), "r"(c)); + assert(result == 125); + printf("Test 15 (complex arithmetic): PASSED\n"); +} + +/* Test 16: Named operand (GCC extension) */ +void test_named_operand(void) +{ + int input = 10; + int output; + asm("add %[out], %[in], #5" + : [out] "=r"(output) + : [in] "r"(input)); + assert(output == 15); + printf("Test 16 (named operand): PASSED\n"); +} + +/* Test 17: Memory clobber */ +void test_memory_clobber(void) +{ + int x = 10; + asm volatile("" : : : "memory"); + assert(x == 10); + printf("Test 17 (memory clobber): PASSED\n"); +} + +/* Test 18: Condition flags clobber */ +void test_cc_clobber(void) +{ + int x = 100; + int y; + asm("adds %0, %1, #0" : "=r"(y) : "r"(x) : "cc"); + assert(y == 100); + printf("Test 18 (cc clobber): PASSED\n"); +} + +/* Test 19: Large immediate with movz/movk */ +void test_large_immediate(void) +{ + uint64_t val; + asm("movz %0, #0x1234, lsl #16; movk %0, #0x5678" : "=r"(val)); + assert(val == 0x12345678ULL); + printf("Test 19 (large immediate): PASSED\n"); +} + +/* Test 20: Bitwise operations */ +void test_bitwise_ops(void) +{ + uint64_t a = 0xf0f0f0f00f0f0f0fULL; + uint64_t b = 0x3333ffff0000ccccULL; + uint64_t andv; + uint64_t orv; + uint64_t xorv; + uint64_t imm_and; + + asm("and %0, %1, %2" : "=r"(andv) : "r"(a), "r"(b)); + asm("orr %0, %1, %2" : "=r"(orv) : "r"(a), "r"(b)); + asm("eor %0, %1, %2" : "=r"(xorv) : "r"(a), "r"(b)); + asm("and %0, %1, %2" : "=r"(imm_and) + : "r"(~0ULL), "L"(0xff00ff00ff00ff00ULL)); + + assert(andv == (a & b)); + assert(orv == (a | b)); + assert(xorv == (a ^ b)); + assert(imm_and == 0xff00ff00ff00ff00ULL); + printf("Test 20 (bitwise ops): PASSED\n"); +} + +/* Test 21: Register shift operands */ +void test_register_shift_operands(void) +{ + uint64_t val = 3; + uint64_t amount = 4; + uint64_t shifted; + uint64_t rotated; + + asm("lsl %0, %1, %2" : "=r"(shifted) : "r"(val), "r"(amount)); + asm("ror %0, %1, %2" : "=r"(rotated) + : "r"(0x0123456789abcdefULL), "r"(8ULL)); + assert(shifted == 48); + assert(rotated == 0xef0123456789abcdULL); + printf("Test 21 (register shifts): PASSED\n"); +} + +/* Test 22: ROR immediate alias of EXTR */ +void test_ror_immediate(void) +{ + uint64_t rotated; + + asm("ror %0, %1, #8" : "=r"(rotated) : "r"(0x0123456789abcdefULL)); + assert(rotated == 0xef0123456789abcdULL); + printf("Test 22 (ror immediate): PASSED\n"); +} + +/* Test 23: FP/SIMD register constraint and modifier */ +void test_fp_register_operand(void) +{ + double x = 3.5; + double y; + + asm("ldr %d0, [%1]" : "=w"(y) : "r"(&x)); + assert(y == x); + printf("Test 23 (fp register operand): PASSED\n"); +} + +/* Test 24: Zero constraint */ +void test_zero_constraint(void) +{ + uint64_t x = 41; + uint64_t y; + + asm("add %0, %1, %x2" : "=r"(y) : "r"(x), "Z"(0)); + assert(y == x); + printf("Test 24 (Z constraint): PASSED\n"); +} + +/* Test 25: 32-bit logical immediate constraint */ +void test_logical_imm32_constraint(void) +{ + uint32_t y; + + asm("orr %w0, wzr, %1" : "=r"(y) : "K"(0xff)); + assert(y == 0xff); + printf("Test 25 (K constraint): PASSED\n"); +} + +/* Test 26: 64-bit logical immediate constraint */ +void test_logical_imm64_constraint(void) +{ + uint64_t y; + + asm("orr %0, xzr, %1" : "=r"(y) : "L"(0xff00ff00ff00ff00ULL)); + assert(y == 0xff00ff00ff00ff00ULL); + printf("Test 26 (L constraint): PASSED\n"); +} + +/* Test 27: 32-bit MOV pseudo immediate constraint */ +void test_mov_imm32_constraint(void) +{ + uint32_t y; + + asm("mov %w0, %1" : "=r"(y) : "M"(0x1234)); + assert(y == 0x1234); + printf("Test 27 (M constraint): PASSED\n"); +} + +/* Test 28: 64-bit MOV pseudo immediate constraint */ +void test_mov_imm64_constraint(void) +{ + uint64_t y; + + asm("mov %0, %1" : "=r"(y) : "N"(0x12340000ULL)); + assert(y == 0x12340000ULL); + printf("Test 28 (N constraint): PASSED\n"); +} + +/* Test 29: x FP/SIMD register constraint */ +void test_x_constraint_fp(void) +{ + double x = 6.25; + double y; + + asm("ldr %d0, [%1]" : "=x"(y) : "r"(&x)); + assert(y == x); + printf("Test 29 (x constraint): PASSED\n"); +} + +/* Test 30: y FP/SIMD register constraint */ +void test_y_constraint_fp(void) +{ + double x = 7.25; + double y; + + asm("ldr %d0, [%1]" : "=y"(y) : "r"(&x)); + assert(y == x); + printf("Test 30 (y constraint): PASSED\n"); +} + +/* Test 31: Q memory constraint */ +static int arm64_q_load(int *ptr) +{ + int y; + + asm("ldr %w0, %1" : "=r"(y) : "Q"(*ptr)); + return y; +} + +void test_q_memory_constraint(void) +{ + int x = 91; + assert(arm64_q_load(&x) == 91); + printf("Test 31 (Q constraint): PASSED\n"); +} + +/* Test 32: Ump pair-memory constraint */ +void test_ump_memory_constraint(void) +{ + struct pair64 pair = { 0x1122334455667788ULL, 0x99aabbccddeeff00ULL }; + uint64_t a; + uint64_t b; + + asm("ldp %0, %1, %2" : "=r"(a), "=r"(b) : "Ump"(pair)); + assert(a == pair.a); + assert(b == pair.b); + printf("Test 32 (Ump constraint): PASSED\n"); +} + +int main(void) +{ + printf("ARM64 Extended Inline Assembly Tests\n"); + test_basic_output(); + test_input_operand(); + test_read_write_operand(); + test_memory_load(); + test_memory_store(); + test_stack_memory_operand(); + test_symbol_memory_operand(); + test_clobber_list(); + test_multiple_outputs(); + test_constraint_reference(); + test_early_clobber(); + test_w_register(); + test_immediate_i_constraint(); + test_general_operand_constraint(); + test_multiple_io(); + test_regvar_preservation(); + test_complex_arithmetic(); + test_named_operand(); + test_memory_clobber(); + test_cc_clobber(); + test_large_immediate(); + test_bitwise_ops(); + test_register_shift_operands(); + test_ror_immediate(); + test_fp_register_operand(); + test_zero_constraint(); + test_logical_imm32_constraint(); + test_logical_imm64_constraint(); + test_mov_imm32_constraint(); + test_mov_imm64_constraint(); + test_x_constraint_fp(); + test_y_constraint_fp(); + test_q_memory_constraint(); + test_ump_memory_constraint(); + test_symbolic_address_constraint_compile_only(); + printf("ARM64 Extended Inline Assembly Tests PASSED!\n"); + return 0; +} diff --git a/tests/tests2/140_arm64_extasm.expect b/tests/tests2/140_arm64_extasm.expect new file mode 100644 index 00000000..87d07fcc --- /dev/null +++ b/tests/tests2/140_arm64_extasm.expect @@ -0,0 +1,36 @@ +ARM64 Extended Inline Assembly Tests +Test 1 (basic output): PASSED +Test 2 (input operand): PASSED +Test 3 (read-write operand): PASSED +Test 4 (memory load): PASSED +Test 5 (memory store): PASSED +Test 5a (stack memory operand): PASSED +Test 5b (symbol memory operand): PASSED +Test 6 (clobber list): PASSED +Test 7 (multiple outputs): PASSED +Test 8 (constraint reference): PASSED +Test 9 (early clobber): PASSED +Test 10 (w register modifier): PASSED +Test 11 (immediate I constraint): PASSED +Test 12 (general operand constraint): PASSED +Test 13 (multiple IO): PASSED +Test 14 (regvar preservation): PASSED +Test 15 (complex arithmetic): PASSED +Test 16 (named operand): PASSED +Test 17 (memory clobber): PASSED +Test 18 (cc clobber): PASSED +Test 19 (large immediate): PASSED +Test 20 (bitwise ops): PASSED +Test 21 (register shifts): PASSED +Test 22 (ror immediate): PASSED +Test 23 (fp register operand): PASSED +Test 24 (Z constraint): PASSED +Test 25 (K constraint): PASSED +Test 26 (L constraint): PASSED +Test 27 (M constraint): PASSED +Test 28 (N constraint): PASSED +Test 29 (x constraint): PASSED +Test 30 (y constraint): PASSED +Test 31 (Q constraint): PASSED +Test 32 (Ump constraint): PASSED +ARM64 Extended Inline Assembly Tests PASSED! diff --git a/tests/tests2/73_arm64.c b/tests/tests2/73_arm64.c index 855c4767..16be1047 100644 --- a/tests/tests2/73_arm64.c +++ b/tests/tests2/73_arm64.c @@ -38,6 +38,11 @@ struct hfa32 { long double a, b; } hfa32 = { 32.1, 32.2 }; struct hfa33 { long double a, b, c; } hfa33 = { 33.1, 33.2, 33.3 }; struct hfa34 { long double a, b, c, d; } hfa34 = { 34.1, 34.2, 34.3, 34.4 }; +struct empty { }; +/* Keep the top-level offsets at zero without changing the type into a union. */ +struct hfae12 { struct empty e; struct hfa12 h; } hfae12 = { { }, { 112.1, 112.2 } }; +struct hfae22 { struct empty e; struct hfa22 h; } hfae22 = { { }, { 122.1, 122.2 } }; + void fa_s1(struct s1 a) { printf("%.1s\n", a.x); } void fa_s2(struct s2 a) { printf("%.2s\n", a.x); } void fa_s3(struct s3 a) { printf("%.3s\n", a.x); } @@ -82,6 +87,10 @@ void fa_hfa33(struct hfa33 a) { printf("%.1Lf %.1Lf %.1Lf\n", a.a, a.b, a.c); } void fa_hfa34(struct hfa34 a) { printf("%.1Lf %.1Lf %.1Lf %.1Lf\n", a.a, a.b, a.c, a.d); } +void fa_hfae12(struct hfae12 a) +{ printf("%.1f %.1f\n", a.h.a, a.h.b); } +void fa_hfae22(struct hfae22 a) +{ printf("%.1f %.1f\n", a.h.a, a.h.b); } void fa1(struct s8 a, struct s9 b, struct s10 c, struct s11 d, struct s12 e, struct s13 f) @@ -140,6 +149,8 @@ void arg(void) fa_hfa32(hfa32); fa_hfa33(hfa33); fa_hfa34(hfa34); + fa_hfae12(hfae12); + fa_hfae22(hfae22); fa1(s8, s9, s10, s11, s12, s13); fa2(s9, s10, s11, s12, s13, s14); fa3(hfa14, hfa23, hfa32); @@ -178,6 +189,8 @@ struct hfa31 fr_hfa31(void) { return hfa31; } struct hfa32 fr_hfa32(void) { return hfa32; } struct hfa33 fr_hfa33(void) { return hfa33; } struct hfa34 fr_hfa34(void) { return hfa34; } +struct hfae12 fr_hfae12(void) { return hfae12; } +struct hfae22 fr_hfae22(void) { return hfae22; } void ret(void) { @@ -228,6 +241,8 @@ void ret(void) printf("%.1Lf %.1Lf\n", fr_hfa32().a, fr_hfa32().b); printf("%.1Lf %.1Lf\n", fr_hfa33().a, fr_hfa33().c); printf("%.1Lf %.1Lf\n", fr_hfa34().a, fr_hfa34().d); + printf("%.1f %.1f\n", fr_hfae12().h.a, fr_hfae12().h.b); + printf("%.1f %.1f\n", fr_hfae22().h.a, fr_hfae22().h.b); } void* diff --git a/tests/tests2/73_arm64.expect b/tests/tests2/73_arm64.expect index 7bdebd30..7bc07500 100644 --- a/tests/tests2/73_arm64.expect +++ b/tests/tests2/73_arm64.expect @@ -28,6 +28,8 @@ cdefghijklmnopqrs 32.1 32.1 33.1 33.2 33.3 34.1 34.2 34.3 34.4 +112.1 112.2 +122.1 122.2 stu ABC JKL TUV 456 ghi ABC JKL TUV 456 ghi tuv 14.1 14.4 23.1 23.3 32.1 32.2 @@ -62,6 +64,8 @@ cdefghijklmnopqrs 32.1 32.2 33.1 33.3 34.1 34.4 +112.1 112.2 +122.1 122.2 stdarg: ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI lmnopqr ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI