fix github CI & stuff
Some checks are pending
build and test / test-x86_64-linux (push) Waiting to run
build and test / test-x86_64-osx (push) Waiting to run
build and test / test-aarch64-osx (push) Waiting to run
build and test / test-x86_64-win32 (push) Waiting to run
build and test / test-i386-win32 (push) Waiting to run
build and test / test-armv7-linux (push) Waiting to run
build and test / test-aarch64-linux (push) Waiting to run
build and test / test-riscv64-linux (push) Waiting to run

workflows/build.yml:
- win32/64: install mingw32/64 gcc on msys (because the default
  gcc installed elsewhere seems to use ucrt, producing incompatible
  fp printf formats.)
tccgen.c:
- cleanup funcs: save any lvalues from return expressions.  Also use
  get_temp_local_var() which however was causing a problem on i386
  because its gfunc_call() removes the arguments from vstack and by
  that defeats the 'in-use'  tracking of get_temp_local_var().  Fixed by:
i386/arm/arm64/x86_64-gen.c:
- in gfunc_call(): save_regs before anything else, fixes
  problems seen in arm64/i386
tccpp.c:
- allow arm asm specific use of '#' in macros
libtcc.c:
- organize -M options, and:
tccpe.c:
- move the subsystem option parsing from libtcc.c
tccelf.c:
- improved error handling in tcc_load_ldscript()
lib/atomic.S:
- TCC_TARGET_... not defined when building the lib
- endbrNN security feature not supported by TCC
tests/tests2/136_atomic_gcc_style.c:
- never use standard assert() in tests
This commit is contained in:
grischka 2025-07-14 21:55:37 +02:00
parent 28d7fb85b2
commit 9670d10294
15 changed files with 133 additions and 95 deletions

View File

@ -29,29 +29,41 @@ jobs:
- name: make & test tcc (aarch64-osx)
run: ./configure && make && make test -k
test-x86-win32:
runs-on: windows-2022
timeout-minutes: 6
test-x86_64-win32:
runs-on: windows-2025
timeout-minutes: 4
steps:
- uses: actions/checkout@v4
- name: make & test tcc (x86_64-win32)
shell: cmd
run: |
echo ::group:: setup msys mingw64-gcc
set MSYS2_PATH_TYPE=inherit
set MSYSTEM=MINGW64
set CHERE_INVOKING=yes
C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-x86_64-gcc"
echo ::endgroup::
C:\msys64\usr\bin\bash -l -c "./configure && make && make test -k"
test-i386-win32:
runs-on: windows-2025
timeout-minutes: 4
steps:
- uses: actions/checkout@v4
- name: make & test tcc (i386-win32)
shell: cmd
run: |
echo ::group:: setup msys mingw32-gcc
set MSYS2_PATH_TYPE=inherit
set MSYSTEM=MINGW32
set CHERE_INVOKING=yes
C:\msys64\usr\bin\bash -l -c "./configure && make clean all && make test -k"
C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-i686-gcc"
echo ::endgroup::
C:\msys64\usr\bin\bash -l -c "./configure && make all && make test -k"
test-armv7-linux:
runs-on: ubuntu-22.04
timeout-minutes: 6
timeout-minutes: 8
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
@ -69,14 +81,14 @@ jobs:
test-aarch64-linux:
runs-on: ubuntu-22.04
timeout-minutes: 6
timeout-minutes: 8
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
name: make & test tcc (aarch64-linux)
with:
arch: aarch64
distro: ubuntu22.04
distro: ubuntu24.04
githubToken: ${{ github.token }}
install: |
apt-get update -q -y
@ -87,7 +99,7 @@ jobs:
test-riscv64-linux:
runs-on: ubuntu-22.04
timeout-minutes: 6
timeout-minutes: 8
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3

View File

@ -252,8 +252,11 @@ LDFLAGS += -g
endif
# convert "include/tccdefs.h" to "tccdefs_.h"
%_.h : include/%.h conftest.c
$S$(CC) -DC2STR $(filter %.c,$^) -o c2str.exe && ./c2str.exe $< $@
%_.h : include/%.h c2str.exe
$S./c2str.exe $< $@
c2str.exe : conftest.c
$S$(CC) -DC2STR $< -o $@
# target specific object rule
$(X)%.o : %.c $(LIBTCC_INC)

