Skip to content

Commit 30afb50

Browse files
author
grischka
committed
arm64-win32 review: fix problems and pass tests
tccpe.c: - fix arm64 unwind codes (to make native set/longjmp() work) sizeof(RUNTIME_FUNCTION) is 8 on arm64 in the first place no need to note stack slots if we don't save any registers anyway arm64-gen.c: - fix long double reg-move - fix arm64_hfa() for structs with float arrays - gfunc_prolog(): setup stackframe eariler (simplifies unwind codes) - new function gv_addr(RC); win32/include/setjmp.h: - provide correct definition for setjmo() (frameoffset = 224) tccasm.c: - support ".quad" with symbol & relocation - support ".size" - fix ". - symbol" arithmetic win32/lib/crt1.c and win32/include/stdlib.h: - do not write to __argc/__argv which reside in msvcrt.dll (msvcrt.dll on arm64 does not like that, crashes on unload) tcc.c,libtcc.c: - new functions tcc_fopen/fclose to avoid different stdio unstances in tcc.exe & libtcc.dll tests & github workflow: - add test-win32.bat to run tests with a tcc compiled by build-tcc.bat - add msvcrt_start.c for gcc/clang to use the same runtime as tcc the problem is that newer gcc as well as clang and cl are linking to newer runtimes (such as UCRT) that have partially different printf format behavior which makes tcctest fail. the solution here is to force these compilers to link with msvcrt.dll just like tcc. Also, there is no gcc on arm64-win32 currently at all. Anyway, this approach to running the github CI tests does not require msys2. But It does rely on gnumake as well as on some 'sh' shell though which seems to be installed somewhere (maybe it is the one from git).
1 parent 576cd2a commit 30afb50

33 files changed

Lines changed: 601 additions & 455 deletions

.github/workflows/build.yml

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,56 +34,51 @@ jobs:
3434
timeout-minutes: 6
3535
steps:
3636
- uses: actions/checkout@v4
37-
- name: make & test tcc (x86_64-win32)
38-
shell: cmd
39-
run: |
40-
echo ::group:: setup msys mingw64-gcc
41-
set MSYS2_PATH_TYPE=inherit
42-
set MSYSTEM=MINGW64
43-
set CHERE_INVOKING=yes
44-
C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-x86_64-gcc"
45-
echo ::endgroup::
46-
C:\msys64\usr\bin\bash -l -c "./configure && make && make test -k"
47-
- uses: ilammy/msvc-dev-cmd@v1
48-
with:
49-
arch: amd64
50-
- name: build with MSVC (x86_64-win32)
37+
- name: test (x86_64-win32)
5138
shell: cmd
5239
run: |
5340
echo ::group:: run build-tcc.bat
5441
cd win32
55-
call build-tcc.bat -t 64 -c cl
42+
call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=amd64
43+
call build-tcc.bat -c cl -t x86_64
44+
@echo off
5645
echo ::endgroup::
57-
.\tcc -I.. libtcc.dll -v ../tests/libtcc_test.c -o libtest.exe && .\libtest.exe
58-
.\tcc -I.. libtcc.dll -run ../tests/libtcc_test.c
46+
cd ..\tests
47+
call test-win32.bat all -k
5948
6049
test-i386-win32:
6150
runs-on: windows-2025
6251
timeout-minutes: 6
6352
steps:
6453
- uses: actions/checkout@v4
65-
- name: make & test tcc (i386-win32)
54+
- name: test (i386-win32)
6655
shell: cmd
6756
run: |
68-
echo ::group:: setup msys mingw32-gcc
69-
set MSYS2_PATH_TYPE=inherit
70-
set MSYSTEM=MINGW32
71-
set CHERE_INVOKING=yes
72-
C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-i686-gcc"
57+
echo ::group:: run build-tcc.bat
58+
cd win32
59+
call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=x86
60+
call build-tcc.bat -c cl -t i386
61+
@echo off
7362
echo ::endgroup::
74-
C:\msys64\usr\bin\bash -l -c "./configure && make && make test -k"
75-
- uses: ilammy/msvc-dev-cmd@v1
76-
with:
77-
arch: x86
78-
- name: build with MSVC (i386-win32)
63+
cd ..\tests
64+
call test-win32.bat -p c:\mingw32\bin all -k
65+
66+
test-arm64-win32:
67+
runs-on: windows-11-arm
68+
timeout-minutes: 6
69+
steps:
70+
- uses: actions/checkout@v4
71+
- name: test (arm64-win32)
7972
shell: cmd
8073
run: |
8174
echo ::group:: run build-tcc.bat
8275
cd win32
83-
call build-tcc.bat -t 32 -c cl
76+
call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=arm64
77+
call build-tcc.bat -c cl -t arm64
78+
@echo off
8479
echo ::endgroup::
85-
.\tcc -I.. libtcc.dll -v ../tests/libtcc_test.c -o libtest.exe && .\libtest.exe
86-
.\tcc -I.. libtcc.dll -run ../tests/libtcc_test.c
80+
cd ..\tests
81+
call test-win32.bat -c clang all -k
8782
8883
test-armv7-linux:
8984
runs-on: ubuntu-22.04

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ ifdef CONFIG_WIN32
3131
LIBTCCDEF = libtcc.def
3232
endif
3333
ifneq ($(CONFIG_debug),yes)
34-
LDFLAGS += -s
34+
ifneq ($(CC_NAME),clang)
35+
LDFLAGS += -s
36+
endif
3537
endif
3638
NATIVE_TARGET = $(if $(findstring arm64,$(ARCH)),arm64-win32,$(ARCH)-win$(if $(findstring arm,$(ARCH)),ce,32))
3739
else

