From 69c8e9256670a6abf1f9b0f36267bde1ac56d583 Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Sat, 2 May 2026 23:52:34 +0800 Subject: [PATCH] riscv64: add PROMOTE_RET for narrow return ABI compliance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other mature backends (i386, x86_64, arm64) all define PROMOTE_RET, which forces explicit sign/zero extension of narrow return types at the caller side. Without it, riscv64 relied on the assumption that RV64 registers are always sign-extended, which may not hold when interfacing with non-TCC compilers. Verified on Spacemit X100 (riscv64): self-compilation is self-consistent across 3 layers. test3 reference mismatch is expected — the test intentionally invokes UB via type-punned function pointers (csf macro). --- riscv64-gen.c | 4 ++ tests/tests2/138_narrow_return_promotion.c | 52 +++++++++++++++++++ .../tests2/138_narrow_return_promotion.expect | 2 + 3 files changed, 58 insertions(+) create mode 100644 tests/tests2/138_narrow_return_promotion.c create mode 100644 tests/tests2/138_narrow_return_promotion.expect diff --git a/riscv64-gen.c b/riscv64-gen.c index 19c76682..4725c1b6 100644 --- a/riscv64-gen.c +++ b/riscv64-gen.c @@ -30,6 +30,10 @@ #define CHAR_IS_UNSIGNED +/* define if return values need to be extended explicitely + at caller side (for interfacing with non-TCC compilers) */ +#define PROMOTE_RET + #else #define USING_GLOBALS #include "tcc.h" diff --git a/tests/tests2/138_narrow_return_promotion.c b/tests/tests2/138_narrow_return_promotion.c new file mode 100644 index 00000000..4c07322e --- /dev/null +++ b/tests/tests2/138_narrow_return_promotion.c @@ -0,0 +1,52 @@ +#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 new file mode 100644 index 00000000..31c65ffa --- /dev/null +++ b/tests/tests2/138_narrow_return_promotion.expect @@ -0,0 +1,2 @@ +uc=80 sc=ffffffffffffff80 us=8000 ss=ffffffffffff8000 +PASS