View File

@ -1311,10 +1311,6 @@ again:
if (++pass < 2)
goto again;
/* Manually free remaining registers since next parameters are loaded
* manually, without the help of gv(int). */
save_regs(nb_args);
if(todo) {
o(0xE8BD0000|todo); /* pop {todo} */
for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) {
@ -1354,6 +1350,8 @@ void gfunc_call(int nb_args)
gbound_args(nb_args);
#endif
save_regs(nb_args + 1);
#ifdef TCC_ARM_EABI
if (float_abi == ARM_HARD_FLOAT) {
variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS);

View File

@ -997,6 +997,8 @@ ST_FUNC void gfunc_call(int nb_args)
int variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS);
int var_nb_arg = n_func_args(&vtop[-nb_args].type);
save_regs(nb_args + 1);
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check)
gbound_args(nb_args);
@ -1101,9 +1103,6 @@ ST_FUNC void gfunc_call(int nb_args)
// value in floating-point registers
if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz);
// save regs because struct may overwrite previous func call result
save_regs(0);
vtop->type.t = VT_PTR;
gaddrof();
gv(RC_R30);
@ -1129,7 +1128,6 @@ ST_FUNC void gfunc_call(int nb_args)
vswap();
}
save_regs(0);
arm64_gen_bl_or_b(0);
--vtop;
if (stack & 0xfff)

View File

@ -406,6 +406,8 @@ ST_FUNC void gfunc_call(int nb_args)
gbound_args(nb_args);
#endif
save_regs(nb_args + 1);
args_size = 0;
for(i = 0;i < nb_args; i++) {
if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
@ -463,7 +465,7 @@ ST_FUNC void gfunc_call(int nb_args)
}
vtop--;
}
save_regs(0); /* save used temporary registers */
func_sym = vtop->type.ref;
func_call = func_sym->f.func_call;
/* fast call case */

View File

@ -23,8 +23,10 @@
/* ---------------------------------------------- */
#if defined __i386__
#define endbr32
.text
.align 2
.global _(__atomic_load_1)
.type _(__atomic_load_1), %function
@ -297,9 +299,11 @@ _(atomic_flag_clear_explicit):
#endif //__i386__
/* ---------------------------------------------- */
#if defined __x86_64__ && !defined TCC_TARGET_PE
#if defined __x86_64__ && !defined _WIN32
#define endbr64
.text
.align 2
.global _(__atomic_load_1)
.type _(__atomic_load_1), %function
@ -508,12 +512,11 @@ _(atomic_flag_clear_explicit):
ret
.size _(atomic_flag_clear_explicit), .-_(atomic_flag_clear_explicit)
#endif //__x86_64__ && !TCC_TARGET_PE
#endif //__x86_64__ && !_WIN32
/* ---------------------------------------------- */
#if defined __x86_64__ && defined TCC_TARGET_PE
#if defined __x86_64__ && defined _WIN32
.text
.align 2
.global _(__atomic_load_1)
.type _(__atomic_load_1), %function
@ -700,12 +703,11 @@ _(atomic_flag_clear_explicit):
ret
.size _(atomic_flag_clear_explicit), .-_(atomic_flag_clear_explicit)
#endif //__x86_64__ && TCC_TARGET_PE
#endif //__x86_64__ && _WIN32
/* ---------------------------------------------- */
#if defined __arm__
.text
.align 2
#ifndef __TINYC__
.arch armv6k
.syntax unified
@ -1496,7 +1498,6 @@ _(atomic_flag_clear_explicit):
/* ---------------------------------------------- */
#if defined __aarch64__
.text
.align 2
.global _(__atomic_load_1)
.type _(__atomic_load_1), %function
@ -2115,7 +2116,6 @@ _(atomic_flag_clear_explicit):
/* ---------------------------------------------- */
#if defined __riscv
.text
.align 2
.global _(__atomic_load_1)
.type _(__atomic_load_1), %function