arm64-asm.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,7 @@ enum {
8686
#define TREG_X30 30
8787
#define TREG_SP 31
8888

89-
#ifdef TCC_TARGET_PE
90-
#define ARM64_FREG_BASE 19
91-
#else
9289
#define ARM64_FREG_BASE 20
93-
#endif
9490
#define ARM64_FREG_LAST (ARM64_FREG_BASE + 7)
9591

9692
typedef struct Operand {

arm64-gen.c

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ ST_FUNC void load(int r, SValue *sv)
579579
if (svr < VT_CONST) {
580580
if (IS_FREG(r) && IS_FREG(svr))
581581
if (svtt == VT_LDOUBLE)
582-
o(ARM64_MOV_V16B | fltr(r) | fltr(svr) << 5);
582+
o(ARM64_MOV_V16B | fltr(r) | fltr(svr) * 0x10020);
583583
// mov v(r).16b,v(svr).16b
584584
else
585585
o(ARM64_FMOV_SCALAR | fltr(r) | fltr(svr) << 5); // fmov d(r),d(svr)
@@ -794,7 +794,7 @@ static int arm64_hfa_aux(CType *type, int *fsize, int num)
794794
return num;
795795
}
796796
}
797-
else if ((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR)) {
797+
else if (type->t & VT_ARRAY) { /* handle float array within struct */
798798
int num1;
799799
if (!type->ref->c)
800800
return num;
@@ -811,8 +811,7 @@ static int arm64_hfa_aux(CType *type, int *fsize, int num)
811811

812812
static int arm64_hfa(CType *type, unsigned *fsize)
813813
{
814-
if ((type->t & VT_BTYPE) == VT_STRUCT ||
815-
((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR))) {
814+
if ((type->t & VT_BTYPE) == VT_STRUCT) {
816815
int sz = 0;
817816
int n = arm64_hfa_aux(type, &sz, 0);
818817
if (0 < n && n <= 4) {
@@ -1037,6 +1036,13 @@ static void arm64_sub_sp(uint64_t diff)
10371036
}
10381037
}
10391038

1039+
static int gv_addr(int r)
1040+
{
1041+
gaddrof();
1042+
vtop->type.t = VT_PTR;
1043+
return gv(r);
1044+
}
1045+
10401046
ST_FUNC void gfunc_call(int nb_args)
10411047
{
10421048
CType *return_type;
@@ -1147,9 +1153,7 @@ ST_FUNC void gfunc_call(int nb_args)
11471153
else if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
11481154
int align, size = type_size(&vtop->type, &align);
11491155
if (size) {
1150-
vtop->type.t = VT_PTR;
1151-
gaddrof();
1152-
gv(RC_R(a[i] / 2));
1156+
gv_addr(RC_R(a[i] / 2));
11531157
arm64_ldrs(a[i] / 2, size);
11541158
}
11551159
}
@@ -1165,9 +1169,7 @@ ST_FUNC void gfunc_call(int nb_args)
11651169
uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz);
11661170
if (n > 0) {
11671171
/* HFA struct - load from memory into float registers */
1168-
vtop->type.t = VT_PTR;
1169-
gaddrof();
1170-
gv(RC_R30);
1172+
gv_addr(RC_R30);
11711173
for (j = 0; j < n; j++)
11721174
o(0x3d4003c0 |
11731175
(sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |
@@ -1186,7 +1188,7 @@ ST_FUNC void gfunc_call(int nb_args)
11861188
if ((return_type->t & VT_BTYPE) == VT_STRUCT) {
11871189
if (a[0] == 1) {
11881190
// indirect return: set x8 and discard the stack value
1189-
gv(RC_R(8));
1191+
gv_addr(RC_R(8));
11901192
--vtop;
11911193
}
11921194
else
@@ -1206,7 +1208,7 @@ ST_FUNC void gfunc_call(int nb_args)
12061208
int bt = rt & VT_BTYPE;
12071209
if (bt == VT_STRUCT && !(a[0] & 1)) {
12081210
// A struct was returned in registers, so write it out:
1209-
gv(RC_R(8));
1211+
gv_addr(RC_R(8));
12101212
--vtop;
12111213
if (a[0] == 0) {
12121214
int align, size = type_size(return_type, &align);
@@ -1271,6 +1273,7 @@ ST_FUNC void gfunc_prolog(Sym *func_sym)
12711273

12721274
for (sym = func_type->ref; sym; sym = sym->next)
12731275
++n;
1276+
12741277
pcs_n = n - 1;
12751278
c = n + variadic;
12761279
t = tcc_malloc(c * sizeof(*t));
@@ -1321,6 +1324,8 @@ ST_FUNC void gfunc_prolog(Sym *func_sym)
13211324

13221325
arm64_func_start_offset = ind;
13231326
o(0xa9b27bfd); // stp x29,x30,[sp,#-224]!
1327+
o(0x910003fd); // mov x29,sp
1328+
13241329
for (i = 0; i < last_float; i++)
13251330
// stp q0,q1,[sp,#16], stp q2,q3,[sp,#48]
13261331
// stp q4,q5,[sp,#80], stp q6,q7,[sp,#112]
@@ -1368,7 +1373,6 @@ ST_FUNC void gfunc_prolog(Sym *func_sym)
13681373
tcc_free(a);
13691374
tcc_free(t);
13701375

1371-
o(0x910003fd); // mov x29,sp
13721376
arm64_func_sub_sp_offset = ind;
13731377
/* In gfunc_epilog these will be replaced with stack setup code. */
13741378
for (i = 0; i < ARM64_FUNC_STACK_SETUP_SLOTS; ++i)
@@ -1384,8 +1388,7 @@ ST_FUNC void gen_va_start(void)
13841388
{
13851389
int r;
13861390
--vtop; // we don't need the "arg"
1387-
gaddrof();
1388-
r = intr(gv(RC_INT));
1391+
r = intr(gv_addr(RC_INT));
13891392

13901393
#ifdef TCC_TARGET_PE
13911394
if (arm64_func_va_list_stack) {
@@ -1433,13 +1436,12 @@ ST_FUNC void gen_va_arg(CType *t)
14331436
uint32_t r0, r1;
14341437

14351438
#ifdef TCC_TARGET_PE
1436-
int indirect = 0, slot = size + 7 & -8;
1439+
int indirect = 0, slot = (size + 7) & -8;
14371440

14381441
if (size > 16)
14391442
indirect = 1, slot = 8;
14401443

1441-
gaddrof();
1442-
r0 = intr(gv(RC_INT));
1444+
r0 = intr(gv_addr(RC_INT));
14431445
r1 = get_reg(RC_INT);
14441446
vtop[0].r = r1 | VT_LVAL;
14451447
r1 = intr(r1);
@@ -1459,7 +1461,6 @@ ST_FUNC void gen_va_arg(CType *t)
14591461
o(0x9100001e | r1 << 5 | slot << 10); // add x30,x(r1),#(slot)
14601462
o(0xf900001e | r0 << 5); // str x30,[x(r0)] // ap += slot
14611463
}
1462-
14631464
if (indirect)
14641465
o(ARM64_LDR_X | ARM64_RN(r1) | r1); // ldr x(r1),[x(r1)]
14651466

@@ -1469,8 +1470,7 @@ ST_FUNC void gen_va_arg(CType *t)
14691470
if (!is_float(t->t))
14701471
hfa = arm64_hfa(t, &fsize);
14711472

1472-
gaddrof();
1473-
r0 = intr(gv(RC_INT));
1473+
r0 = intr(gv_addr(RC_INT));
14741474
r1 = get_reg(RC_INT);
14751475
vtop[0].r = r1 | VT_LVAL;
14761476
r1 = intr(r1);
@@ -1570,8 +1570,7 @@ ST_FUNC void gfunc_return(CType *func_type)
15701570
case 0:
15711571
if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
15721572
int align, size = type_size(func_type, &align);
1573-
gaddrof();
1574-
gv(RC_R(0));
1573+
gv_addr(RC_R(0));
15751574
arm64_ldrs(0, size);
15761575
}
15771576
else
@@ -1590,8 +1589,7 @@ ST_FUNC void gfunc_return(CType *func_type)
15901589
if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
15911590
/* HFA struct return - load from the address on vtop into float registers */
15921591
uint32_t j, sz, n = arm64_hfa(func_type, &sz);
1593-
gaddrof();
1594-
gv(RC_R(0));
1592+
gv_addr(RC_R(0));
15951593
for (j = 0; j < n; j++)
15961594
o(0x3d400000 |
15971595
(sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |

i386-asm.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -494,13 +494,6 @@ ST_FUNC void gen_expr32(ExprValue *pe)
494494
gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
495495
}
496496

497-
#ifdef TCC_TARGET_X86_64
498-
ST_FUNC void gen_expr64(ExprValue *pe)
499-
{
500-
gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
501-
}
502-
#endif
503-
504497
/* XXX: unify with C code output ? */
505498
static void gen_disp32(ExprValue *pe)
506499
{

lib/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ ARM_O = libtcc1.o armeabi.o armflush.o $(COMMON_O)
4141
ARM64_O = lib-arm64.o $(COMMON_O)
4242
RISCV64_O = lib-arm64.o $(COMMON_O)
4343
COMMON_O = stdatomic.o atomic.o builtin.o alloca.o alloca-bt.o
44-
WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o
44+
WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o winex.o
4545
LIN_O = dsohandle.o
4646
OSX_O =
4747

@@ -63,7 +63,7 @@ OBJ-x86_64 = $(X86_64_O) va_list.o $(LIN_O)
6363
OBJ-x86_64-osx = $(X86_64_O) va_list.o $(OSX_O)
6464
OBJ-i386-win32 = $(I386_O) chkstk.o $(WIN_O)
6565
OBJ-x86_64-win32 = $(X86_64_O) chkstk.o $(WIN_O)
66-
OBJ-arm64 = $(ARM64_O) $(LIN_O)
66+
OBJ-arm64 = $(ARM64_O) armflush.o $(LIN_O)
6767
OBJ-arm64-osx = $(ARM64_O) $(OSX_O)
6868
OBJ-arm64-win32 = $(ARM64_O) chkstk.o $(WIN_O)
6969
OBJ-arm = $(ARM_O) $(LIN_O)

lib/armflush.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
intrinsic with gcc. However tcc in order to compile
55
itself needs this function */
66

7+
/* ------------------------------------------------------------- */
8+
#if defined __arm__
9+
710
#ifdef __TINYC__
811

912
/* syscall wrapper */
@@ -49,3 +52,13 @@ void __clear_cache(void *beginning, void *end)
4952
* However, there is no ARM asm parser in tcc so we use it for now */
5053
syscall(__ARM_NR_cacheflush, beginning, end, 0);
5154
}
55+
56+
/* ------------------------------------------------------------- */
57+
#elif defined __aarch64__
58+
void __clear_cache(void *beg, void *end)
59+
{
60+
__arm64_clear_cache(beg, end);
61+
}
62+
63+
/* ------------------------------------------------------------- */
64+
#endif

lib/bt-dll.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
REDIR(__bound_strncmp) \
3535
REDIR(__bound_strcat) \
3636
REDIR(__bound_strchr) \
37-
REDIR(__bound_strdup)
37+
REDIR(__bound_strdup) \
38+
REDIR(__bound_strncat) \
39+
REDIR(__bound_strrchr) \
40+
REDIR(__bound_setjmp) \
41+
REDIR(__bound_longjmp)
3842

3943
#ifdef __leading_underscore
4044
#define _(s) "_"#s
@@ -45,11 +49,28 @@
4549
#define REDIR(s) void *s;
4650
static struct { REDIR_ALL } all_ptrs;
4751
#undef REDIR
52+
4853
#define REDIR(s) #s"\0"
4954
static const char all_names[] = REDIR_ALL;
5055
#undef REDIR
51-
#define REDIR(s) __asm__(".global " _(s) ";" _(s) ": jmp *%0" : : "m" (all_ptrs.s) );
52-
static void all_jmps() { REDIR_ALL }
56+
57+
#if __aarch64__
58+
# define REDIR(s) \
59+
__asm__(".global "_(s)";"_(s)":"); \
60+
__asm__(".int 0x58000090"); /* ldr x16, [pc, #16] */ \
61+
__asm__(".int 0xf9400210"); /* ldr x16, [x16] */ \
62+
__asm__(".int 0xd61f0200"); /* br x16 */ \
63+
__asm__(".int 0xd503201f"); /* nop for alignment */ \
64+
__asm__(".quad all_ptrs + (. - all_jmps - 16) / 24 * 8"); \
65+
__asm__(".type "_(s)",function\n.size "_(s)",.-"_(s));
66+
67+
__asm__(".text\n.align 8\nall_jmps:");
68+
REDIR_ALL
69+
#else
70+
# define REDIR(s) \
71+
__asm__(".global "_(s)";"_(s)":"); goto *all_ptrs.s;
72+
static void all_jmps() { REDIR_ALL }
73+
#endif
5374
#undef REDIR
5475

5576
void __bt_init_dll(int bcheck)

lib/lib-arm64.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,6 @@ typedef unsigned long long uint64_t;
2323
#include <string.h>
2424
#endif
2525

26-
#if !defined __riscv && !defined __APPLE__
27-
void __clear_cache(void *beg, void *end)
28-
{
29-
__arm64_clear_cache(beg, end);
30-
}
31-
#endif
32-
3326
typedef union {
3427
struct { uint64_t x0, x1; };
3528
long double f;

0 commit comments

Comments
 (0)