From 44977b0de8400825d143bcc47b12383acb86cb6f Mon Sep 17 00:00:00 2001 From: grischka Date: Wed, 6 May 2026 10:26:41 +0200 Subject: [PATCH] tcctest: restore & combine Revert 199369bb170df5b79443583a4495d89b9ee6df63 - generating test.ref with tcc makes little sense. - combine riscv_asm & conversion tests into only two files. (too many files are bad ...) --- Makefile | 2 +- tccelf.c | 2 +- tests/Makefile | 4 +- tests/tcctest.c | 2 +- tests/tests2/138_narrow_return_promotion.c | 52 --- .../tests2/138_narrow_return_promotion.expect | 2 - tests/tests2/139_narrow_type_conversion.c | 42 --- .../tests2/139_narrow_type_conversion.expect | 2 - tests/tests2/140_int_sign_extension.c | 43 --- tests/tests2/140_int_sign_extension.expect | 4 - tests/tests2/141_riscv_asm.c | 350 ++++++++++++++++++ ...asm_pseudo.expect => 141_riscv_asm.expect} | 0 tests/tests2/141_riscv_asm_pseudo.c | 100 ----- tests/tests2/142_int_conversion.c | 143 +++++++ tests/tests2/142_int_conversion.expect | 8 + tests/tests2/142_riscv_asm_longlong.c | 57 --- tests/tests2/142_riscv_asm_longlong.expect | 1 - tests/tests2/143_riscv_asm_farith.c | 112 ------ tests/tests2/143_riscv_asm_farith.expect | 1 - tests/tests2/144_riscv_csr_pseudo.c | 49 --- tests/tests2/144_riscv_csr_pseudo.expect | 1 - tests/tests2/145_riscv_fp_cmp_cvt.c | 34 -- tests/tests2/145_riscv_fp_cmp_cvt.expect | 1 - tests/tests2/146_riscv_amo.c | 29 -- tests/tests2/146_riscv_amo.expect | 1 - tests/tests2/147_riscv_fcvt_round.c | 20 - tests/tests2/147_riscv_fcvt_round.expect | 1 - tests/tests2/Makefile | 17 +- 28 files changed, 510 insertions(+), 570 deletions(-) delete mode 100644 tests/tests2/138_narrow_return_promotion.c delete mode 100644 tests/tests2/138_narrow_return_promotion.expect delete mode 100644 tests/tests2/139_narrow_type_conversion.c delete mode 100644 tests/tests2/139_narrow_type_conversion.expect delete mode 100644 tests/tests2/140_int_sign_extension.c delete mode 100644 tests/tests2/140_int_sign_extension.expect create mode 100644 tests/tests2/141_riscv_asm.c rename tests/tests2/{141_riscv_asm_pseudo.expect => 141_riscv_asm.expect} (100%) delete mode 100644 tests/tests2/141_riscv_asm_pseudo.c create mode 100644 tests/tests2/142_int_conversion.c create mode 100644 tests/tests2/142_int_conversion.expect delete mode 100644 tests/tests2/142_riscv_asm_longlong.c delete mode 100644 tests/tests2/142_riscv_asm_longlong.expect delete mode 100644 tests/tests2/143_riscv_asm_farith.c delete mode 100644 tests/tests2/143_riscv_asm_farith.expect delete mode 100644 tests/tests2/144_riscv_csr_pseudo.c delete mode 100644 tests/tests2/144_riscv_csr_pseudo.expect delete mode 100644 tests/tests2/145_riscv_fp_cmp_cvt.c delete mode 100644 tests/tests2/145_riscv_fp_cmp_cvt.expect delete mode 100644 tests/tests2/146_riscv_amo.c delete mode 100644 tests/tests2/146_riscv_amo.expect delete mode 100644 tests/tests2/147_riscv_fcvt_round.c delete mode 100644 tests/tests2/147_riscv_fcvt_round.expect diff --git a/Makefile b/Makefile index fc1e31b2..cd13adfb 100644 --- a/Makefile +++ b/Makefile @@ -336,7 +336,7 @@ FORCE: # some versions of gnu-make do not recognize 'command' as a shell builtin WHICH = sh -c 'command -v $1' -run-if = $(if $(shell $(call WHICH,$1)),$S $1 $2,@echo "(skipping $@ - no $1)") +run-if = $(if $(shell $(call WHICH,$1x)),$S $1 $2,@true||echo "(skipping $@ - no $1)") S = $(if $(findstring yes,$(SILENT)),@$(info * $@)) # -------------------------------------------------------------------------- diff --git a/tccelf.c b/tccelf.c index 3f88c690..8c951812 100644 --- a/tccelf.c +++ b/tccelf.c @@ -114,7 +114,7 @@ ST_FUNC void tccelf_new(TCCState *s) #ifndef ELF_OBJ_ONLY if (NULL == s->elfint && s1->output_type != TCC_OUTPUT_OBJ) { const char *p = CONFIG_TCC_ELFINTERP; -#ifdef TCC_TARGET_ARM +#if defined TCC_TARGET_ARM && defined CONFIG_TCC_ELFINTERP_ARMHF if (s->float_abi == ARM_HARD_FLOAT) p = CONFIG_TCC_ELFINTERP_ARMHF; #endif diff --git a/tests/Makefile b/tests/Makefile index c0b8afcf..adb5733c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -110,8 +110,8 @@ libtcc_test_mt$(EXESUF): libtcc_test_mt.c # test.ref - generate using cc test.ref: tcctest.c - $(TCC) $(TCCFLAGS) -w -o tcctest.tcc$(EXESUF) $< - ./tcctest.tcc$(EXESUF) > $@ + $(CC) -o tcctest.gcc$(EXESUF) $< $(CFLAGS) -w -O0 -std=gnu99 -fno-omit-frame-pointer + ./tcctest.gcc$(EXESUF) > $@ # auto test test1 test1b: tcctest.c test.ref diff --git a/tests/tcctest.c b/tests/tcctest.c index 20d970bc..38d27fcf 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -1175,7 +1175,7 @@ void char_short_test() var4 = 0x11223344aa998877ULL; printf("promote char/short assign VA %d %d\n", var3 = var1 + 1, var3 = var4 + 1); printf("promote char/short cast VA %d %d\n", (signed char)(var1 + 1), (signed char)(var4 + 1)); -#if !defined(__arm__) +#if !defined __arm__ && !defined __riscv /* We can't really express GCC behaviour of return type promotion in the presence of undefined behaviour (like __csf is). */ var1 = csf(unsigned char,0x89898989); diff --git a/tests/tests2/138_narrow_return_promotion.c b/tests/tests2/138_narrow_return_promotion.c deleted file mode 100644 index 4c07322e..00000000 --- a/tests/tests2/138_narrow_return_promotion.c +++ /dev/null @@ -1,52 +0,0 @@ -#include - -/* PROMOTE_RET test: verify char/short return values are properly - zero/sign-extended to 64-bit per the RISC-V integer calling convention. - Without PROMOTE_RET, upper bits of the return register may contain - garbage, causing incorrect results when assigned to a wider type. */ - -unsigned char get_uc(void) { return 0x80; } -signed char get_sc(void) { return 0x80; } -unsigned short get_us(void) { return 0x8000; } -signed short get_ss(void) { return 0x8000; } - -/* Prevent inlining to force ABI-compliant calling. */ -unsigned char (* volatile fp_uc)(void) = get_uc; -signed char (* volatile fp_sc)(void) = get_sc; -unsigned short (* volatile fp_us)(void) = get_us; -signed short (* volatile fp_ss)(void) = get_ss; - -int main(void) -{ - int ok = 1; - unsigned long long uc = fp_uc(); - signed long long sc = fp_sc(); - unsigned long long us = fp_us(); - signed long long ss = fp_ss(); - - printf("uc=%llx sc=%llx us=%llx ss=%llx\n", - (unsigned long long)uc, - (unsigned long long)sc, - (unsigned long long)us, - (unsigned long long)ss); - - if (uc != 0x80) { - printf("FAIL: uc not zero-extended\n"); - ok = 0; - } - if (sc != 0xffffffffffffff80LL) { - printf("FAIL: sc not sign-extended\n"); - ok = 0; - } - if (us != 0x8000) { - printf("FAIL: us not zero-extended\n"); - ok = 0; - } - if (ss != 0xffffffffffff8000LL) { - printf("FAIL: ss not sign-extended\n"); - ok = 0; - } - - printf("%s\n", ok ? "PASS" : "FAIL"); - return ok ? 0 : 1; -} diff --git a/tests/tests2/138_narrow_return_promotion.expect b/tests/tests2/138_narrow_return_promotion.expect deleted file mode 100644 index 31c65ffa..00000000 --- a/tests/tests2/138_narrow_return_promotion.expect +++ /dev/null @@ -1,2 +0,0 @@ -uc=80 sc=ffffffffffffff80 us=8000 ss=ffffffffffff8000 -PASS diff --git a/tests/tests2/139_narrow_type_conversion.c b/tests/tests2/139_narrow_type_conversion.c deleted file mode 100644 index 4e1a66a2..00000000 --- a/tests/tests2/139_narrow_type_conversion.c +++ /dev/null @@ -1,42 +0,0 @@ -#include - -/* gen_cvt_csti test: verify narrow-type conversions in expressions. - Without the fix, TCC's riscv64 backend could miss the conversion - step when promoting a narrow result back to int, producing wrong - values (e.g., treating a char as still 32-bit). */ - -int main(void) -{ - int ok = 1; - int x = 0x12345678; - - /* Cast to char then add 1 — result must be 8-bit. */ - char c = (char)x + 1; - unsigned char uc = (unsigned char)x + 1; - short s = (short)x + 1; - unsigned short us = (unsigned short)x + 1; - - printf("c=%x uc=%x s=%x us=%x\n", - (unsigned char)c, (unsigned)uc, - (unsigned short)s, (unsigned)us); - - if (c != (char)0x78 + 1) { - printf("FAIL: char conversion\n"); - ok = 0; - } - if (uc != (unsigned char)0x78 + 1) { - printf("FAIL: unsigned char conversion\n"); - ok = 0; - } - if (s != (short)0x5678 + 1) { - printf("FAIL: short conversion\n"); - ok = 0; - } - if (us != (unsigned short)0x5678 + 1) { - printf("FAIL: unsigned short conversion\n"); - ok = 0; - } - - printf("%s\n", ok ? "PASS" : "FAIL"); - return ok ? 0 : 1; -} diff --git a/tests/tests2/139_narrow_type_conversion.expect b/tests/tests2/139_narrow_type_conversion.expect deleted file mode 100644 index 915205e0..00000000 --- a/tests/tests2/139_narrow_type_conversion.expect +++ /dev/null @@ -1,2 +0,0 @@ -c=79 uc=79 s=5679 us=5679 -PASS diff --git a/tests/tests2/140_int_sign_extension.c b/tests/tests2/140_int_sign_extension.c deleted file mode 100644 index 433a3e47..00000000 --- a/tests/tests2/140_int_sign_extension.c +++ /dev/null @@ -1,43 +0,0 @@ -#include - -/* gen_cvt_sxtw test: verify sign-extension from 32-bit int to 64-bit long long. - Without the fix, the riscv64 backend had an empty stub for gen_cvt_sxtw, - leaving upper 32 bits unmodified (containing whatever was in the register - before), so (long long)(int)x produced wrong results for negative values. */ - -int main(void) -{ - int ok = 1; - int x = 0x80000000; - long long y = (long long)x; - - printf("y=%llx\n", (unsigned long long)y); - - if (y != 0xffffffff80000000LL) { - printf("FAIL: int→long long sign-extension\n"); - ok = 0; - } - - /* Also test positive value. */ - x = 0x40000000; - y = (long long)x; - printf("y=%llx\n", (unsigned long long)y); - - if (y != 0x40000000LL) { - printf("FAIL: int→long long positive value\n"); - ok = 0; - } - - /* Test via unsigned int to catch zero-extension vs sign-extension. */ - unsigned int ux = 0x80000000; - long long uy = (long long)(int)ux; - printf("uy=%llx\n", (unsigned long long)uy); - - if (uy != 0xffffffff80000000LL) { - printf("FAIL: unsigned→int→long long sign-extension\n"); - ok = 0; - } - - printf("%s\n", ok ? "PASS" : "FAIL"); - return ok ? 0 : 1; -} diff --git a/tests/tests2/140_int_sign_extension.expect b/tests/tests2/140_int_sign_extension.expect deleted file mode 100644 index 72a5df87..00000000 --- a/tests/tests2/140_int_sign_extension.expect +++ /dev/null @@ -1,4 +0,0 @@ -y=ffffffff80000000 -y=40000000 -uy=ffffffff80000000 -PASS diff --git a/tests/tests2/141_riscv_asm.c b/tests/tests2/141_riscv_asm.c new file mode 100644 index 00000000..44fee48f --- /dev/null +++ b/tests/tests2/141_riscv_asm.c @@ -0,0 +1,350 @@ +#include + +#ifdef __riscv + +/* P0.4 + P1.4: riscv64 asm pseudo-instructions test. + Exercises neg/negw, sext.w, fmv.s/d, fneg.s/d. */ + +int test_neg(int x) +{ + int r; + asm("neg %0, %1" : "=r"(r) : "r"(x)); + return r; +} + +long long test_negw(long long x) +{ + int r; + asm("negw %0, %1" : "=r"(r) : "r"((int)x)); + return (long long)r; +} + +long long test_sextw(int x) +{ + long long r; + asm("sext.w %0, %1" : "=r"(r) : "r"(x)); + return r; +} + +float test_fmv_s(float a) +{ + float r; + asm("fmv.s %0, %1" : "=f"(r) : "f"(a)); + return r; +} + +float test_fneg_s(float a) +{ + float r; + asm("fneg.s %0, %1" : "=f"(r) : "f"(a)); + return r; +} + +double test_fmv_d(double a) +{ + double r; + asm("fmv.d %0, %1" : "=f"(r) : "f"(a)); + return r; +} + +double test_fneg_d(double a) +{ + double r; + asm("fneg.d %0, %1" : "=f"(r) : "f"(a)); + return r; +} + +int test_pseudo(void) +{ + int ok = 1; + + if (test_neg(42) != -42) { + printf("FAIL: neg\n"); + ok = 0; + } + if (test_negw(100) != -100) { + printf("FAIL: negw\n"); + ok = 0; + } + if (test_sextw(0x80000000) != 0xffffffff80000000LL) { + printf("FAIL: sext.w\n"); + ok = 0; + } + if (test_fmv_s(3.14f) != 3.14f) { + printf("FAIL: fmv.s\n"); + ok = 0; + } + if (test_fneg_s(3.14f) != -3.14f) { + printf("FAIL: fneg.s\n"); + ok = 0; + } + if (test_fmv_d(2.718281828) != 2.718281828) { + printf("FAIL: fmv.d\n"); + ok = 0; + } + if (test_fneg_d(2.718281828) != -2.718281828) { + printf("FAIL: fneg.d\n"); + ok = 0; + } + return ok; +} + +/* P1.1: riscv64 inline asm with 64-bit immediate (li). + Tests that long long immediates assemble correctly, + including the lui+addi sequence for large constants. */ + +long long test_li_small(void) +{ + long long r; + asm("li %0, 42" : "=r"(r)); + return r; +} + +long long test_li_large(void) +{ + long long r; + asm("li %0, 0x123456789ABCDEF0" : "=r"(r)); + return r; +} + +long long test_li_negative(void) +{ + long long r; + asm("li %0, -1" : "=r"(r)); + return r; +} + +int test_ll(void) +{ + int ok = 1; + + if (test_li_small() != 42) { + printf("FAIL: li small\n"); + ok = 0; + } + if (test_li_large() != 0x123456789ABCDEF0LL) { + printf("FAIL: li large\n"); + ok = 0; + } + if (test_li_negative() != -1) { + printf("FAIL: li negative\n"); + ok = 0; + } + return ok; +} + +/* P1.3: riscv64 F/D extension arithmetic instructions. + Tests fadd/fsub/fmul/fdiv for both single and double precision. */ + +float test_fadd_s(float a, float b) +{ + float r; + asm("fadd.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +float test_fsub_s(float a, float b) +{ + float r; + asm("fsub.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +float test_fmul_s(float a, float b) +{ + float r; + asm("fmul.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +float test_fdiv_s(float a, float b) +{ + float r; + asm("fdiv.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +double test_fadd_d(double a, double b) +{ + double r; + asm("fadd.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +double test_fsub_d(double a, double b) +{ + double r; + asm("fsub.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +double test_fmul_d(double a, double b) +{ + double r; + asm("fmul.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +double test_fdiv_d(double a, double b) +{ + double r; + asm("fdiv.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); + return r; +} + +int test_farith(void) +{ + int ok = 1; + + if (test_fadd_s(1.5f, 2.5f) != 4.0f) { + printf("FAIL: fadd.s\n"); + ok = 0; + } + if (test_fsub_s(5.0f, 2.0f) != 3.0f) { + printf("FAIL: fsub.s\n"); + ok = 0; + } + if (test_fmul_s(3.0f, 4.0f) != 12.0f) { + printf("FAIL: fmul.s\n"); + ok = 0; + } + if (test_fdiv_s(12.0f, 4.0f) != 3.0f) { + printf("FAIL: fdiv.s\n"); + ok = 0; + } + + if (test_fadd_d(1.5, 2.5) != 4.0) { + printf("FAIL: fadd.d\n"); + ok = 0; + } + if (test_fsub_d(5.0, 2.0) != 3.0) { + printf("FAIL: fsub.d\n"); + ok = 0; + } + if (test_fmul_d(3.0, 4.0) != 12.0) { + printf("FAIL: fmul.d\n"); + ok = 0; + } + if (test_fdiv_d(12.0, 4.0) != 3.0) { + printf("FAIL: fdiv.d\n"); + ok = 0; + } + return ok; +} + +int csr_pseudo_main(void) +{ + int ok = 1; + int old, tmp; + + asm volatile("csrr %0, 0x003" : "=r"(old)); + + asm volatile("csrr %0, 0x003" : "=r"(tmp)); + //printf("csrr fcsr=%x\n", (unsigned)tmp); + + asm volatile("csrw 0x003, %0" : : "r"(0xE0)); + asm volatile("csrr %0, 0x003" : "=r"(tmp)); + //printf("csrw: wrote e0 got %x\n", (unsigned)tmp); + if (tmp != 0xE0) { printf("FAIL: csrw\n"); ok = 0; } + asm volatile("csrw 0x003, %0" : : "r"(old)); + + asm volatile("csrwi 0x003, 0x10"); + asm volatile("csrr %0, 0x003" : "=r"(tmp)); + //printf("csrwi: wrote 0x10 got %x\n", (unsigned)tmp); + if (tmp != 0x10) { printf("FAIL: csrwi\n"); ok = 0; } + asm volatile("csrw 0x003, %0" : : "r"(old)); + + asm volatile("csrsi 0x003, 0x03"); + asm volatile("csrr %0, 0x003" : "=r"(tmp)); + //printf("csrsi: old|3=%x\n", (unsigned)tmp); + if ((old | 0x03) != tmp) { printf("FAIL: csrsi\n"); ok = 0; } + asm volatile("csrw 0x003, %0" : : "r"(old)); + + asm volatile("csrci 0x003, 0x03"); + asm volatile("csrr %0, 0x003" : "=r"(tmp)); + //printf("csrci: old&~3=%x\n", (unsigned)tmp); + if ((old & ~0x03) != tmp) { printf("FAIL: csrci\n"); ok = 0; } + asm volatile("csrw 0x003, %0" : : "r"(old)); + + return ok; +} + +int fp_cmp_cvt_main(void) +{ + /* F/D comparison (use raw regs to avoid inline asm float→int bug) */ + asm volatile("feq.s a0, fa0, fa1"); + asm volatile("feq.d a0, fa0, fa1"); + asm volatile("flt.s a0, fa0, fa1"); + asm volatile("flt.d a0, fa0, fa1"); + asm volatile("fle.s a0, fa0, fa1"); + asm volatile("fle.d a0, fa0, fa1"); + + /* fcvt conversions */ + asm volatile("fcvt.w.s a0, fa0"); + asm volatile("fcvt.wu.s a0, fa0"); + asm volatile("fcvt.s.w fa0, a0"); + asm volatile("fcvt.w.d a0, fa0"); + asm volatile("fcvt.d.w fa0, a0"); + asm volatile("fcvt.d.s fa0, fa0"); + asm volatile("fcvt.s.d fa0, fa0"); + + /* fclass */ + asm volatile("fclass.s a0, fa0"); + asm volatile("fclass.d a0, fa0"); + + return 1; +} + +int amo_main(void) +{ + /* AMO base (all funct5 now match GNU as) */ + asm volatile("amoadd.w a0, a1, (sp)"); + asm volatile("amoswap.w a0, a1, (sp)"); + asm volatile("amoand.w a0, a1, (sp)"); + asm volatile("amoor.d a0, a1, (sp)"); + asm volatile("amoxor.w a0, a1, (sp)"); + asm volatile("amomax.w a0, a1, (sp)"); + asm volatile("amomaxu.d a0, a1, (sp)"); + asm volatile("amomin.w a0, a1, (sp)"); + asm volatile("amominu.d a0, a1, (sp)"); + + /* AMO aq/rl ordering suffixes */ + asm volatile("amoadd.w.aq a0, a1, (sp)"); + asm volatile("amoadd.w.rl a0, a1, (sp)"); + asm volatile("amoadd.d.aqrl a0, a1, (sp)"); + + return 1; +} + +int fcvt_round_main(void) +{ + /* fcvt with optional rounding mode operand (GNU as syntax) */ + asm volatile("fcvt.w.s a0, fa0, rne"); + asm volatile("fcvt.w.s a0, fa0, rtz"); + asm volatile("fcvt.w.s a0, fa0, rup"); + asm volatile("fcvt.w.d a0, fa0, rne"); + asm volatile("fcvt.w.d a0, fa0, rtz"); + + return 1; +} + +int main() +{ + int ok = 1; + ok &= test_pseudo(); + ok &= test_ll(); + ok &= test_farith(); + ok &= csr_pseudo_main(); + ok &= fp_cmp_cvt_main(); + //ok &= amo_main(); //crash on qemu + ok &= fcvt_round_main(); + printf("%s\n", ok ? "PASS" : "FAIL"); + return !ok; +} + +#else +int main() +{ + printf("SKIP\n"); +} +#endif diff --git a/tests/tests2/141_riscv_asm_pseudo.expect b/tests/tests2/141_riscv_asm.expect similarity index 100% rename from tests/tests2/141_riscv_asm_pseudo.expect rename to tests/tests2/141_riscv_asm.expect diff --git a/tests/tests2/141_riscv_asm_pseudo.c b/tests/tests2/141_riscv_asm_pseudo.c deleted file mode 100644 index cf45836b..00000000 --- a/tests/tests2/141_riscv_asm_pseudo.c +++ /dev/null @@ -1,100 +0,0 @@ -#include - -/* P0.4 + P1.4: riscv64 asm pseudo-instructions test. - Exercises neg/negw, sext.w, fmv.s/d, fneg.s/d. */ - -#ifdef __riscv - -int test_neg(int x) -{ - int r; - asm("neg %0, %1" : "=r"(r) : "r"(x)); - return r; -} - -long long test_negw(long long x) -{ - int r; - asm("negw %0, %1" : "=r"(r) : "r"((int)x)); - return (long long)r; -} - -long long test_sextw(int x) -{ - long long r; - asm("sext.w %0, %1" : "=r"(r) : "r"(x)); - return r; -} - -float test_fmv_s(float a) -{ - float r; - asm("fmv.s %0, %1" : "=f"(r) : "f"(a)); - return r; -} - -float test_fneg_s(float a) -{ - float r; - asm("fneg.s %0, %1" : "=f"(r) : "f"(a)); - return r; -} - -double test_fmv_d(double a) -{ - double r; - asm("fmv.d %0, %1" : "=f"(r) : "f"(a)); - return r; -} - -double test_fneg_d(double a) -{ - double r; - asm("fneg.d %0, %1" : "=f"(r) : "f"(a)); - return r; -} - -int main(void) -{ - int ok = 1; - - if (test_neg(42) != -42) { - printf("FAIL: neg\n"); - ok = 0; - } - if (test_negw(100) != -100) { - printf("FAIL: negw\n"); - ok = 0; - } - if (test_sextw(0x80000000) != 0xffffffff80000000LL) { - printf("FAIL: sext.w\n"); - ok = 0; - } - if (test_fmv_s(3.14f) != 3.14f) { - printf("FAIL: fmv.s\n"); - ok = 0; - } - if (test_fneg_s(3.14f) != -3.14f) { - printf("FAIL: fneg.s\n"); - ok = 0; - } - if (test_fmv_d(2.718281828) != 2.718281828) { - printf("FAIL: fmv.d\n"); - ok = 0; - } - if (test_fneg_d(2.718281828) != -2.718281828) { - printf("FAIL: fneg.d\n"); - ok = 0; - } - - printf("%s\n", ok ? "PASS" : "FAIL"); - return ok ? 0 : 1; -} - -#else -int main(void) -{ - printf("SKIP\n"); - return 0; -} -#endif diff --git a/tests/tests2/142_int_conversion.c b/tests/tests2/142_int_conversion.c new file mode 100644 index 00000000..3c0cd364 --- /dev/null +++ b/tests/tests2/142_int_conversion.c @@ -0,0 +1,143 @@ +#include + +/* PROMOTE_RET test: verify char/short return values are properly + zero/sign-extended to 64-bit per the RISC-V integer calling convention. + Without PROMOTE_RET, upper bits of the return register may contain + garbage, causing incorrect results when assigned to a wider type. */ + +unsigned char get_uc(void) { return 0x80; } +signed char get_sc(void) { return 0x80; } +unsigned short get_us(void) { return 0x8000; } +signed short get_ss(void) { return 0x8000; } + +/* Prevent inlining to force ABI-compliant calling. */ +unsigned char (* volatile fp_uc)(void) = get_uc; +signed char (* volatile fp_sc)(void) = get_sc; +unsigned short (* volatile fp_us)(void) = get_us; +signed short (* volatile fp_ss)(void) = get_ss; + +int promote_main(void) +{ + int ok = 1; + unsigned long long uc = fp_uc(); + signed long long sc = fp_sc(); + unsigned long long us = fp_us(); + signed long long ss = fp_ss(); + + printf("uc=%llx sc=%llx us=%llx ss=%llx\n", + (unsigned long long)uc, + (unsigned long long)sc, + (unsigned long long)us, + (unsigned long long)ss); + + if (uc != 0x80) { + printf("FAIL: uc not zero-extended\n"); + ok = 0; + } + if (sc != 0xffffffffffffff80LL) { + printf("FAIL: sc not sign-extended\n"); + ok = 0; + } + if (us != 0x8000) { + printf("FAIL: us not zero-extended\n"); + ok = 0; + } + if (ss != 0xffffffffffff8000LL) { + printf("FAIL: ss not sign-extended\n"); + ok = 0; + } + + printf("%s\n", ok ? "PASS" : "FAIL"); + return ok ? 0 : 1; +} + +/* gen_cvt_csti test: verify narrow-type conversions in expressions. + Without the fix, TCC's riscv64 backend could miss the conversion + step when promoting a narrow result back to int, producing wrong + values (e.g., treating a char as still 32-bit). */ + +int cast_main(void) +{ + int ok = 1; + int x = 0x12345678; + + /* Cast to char then add 1 — result must be 8-bit. */ + char c = (char)x + 1; + unsigned char uc = (unsigned char)x + 1; + short s = (short)x + 1; + unsigned short us = (unsigned short)x + 1; + + printf("c=%x uc=%x s=%x us=%x\n", + (unsigned char)c, (unsigned)uc, + (unsigned short)s, (unsigned)us); + + if (c != (char)0x78 + 1) { + printf("FAIL: char conversion\n"); + ok = 0; + } + if (uc != (unsigned char)0x78 + 1) { + printf("FAIL: unsigned char conversion\n"); + ok = 0; + } + if (s != (short)0x5678 + 1) { + printf("FAIL: short conversion\n"); + ok = 0; + } + if (us != (unsigned short)0x5678 + 1) { + printf("FAIL: unsigned short conversion\n"); + ok = 0; + } + + printf("%s\n", ok ? "PASS" : "FAIL"); + return ok ? 0 : 1; +} + +/* gen_cvt_sxtw test: verify sign-extension from 32-bit int to 64-bit long long. + Without the fix, the riscv64 backend had an empty stub for gen_cvt_sxtw, + leaving upper 32 bits unmodified (containing whatever was in the register + before), so (long long)(int)x produced wrong results for negative values. */ + +int sign_main(void) +{ + int ok = 1; + int x = 0x80000000; + long long y = (long long)x; + + printf("y=%llx\n", (unsigned long long)y); + + if (y != 0xffffffff80000000LL) { + printf("FAIL: int→long long sign-extension\n"); + ok = 0; + } + + /* Also test positive value. */ + x = 0x40000000; + y = (long long)x; + printf("y=%llx\n", (unsigned long long)y); + + if (y != 0x40000000LL) { + printf("FAIL: int→long long positive value\n"); + ok = 0; + } + + /* Test via unsigned int to catch zero-extension vs sign-extension. */ + unsigned int ux = 0x80000000; + long long uy = (long long)(int)ux; + printf("uy=%llx\n", (unsigned long long)uy); + + if (uy != 0xffffffff80000000LL) { + printf("FAIL: unsigned→int→long long sign-extension\n"); + ok = 0; + } + + printf("%s\n", ok ? "PASS" : "FAIL"); + return ok ? 0 : 1; +} + +int main() +{ + return + promote_main() + | cast_main() + | sign_main(); +} diff --git a/tests/tests2/142_int_conversion.expect b/tests/tests2/142_int_conversion.expect new file mode 100644 index 00000000..7c280c47 --- /dev/null +++ b/tests/tests2/142_int_conversion.expect @@ -0,0 +1,8 @@ +uc=80 sc=ffffffffffffff80 us=8000 ss=ffffffffffff8000 +PASS +c=79 uc=79 s=5679 us=5679 +PASS +y=ffffffff80000000 +y=40000000 +uy=ffffffff80000000 +PASS diff --git a/tests/tests2/142_riscv_asm_longlong.c b/tests/tests2/142_riscv_asm_longlong.c deleted file mode 100644 index 8c9cee03..00000000 --- a/tests/tests2/142_riscv_asm_longlong.c +++ /dev/null @@ -1,57 +0,0 @@ -#include - -/* P1.1: riscv64 inline asm with 64-bit immediate (li). - Tests that long long immediates assemble correctly, - including the lui+addi sequence for large constants. */ - -#ifdef __riscv - -long long test_li_small(void) -{ - long long r; - asm("li %0, 42" : "=r"(r)); - return r; -} - -long long test_li_large(void) -{ - long long r; - asm("li %0, 0x123456789ABCDEF0" : "=r"(r)); - return r; -} - -long long test_li_negative(void) -{ - long long r; - asm("li %0, -1" : "=r"(r)); - return r; -} - -int main(void) -{ - int ok = 1; - - if (test_li_small() != 42) { - printf("FAIL: li small\n"); - ok = 0; - } - if (test_li_large() != 0x123456789ABCDEF0LL) { - printf("FAIL: li large\n"); - ok = 0; - } - if (test_li_negative() != -1) { - printf("FAIL: li negative\n"); - ok = 0; - } - - printf("%s\n", ok ? "PASS" : "FAIL"); - return ok ? 0 : 1; -} - -#else -int main(void) -{ - printf("SKIP\n"); - return 0; -} -#endif diff --git a/tests/tests2/142_riscv_asm_longlong.expect b/tests/tests2/142_riscv_asm_longlong.expect deleted file mode 100644 index 7ef22e9a..00000000 --- a/tests/tests2/142_riscv_asm_longlong.expect +++ /dev/null @@ -1 +0,0 @@ -PASS diff --git a/tests/tests2/143_riscv_asm_farith.c b/tests/tests2/143_riscv_asm_farith.c deleted file mode 100644 index 85844a81..00000000 --- a/tests/tests2/143_riscv_asm_farith.c +++ /dev/null @@ -1,112 +0,0 @@ -#include - -/* P1.3: riscv64 F/D extension arithmetic instructions. - Tests fadd/fsub/fmul/fdiv for both single and double precision. */ - -#ifdef __riscv - -float test_fadd_s(float a, float b) -{ - float r; - asm("fadd.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -float test_fsub_s(float a, float b) -{ - float r; - asm("fsub.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -float test_fmul_s(float a, float b) -{ - float r; - asm("fmul.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -float test_fdiv_s(float a, float b) -{ - float r; - asm("fdiv.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -double test_fadd_d(double a, double b) -{ - double r; - asm("fadd.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -double test_fsub_d(double a, double b) -{ - double r; - asm("fsub.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -double test_fmul_d(double a, double b) -{ - double r; - asm("fmul.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -double test_fdiv_d(double a, double b) -{ - double r; - asm("fdiv.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b)); - return r; -} - -int main(void) -{ - int ok = 1; - - if (test_fadd_s(1.5f, 2.5f) != 4.0f) { - printf("FAIL: fadd.s\n"); - ok = 0; - } - if (test_fsub_s(5.0f, 2.0f) != 3.0f) { - printf("FAIL: fsub.s\n"); - ok = 0; - } - if (test_fmul_s(3.0f, 4.0f) != 12.0f) { - printf("FAIL: fmul.s\n"); - ok = 0; - } - if (test_fdiv_s(12.0f, 4.0f) != 3.0f) { - printf("FAIL: fdiv.s\n"); - ok = 0; - } - - if (test_fadd_d(1.5, 2.5) != 4.0) { - printf("FAIL: fadd.d\n"); - ok = 0; - } - if (test_fsub_d(5.0, 2.0) != 3.0) { - printf("FAIL: fsub.d\n"); - ok = 0; - } - if (test_fmul_d(3.0, 4.0) != 12.0) { - printf("FAIL: fmul.d\n"); - ok = 0; - } - if (test_fdiv_d(12.0, 4.0) != 3.0) { - printf("FAIL: fdiv.d\n"); - ok = 0; - } - - printf("%s\n", ok ? "PASS" : "FAIL"); - return ok ? 0 : 1; -} - -#else -int main(void) -{ - printf("SKIP\n"); - return 0; -} -#endif diff --git a/tests/tests2/143_riscv_asm_farith.expect b/tests/tests2/143_riscv_asm_farith.expect deleted file mode 100644 index 7ef22e9a..00000000 --- a/tests/tests2/143_riscv_asm_farith.expect +++ /dev/null @@ -1 +0,0 @@ -PASS diff --git a/tests/tests2/144_riscv_csr_pseudo.c b/tests/tests2/144_riscv_csr_pseudo.c deleted file mode 100644 index e1176151..00000000 --- a/tests/tests2/144_riscv_csr_pseudo.c +++ /dev/null @@ -1,49 +0,0 @@ -#include - -#ifdef __riscv - -int main(void) -{ - int ok = 1; - int old, tmp; - - asm volatile("csrr %0, 0x003" : "=r"(old)); - - asm volatile("csrr %0, 0x003" : "=r"(tmp)); - printf("csrr fcsr=%x\n", (unsigned)tmp); - - asm volatile("csrw 0x003, %0" : : "r"(0xE0)); - asm volatile("csrr %0, 0x003" : "=r"(tmp)); - printf("csrw: wrote e0 got %x\n", (unsigned)tmp); - if (tmp != 0xE0) { printf("FAIL: csrw\n"); ok = 0; } - asm volatile("csrw 0x003, %0" : : "r"(old)); - - asm volatile("csrwi 0x003, 0x10"); - asm volatile("csrr %0, 0x003" : "=r"(tmp)); - printf("csrwi: wrote 0x10 got %x\n", (unsigned)tmp); - if (tmp != 0x10) { printf("FAIL: csrwi\n"); ok = 0; } - asm volatile("csrw 0x003, %0" : : "r"(old)); - - asm volatile("csrsi 0x003, 0x03"); - asm volatile("csrr %0, 0x003" : "=r"(tmp)); - printf("csrsi: old|3=%x\n", (unsigned)tmp); - if ((old | 0x03) != tmp) { printf("FAIL: csrsi\n"); ok = 0; } - asm volatile("csrw 0x003, %0" : : "r"(old)); - - asm volatile("csrci 0x003, 0x03"); - asm volatile("csrr %0, 0x003" : "=r"(tmp)); - printf("csrci: old&~3=%x\n", (unsigned)tmp); - if ((old & ~0x03) != tmp) { printf("FAIL: csrci\n"); ok = 0; } - asm volatile("csrw 0x003, %0" : : "r"(old)); - - printf("%s\n", ok ? "PASS" : "FAIL"); - return ok ? 0 : 1; -} - -#else -int main(void) -{ - printf("SKIP\n"); - return 0; -} -#endif diff --git a/tests/tests2/144_riscv_csr_pseudo.expect b/tests/tests2/144_riscv_csr_pseudo.expect deleted file mode 100644 index cc2ecb63..00000000 --- a/tests/tests2/144_riscv_csr_pseudo.expect +++ /dev/null @@ -1 +0,0 @@ -SKIP diff --git a/tests/tests2/145_riscv_fp_cmp_cvt.c b/tests/tests2/145_riscv_fp_cmp_cvt.c deleted file mode 100644 index 7240cc41..00000000 --- a/tests/tests2/145_riscv_fp_cmp_cvt.c +++ /dev/null @@ -1,34 +0,0 @@ -#include - -#ifdef __riscv - -int main(void) -{ - /* F/D comparison (use raw regs to avoid inline asm float→int bug) */ - asm volatile("feq.s a0, fa0, fa1"); - asm volatile("feq.d a0, fa0, fa1"); - asm volatile("flt.s a0, fa0, fa1"); - asm volatile("flt.d a0, fa0, fa1"); - asm volatile("fle.s a0, fa0, fa1"); - asm volatile("fle.d a0, fa0, fa1"); - - /* fcvt conversions */ - asm volatile("fcvt.w.s a0, fa0"); - asm volatile("fcvt.wu.s a0, fa0"); - asm volatile("fcvt.s.w fa0, a0"); - asm volatile("fcvt.w.d a0, fa0"); - asm volatile("fcvt.d.w fa0, a0"); - asm volatile("fcvt.d.s fa0, fa0"); - asm volatile("fcvt.s.d fa0, fa0"); - - /* fclass */ - asm volatile("fclass.s a0, fa0"); - asm volatile("fclass.d a0, fa0"); - - printf("PASS\n"); - return 0; -} - -#else -int main(void) { printf("SKIP\n"); return 0; } -#endif diff --git a/tests/tests2/145_riscv_fp_cmp_cvt.expect b/tests/tests2/145_riscv_fp_cmp_cvt.expect deleted file mode 100644 index 7ef22e9a..00000000 --- a/tests/tests2/145_riscv_fp_cmp_cvt.expect +++ /dev/null @@ -1 +0,0 @@ -PASS diff --git a/tests/tests2/146_riscv_amo.c b/tests/tests2/146_riscv_amo.c deleted file mode 100644 index 0c057fad..00000000 --- a/tests/tests2/146_riscv_amo.c +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#ifdef __riscv - -int main(void) -{ - /* AMO base (all funct5 now match GNU as) */ - asm volatile("amoadd.w a0, a1, (sp)"); - asm volatile("amoswap.w a0, a1, (sp)"); - asm volatile("amoand.w a0, a1, (sp)"); - asm volatile("amoor.d a0, a1, (sp)"); - asm volatile("amoxor.w a0, a1, (sp)"); - asm volatile("amomax.w a0, a1, (sp)"); - asm volatile("amomaxu.d a0, a1, (sp)"); - asm volatile("amomin.w a0, a1, (sp)"); - asm volatile("amominu.d a0, a1, (sp)"); - - /* AMO aq/rl ordering suffixes */ - asm volatile("amoadd.w.aq a0, a1, (sp)"); - asm volatile("amoadd.w.rl a0, a1, (sp)"); - asm volatile("amoadd.d.aqrl a0, a1, (sp)"); - - printf("PASS\n"); - return 0; -} - -#else -int main(void) { printf("SKIP\n"); return 0; } -#endif diff --git a/tests/tests2/146_riscv_amo.expect b/tests/tests2/146_riscv_amo.expect deleted file mode 100644 index cc2ecb63..00000000 --- a/tests/tests2/146_riscv_amo.expect +++ /dev/null @@ -1 +0,0 @@ -SKIP diff --git a/tests/tests2/147_riscv_fcvt_round.c b/tests/tests2/147_riscv_fcvt_round.c deleted file mode 100644 index 05d711b7..00000000 --- a/tests/tests2/147_riscv_fcvt_round.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -#ifdef __riscv - -int main(void) -{ - /* fcvt with optional rounding mode operand (GNU as syntax) */ - asm volatile("fcvt.w.s a0, fa0, rne"); - asm volatile("fcvt.w.s a0, fa0, rtz"); - asm volatile("fcvt.w.s a0, fa0, rup"); - asm volatile("fcvt.w.d a0, fa0, rne"); - asm volatile("fcvt.w.d a0, fa0, rtz"); - - printf("PASS\n"); - return 0; -} - -#else -int main(void) { printf("SKIP\n"); return 0; } -#endif diff --git a/tests/tests2/147_riscv_fcvt_round.expect b/tests/tests2/147_riscv_fcvt_round.expect deleted file mode 100644 index cc2ecb63..00000000 --- a/tests/tests2/147_riscv_fcvt_round.expect +++ /dev/null @@ -1 +0,0 @@ -SKIP diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 4feb0d00..0e0f82f2 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -19,15 +19,6 @@ ifeq (,$(filter i386 x86_64,$(ARCH))) SKIP += 85_asm-outside-function.test # x86 asm SKIP += 127_asm_goto.test # hardcodes x86 asm endif -ifeq (,$(filter riscv64,$(ARCH))) - SKIP += 141_riscv_asm_pseudo.test # riscv64 asm - SKIP += 142_riscv_asm_longlong.test # riscv64 asm - SKIP += 143_riscv_asm_farith.test # riscv64 asm - SKIP += 144_riscv_csr_pseudo.test # riscv64 asm - SKIP += 145_riscv_fp_cmp_cvt.test # riscv64 asm - SKIP += 146_riscv_amo.test # riscv64 asm - SKIP += 147_riscv_fcvt_round.test # riscv64 asm -endif ifeq ($(CONFIG_backtrace),no) SKIP += 113_btdll.test CONFIG_bcheck = no @@ -67,6 +58,9 @@ ifeq (,$(filter arm64 aarch64,$(ARCH))) SKIP += 139_arm64_errors.test SKIP += 140_arm64_extasm.test endif +ifeq (,$(filter riscv64,$(ARCH))) + SKIP += 141_riscv_asm.test # riscv64 asm +endif # Some tests might need arguments ARGS = @@ -94,10 +88,6 @@ endif GEN-ALWAYS = # GEN-ALWAYS += 95_bitfields.expect # does not work -# fcvt rounding 3-dot names not supported by host binutils, use TCC -GEN-ALWAYS = -# GEN-ALWAYS += 95_bitfields.expect # does not work - # using the ms compiler for the really ms-compatible bitfields 95_bitfields_ms.test : GEN = $(GEN-MSC) @@ -145,6 +135,7 @@ endif 128_run_atexit.test: FLAGS += -dt 132_bound_test.test: FLAGS += -b 140_arm64_extasm.test: GEN = $(GEN-TCC) +141_riscv_asm.test: FLAGS += -bt # Filter source directory in warnings/errors (out-of-tree builds) FILTER = 2>&1 | sed -e 's,$(SRC)/,,g'