View File

@ -1465,31 +1465,9 @@ static int tcc_set_linker(TCCState *s, const char *optarg)
} else if (link_option(&o, "stack=")) {
s->pe_stack_size = strtoul(o.arg, &end, 10);
} else if (link_option(&o, "subsystem=")) {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
if (0==strcmp("native", o.arg)) {
s->pe_subsystem = 1;
} else if (0==strcmp("console", o.arg)) {
s->pe_subsystem = 3;
} else if (0==strcmp("gui", o.arg) || 0==strcmp("windows", o.arg)) {
s->pe_subsystem = 2;
} else if (0==strcmp("posix", o.arg)) {
s->pe_subsystem = 7;
} else if (0==strcmp("efiapp", o.arg)) {
s->pe_subsystem = 10;
} else if (0==strcmp("efiboot", o.arg)) {
s->pe_subsystem = 11;
} else if (0==strcmp("efiruntime", o.arg)) {
s->pe_subsystem = 12;
} else if (0==strcmp("efirom", o.arg)) {
s->pe_subsystem = 13;
#elif defined(TCC_TARGET_ARM)
if (0==strcmp("wince", o.arg)) {
s->pe_subsystem = 9;
#endif
} else
if (pe_setsubsy(s, o.arg) < 0)
goto err;
#endif /* PE */
#ifdef TCC_TARGET_MACHO
#elif defined TCC_TARGET_MACHO
} else if (link_option(&o, "all_load")) {
s->filetype |= AFF_WHOLE_ARCHIVE;
} else if (link_option(&o, "force_load=")) {
@ -1653,11 +1631,10 @@ static const TCCOption tcc_options[] = {
{ "w", TCC_OPTION_w, 0 },
{ "E", TCC_OPTION_E, 0},
{ "M", TCC_OPTION_M, 0},
{ "MD", TCC_OPTION_MD, 0},
{ "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
{ "MM", TCC_OPTION_MM, 0},
{ "MMD,", TCC_OPTION_MMD, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "MMD", TCC_OPTION_MMD, 0},
{ "MD", TCC_OPTION_MD, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "MMD", TCC_OPTION_MMD, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
{ "MP", TCC_OPTION_MP, 0},
{ "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG },
/* tcctools */
@ -2075,6 +2052,7 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv)
case TCC_OPTION_P:
s->Pflag = atoi(optarg) + 1;
break;
case TCC_OPTION_M:
s->include_sys_deps = 1;
// fall through
@ -2084,29 +2062,31 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv)
if (!s->deps_outfile)
tcc_set_str(&s->deps_outfile, "-");
break;
case TCC_OPTION_MD:
s->include_sys_deps = 1;
// fall through
case TCC_OPTION_MMD:
s->gen_deps = 1;
/* usually, only "-MMD" is used */
/* but the Linux Kernel uses "-MMD,depfile" */
if ((optarg) && (*optarg != '\0'))
tcc_set_str(&s->deps_outfile, optarg);
break;
case TCC_OPTION_MD:
s->gen_deps = 1;
s->include_sys_deps = 1;
break;
if (*optarg != ',')
break;
++optarg;
// fall through
case TCC_OPTION_MF:
tcc_set_str(&s->deps_outfile, optarg);
break;
case TCC_OPTION_MP:
s->gen_phony_deps = 1;
break;
case TCC_OPTION_dumpmachine:
printf("%s\n", dumpmachine_str);
exit(0);
case TCC_OPTION_dumpversion:
printf ("%s\n", TCC_VERSION);
exit(0);
case TCC_OPTION_x:
x = 0;
if (*optarg == 'c')

1
tcc.h
View File

@ -1790,6 +1790,7 @@ ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);
ST_FUNC int pe_load_file(struct TCCState *s1, int fd, const char *filename);
ST_FUNC int pe_output_file(TCCState * s1, const char *filename);
ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value);
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg);
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#endif
#ifdef TCC_TARGET_X86_64

View File

@ -3937,7 +3937,7 @@ static int ld_add_file(TCCState *s1, const char filename[])
{
if (filename[0] == '-' && filename[1] == 'l')
return tcc_add_library(s1, filename + 2);
{
if (CONFIG_SYSROOT[0] != '\0' || !IS_ABSPATH(filename)) {
/* lookup via library paths */
int ret = tcc_add_dll(s1, tcc_basename(filename), 0);
if (ret != FILE_NOT_FOUND)
@ -3981,19 +3981,19 @@ repeat:
} else if (t != LD_TOK_NAME) {
return tcc_error_noabort("unexpected token '%c'", t);
} else if (!strcmp(filename, "AS_NEEDED")) {
ret = ld_add_file_list(s1, filename);
ret |= ld_add_file_list(s1, filename);
} else if (c == 'I' || c == 'G' || c == 'A') {
ret = ld_add_file(s1, filename);
ret |= !!ld_add_file(s1, filename);
}
if (ret)
return -1;
if (ret < 0)
return ret;
t = ld_next(s1, filename, sizeof(filename));
if (t == ',')
t = ld_next(s1, filename, sizeof(filename));
}
if (c == 'G' && new_undef_sym(s1, sym_offset))
if (c == 'G' && ret == 0 && new_undef_sym(s1, sym_offset))
goto repeat;
return 0;
return ret;
}
/* interpret a subset of GNU ldscripts to handle the dummy libc.so
@ -4001,7 +4001,7 @@ repeat:
ST_FUNC int tcc_load_ldscript(TCCState *s1, int fd)
{
char cmd[64];
int t, ret = FILE_NOT_RECOGNIZED;
int t, ret = 0, noscript = 1;
unsigned char *text_ptr, *saved_ptr;
saved_ptr = s1->ld_p;
@ -4012,19 +4012,22 @@ ST_FUNC int tcc_load_ldscript(TCCState *s1, int fd)
break;
if (!strcmp(cmd, "INPUT") ||
!strcmp(cmd, "GROUP")) {
ret = ld_add_file_list(s1, cmd);
ret |= ld_add_file_list(s1, cmd);
} else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
!strcmp(cmd, "TARGET")) {
/* ignore some commands */
ret = ld_add_file_list(s1, cmd);
} else if (0 == ret) {
ret |= ld_add_file_list(s1, cmd);
} else if (noscript) {
ret = FILE_NOT_RECOGNIZED;
} else {
ret = tcc_error_noabort("unexpected '%s'", cmd);
}
if (ret)
if (ret < 0)
break;
noscript = 0;
}
tcc_free(text_ptr);
s1->ld_p = saved_ptr;
return ret;
return ret < 0 ? ret : -ret;
}
#endif /* !ELF_OBJ_ONLY */

