Fix Windows ARM64 runtime regressions

This commit is contained in:
Benjamin Oldenburg 2026-03-15 16:58:23 +07:00
parent 4f4b3dda6b
commit cf4441c415
6 changed files with 67 additions and 21 deletions

View File

@ -100,7 +100,17 @@ jobs:
cd win32 cd win32
call build-tcc.bat -t arm64 -c clang call build-tcc.bat -t arm64 -c clang
echo ::endgroup:: echo ::endgroup::
.\tcc -v .\tcc -B. -v
.\tcc -B. ..\win32\test_arm64.c -o test_arm64.exe && .\test_arm64.exe
.\tcc -B. -run ..\examples\ex1.c
> test_rstdin.txt echo arm64 stdin
.\tcc -B. -rstdin test_rstdin.txt -run ..\win32\test_rstdin.c > test_rstdin.out
type test_rstdin.out
fc /n test_rstdin.out test_rstdin.txt
.\tcc -B. ..\tests\tests2\49_bracket_evaluation.c -o test49.exe && .\test49.exe > test49.out
fc /n test49.out ..\tests\tests2\49_bracket_evaluation.expect
.\tcc -B. ..\tests\tests2\133_old_func.c -o test133.exe && .\test133.exe > test133.out
fc /n test133.out ..\tests\tests2\133_old_func.expect
test-armv7-linux: test-armv7-linux:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04

View File

@ -3,7 +3,7 @@
* ARM64 (AArch64) assembler for TCC * ARM64 (AArch64) assembler for TCC
* *
* Based on ARM64 Architecture Reference Manual * Based on ARM64 Architecture Reference Manual
* Supports AArch64 instruction set for inline assembly * Supports AArch64 assembler parsing plus basic inline asm strings
*/ */
#ifdef TARGET_DEFS_ONLY #ifdef TARGET_DEFS_ONLY
@ -1169,29 +1169,42 @@ ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
} }
} }
/* Generate code for inline asm - ARM64 inline asm with constraints not yet fully implemented */ static int asm_has_clobbers(const uint8_t *clobber_regs)
{
int i;
for (i = 0; i < NB_ASM_REGS; ++i)
if (clobber_regs[i])
return 1;
return 0;
}
/* Basic inline asm strings are assembled directly by tccasm.c.
Operand allocation and clobber handling are still unsupported here. */
ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
int nb_outputs, int is_output, int nb_outputs, int is_output,
uint8_t *clobber_regs, uint8_t *clobber_regs,
int out_reg) int out_reg)
{ {
/* For now, just handle clobber registers by marking them as volatile */ (void)operands;
/* TODO: Implement full ARM64 inline asm support with register allocation */ (void)nb_outputs;
if (nb_operands > 0 || out_reg > 0) { (void)is_output;
tcc_error("ARM64 inline asm with operands is not implemented");
} if (nb_operands > 0 || asm_has_clobbers(clobber_regs) || out_reg >= 0)
gen_nop(); tcc_error("ARM64 extended inline asm is not implemented");
} }
/* Compute constraints - ARM64 not yet fully implemented */
ST_FUNC void asm_compute_constraints(ASMOperand *operands, ST_FUNC void asm_compute_constraints(ASMOperand *operands,
int nb_operands, int nb_outputs, int nb_operands, int nb_outputs,
const uint8_t *clobber_regs, const uint8_t *clobber_regs,
int *pout_reg) int *pout_reg)
{ {
/* TODO: Implement ARM64 constraint computation */ (void)operands;
(void)nb_outputs;
if (pout_reg) if (pout_reg)
*pout_reg = 0; *pout_reg = -1;
if (nb_operands > 0 || asm_has_clobbers(clobber_regs))
tcc_error("ARM64 extended inline asm is not implemented");
} }
/* Handle clobber list */ /* Handle clobber list */

View File