View File

@ -6839,15 +6839,15 @@ static void end_switch(void)
/* ------------------------------------------------------------------------- */
/* __attribute__((cleanup(fn))) */
/* save SValue of symbol to local stack */
static void save_cleanup_sym(Sym *s)
/* protect symbol lvalues from further modification */
static void save_lvalues(void)
{
SValue *sv = vtop;
while (sv >= vstack) {
if (sv->sym == s) {
if (sv->sym && (sv->r & VT_LVAL)) {
int align, size = type_size(&sv->type, &align);
loc = (loc - size) & -align;
vset(&sv->type, VT_LOCAL | VT_LVAL, loc);
int r2, l = get_temp_local_var(size, align, &r2);
vset(&sv->type, VT_LOCAL | VT_LVAL, l), vtop->r2 = r2;
vpushv(sv), *sv = vtop[-1], vstore(), --vtop;
}
--sv;
@ -6860,7 +6860,7 @@ static void try_call_scope_cleanup(Sym *stop)
for (; cls != stop; cls = cls->next) {
Sym *fs = cls->cleanup_func;
Sym *vs = cls->prev_tok;
save_cleanup_sym(vs);
save_lvalues();
vpushsym(&fs->type, fs);
vset(&vs->type, vs->r, vs->c);
vtop->sym = vs;

30
tccpe.c
View File

@ -1254,7 +1254,7 @@ static int pe_check_symbols(struct pe_info *pe)
if (sym->st_shndx == SHN_UNDEF) {
const char *name = (char*)symtab_section->link->data + sym->st_name;
unsigned type = ELFW(ST_TYPE)(sym->st_info);
int imp_sym;
int imp_sym = 0;
struct import_symbol *is;
int _imp_, n;
@ -1928,6 +1928,34 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
pe->type = pe_type;
}
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg)
{
static const struct subsy { const char* p; int v; } x[] = {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
{ "native", 1 },
{ "gui", 2 },
{ "windows", 2 },
{ "console", 3 },
{ "posix", 7 },
{ "efiapp", 10 },
{ "efiboot", 11 },
{ "efiruntime", 12 },
{ "efirom", 13 },
#elif defined(TCC_TARGET_ARM)
{ "wince", 9 },
#endif
{ 0, -1 }};
const struct subsy *y;
for (y = x;; ++y) {
if (!y->p)
return -1;
if (0 == strcmp(y->p, arg)) {
s1->pe_subsystem = y->v;
return 0;
}
}
}
static void pe_set_options(TCCState * s1, struct pe_info *pe)
{
if (PE_DLL == pe->type) {

View File

@ -3076,6 +3076,11 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args)
cval.str.size = tokcstr.size;
cval.str.data = tokcstr.data;
tok_str_add2(&str, TOK_PPSTR, &cval);
#ifdef TCC_TARGET_ARM
} else if ((parse_flags & PARSE_FLAG_ASM_FILE) && t == TOK_PPNUM) {
/* for example: mov r1,#0 */
--macro_str, tok_str_add(&str, '#');
#endif
} else {
expect("macro parameter after '#'");
}

View File

@ -1,8 +1,11 @@
#include <stdatomic.h>
#include <stdbool.h>
#include <assert.h>
#include <stdio.h>
// standard assert would popup a dialog box on windows
#define assert(x) \
printf("assert \"%s\" : %s\n", #x, (x) ? "yes" : "no");
int main() {
// Test 1: Basic functionality of __atomic_store_n and __atomic_load_n
{

View File

@ -1 +1,11 @@
assert "loaded == 42" : yes
assert "success" : yes
assert "atomic_var == 200" : yes
assert "expected == 100" : yes
assert "!success" : yes
assert "atomic_var == 100" : yes
assert "expected == 100" : yes
assert "atomic_var == 60" : yes
assert "loaded_ptr == NULL" : yes
assert "__atomic_load_n(&atomic_var, __ATOMIC_RELAXED) == 10" : yes
All atomic tests passed!

View File

@ -809,6 +809,8 @@ void gfunc_call(int nb_args)
gbound_args(nb_args);
#endif
save_regs(nb_args);
args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE;
arg = nb_args;
@ -906,7 +908,7 @@ void gfunc_call(int nb_args)
}
vtop--;
}
save_regs(0);
/* Copy R10 and R11 into RCX and RDX, respectively */
if (nb_args > 0) {
o(0xd1894c); /* mov %r10, %rcx */
@ -1249,6 +1251,8 @@ void gfunc_call(int nb_args)
gbound_args(nb_args);
#endif
save_regs(nb_args);
/* calculate the number of integer/float register arguments, remember
arguments to be passed via stack (in onstack[]), and also remember
if we have to align the stack pointer to 16 (onstack[i] == 2). Needs
@ -1364,9 +1368,6 @@ void gfunc_call(int nb_args)
tcc_free(onstack);
/* XXX This should be superfluous. */
save_regs(0); /* save used temporary registers */
/* then, we prepare register passing arguments.
Note that we cannot set RDX and RCX in this loop because gv()
may break these temporary registers. Let's use R10 and R11
@ -1416,12 +1417,6 @@ void gfunc_call(int nb_args)
assert(gen_reg == 0);
assert(sse_reg == 0);
/* We shouldn't have many operands on the stack anymore, but the
call address itself is still there, and it might be in %eax
(or edx/ecx) currently, which the below writes would clobber.
So evict all remaining operands here. */
save_regs(0);
/* Copy R10 and R11 into RDX and RCX, respectively */
if (nb_reg_args > 2) {
o(0xd2894c); /* mov %r10, %rdx */