@ -874,12 +874,13 @@ static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned l
size = type_size(type[i], &align); size = type_size(type[i], &align);
#if defined(TCC_TARGET_MACHO) #if defined(TCC_TARGET_MACHO)
if (variadic && i == variadic) { if (variadic > 0 && i == variadic) {
nx = 8; nx = 8;
nv = 8; nv = 8;
} }
#elif defined(TCC_TARGET_PE) #elif defined(TCC_TARGET_PE)
if (variadic && i >= variadic && (hfa || is_float(type[i]->t))) { if ((variadic < 0 || (variadic > 0 && i >= variadic))
&& (hfa || is_float(type[i]->t))) {
hfa = 0; hfa = 0;
if (is_float(type[i]->t)) { if (is_float(type[i]->t)) {
win_vararg_float = 1; win_vararg_float = 1;
@ -992,6 +993,8 @@ static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned l
return ns - 32; return ns - 32;
} }
#define ARM64_PCS_ALL_VARARGS (-1)
static unsigned long arm64_pcs(int variadic, int n, CType **type, unsigned long *a) static unsigned long arm64_pcs(int variadic, int n, CType **type, unsigned long *a)
{ {
unsigned long stack; unsigned long stack;
@ -1074,6 +1077,7 @@ ST_FUNC void gfunc_call(int nb_args)
unsigned long *a, *a1; unsigned long *a, *a1;
unsigned long stack; unsigned long stack;
int i; int i;
int pcs_variadic = 0;
int func_type = vtop[-nb_args].type.ref->f.func_type; int func_type = vtop[-nb_args].type.ref->f.func_type;
int variadic = (func_type == FUNC_ELLIPSIS); int variadic = (func_type == FUNC_ELLIPSIS);
int old_style = (func_type == FUNC_OLD); int old_style = (func_type == FUNC_OLD);
@ -1098,7 +1102,11 @@ ST_FUNC void gfunc_call(int nb_args)
for (i = 0; i < nb_args; i++) for (i = 0; i < nb_args; i++)
t[nb_args - i] = &vtop[-i].type; t[nb_args - i] = &vtop[-i].type;
stack = arm64_pcs((variadic || old_style) ? var_nb_arg : 0, nb_args, t, a); if (variadic)
pcs_variadic = var_nb_arg;
else if (old_style)
pcs_variadic = ARM64_PCS_ALL_VARARGS;
stack = arm64_pcs(pcs_variadic, nb_args, t, a);
// Allocate space for structs replaced by pointer: // Allocate space for structs replaced by pointer:
for (i = nb_args; i; i--) for (i = nb_args; i; i--)
@ -1263,6 +1271,7 @@ ST_FUNC void gfunc_call(int nb_args)
static unsigned long arm64_func_va_list_stack; static unsigned long arm64_func_va_list_stack;
static int arm64_func_va_list_gr_offs; static int arm64_func_va_list_gr_offs;
static int arm64_func_va_list_vr_offs; static int arm64_func_va_list_vr_offs;
static unsigned arm64_func_start_offset;
static int arm64_func_sub_sp_offset; static int arm64_func_sub_sp_offset;
#define ARM64_FUNC_STACK_SETUP_SLOTS 6 #define ARM64_FUNC_STACK_SETUP_SLOTS 6
@ -1341,6 +1350,7 @@ ST_FUNC void gfunc_prolog(Sym *func_sym)
last_int = last_int > 4 ? 4 : last_int; last_int = last_int > 4 ? 4 : last_int;
last_float = last_float > 4 ? 4 : last_float; last_float = last_float > 4 ? 4 : last_float;
arm64_func_start_offset = ind;
o(0xa9b27bfd); // stp x29,x30,[sp,#-224]! o(0xa9b27bfd); // stp x29,x30,[sp,#-224]!
for (i = 0; i < last_float; i++) for (i = 0; i < last_float; i++)
// stp q0,q1,[sp,#16], stp q2,q3,[sp,#48] // stp q0,q1,[sp,#16], stp q2,q3,[sp,#48]
@ -1664,8 +1674,7 @@ ST_FUNC void gfunc_epilog(void)
#ifdef TCC_TARGET_PE #ifdef TCC_TARGET_PE
{ {
unsigned start = arm64_func_sub_sp_offset - 8; pe_add_unwind_data(arm64_func_start_offset, ind, -loc);
pe_add_unwind_data(start, ind, -loc);
} }
#endif #endif
} }

9
tcc.c
View File

@ -400,6 +400,11 @@ static int tcc_run_via_temp_exe(TCCState *s, int argc, char **argv)
DeleteFileA(tmppath); DeleteFileA(tmppath);
return ret; return ret;
} }
static int tcc_run_requires_inprocess(const TCCState *s)
{
return (s->dflag & 16) || s->run_stdin != NULL;
}
#endif #endif
int main(int argc, char **argv) int main(int argc, char **argv)
@ -513,9 +518,7 @@ redo:
if (s->output_type == TCC_OUTPUT_MEMORY) { if (s->output_type == TCC_OUTPUT_MEMORY) {
#ifdef TCC_IS_NATIVE #ifdef TCC_IS_NATIVE
#if defined(_WIN32) && defined(__aarch64__) #if defined(_WIN32) && defined(__aarch64__)
if (s->dflag & 16) if (tcc_run_requires_inprocess(s))
ret = tcc_run(s, argc, argv);
else if (first_file && 0 == strcmp(tcc_basename(first_file), "tcc.c"))
ret = tcc_run(s, argc, argv); ret = tcc_run(s, argc, argv);
else else
ret = tcc_run_via_temp_exe(s, argc, argv); ret = tcc_run_via_temp_exe(s, argc, argv);

View File

@ -179,7 +179,7 @@ if exist libtcc.dll .\tcc -impdef libtcc.dll -o libtcc\libtcc.def
@if errorlevel 1 goto :the_end @if errorlevel 1 goto :the_end
:lib :lib
@rem ARM64 now supported with implemented assembler @rem ARM64 assembler files and basic inline asm strings are supported here.
call :make_lib %T% || goto :the_end call :make_lib %T% || goto :the_end
@if exist %PX%-tcc.exe call :make_lib %TX% %PX%- || goto :the_end @if exist %PX%-tcc.exe call :make_lib %TX% %PX%- || goto :the_end

11
win32/test_rstdin.c Normal file
View File

@ -0,0 +1,11 @@
#include <stdio.h>
int main(void)
{
char buf[64];
if (!fgets(buf, sizeof buf, stdin))
return 1;
printf("%s", buf);
return 0;
}