mirror of
git://repo.or.cz/tinycc.git
synced 2026-06-20 11:54:18 +08:00
Fix Windows ARM64 runtime regressions and coverage
This commit is contained in:
parent
7e7917c3c9
commit
396675f74f
21
.github/workflows/build.yml
vendored
21
.github/workflows/build.yml
vendored
@ -11,7 +11,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: make & test tcc (x86_64-linux)
|
||||
run: ./configure && make && make test -k
|
||||
run: |
|
||||
./configure
|
||||
make
|
||||
make test -k
|
||||
make -C tests/tests2 tests2.112 tests2.128
|
||||
|
||||
test-x86_64-osx:
|
||||
runs-on: macos-15-intel
|
||||
@ -54,6 +58,8 @@ jobs:
|
||||
cd win32
|
||||
call build-tcc.bat -t 64 -c cl
|
||||
echo ::endgroup::
|
||||
.\tcc -B. ..\win32\test_pe_field_alignment.c -o test_pe_field_alignment.exe && .\test_pe_field_alignment.exe > test_pe_field_alignment.out
|
||||
fc /n test_pe_field_alignment.out ..\win32\test_pe_field_alignment.ref
|
||||
.\tcc -I.. libtcc.dll -v ../tests/libtcc_test.c -o libtest.exe && .\libtest.exe
|
||||
.\tcc -I.. libtcc.dll -run ../tests/libtcc_test.c
|
||||
|
||||
@ -100,9 +106,17 @@ jobs:
|
||||
cd win32
|
||||
call build-tcc.bat -t arm64 -c clang
|
||||
echo ::endgroup::
|
||||
if not exist libtcc.dll exit /b 1
|
||||
.\tcc -B. -v
|
||||
.\tcc -B. -run ..\win32\test_run_argv.c alpha "two words" beta > test_run_argv.out
|
||||
fc /n test_run_argv.out ..\win32\test_run_argv.ref
|
||||
type ..\win32\test_run_stdin.ref | .\tcc -B. -run ..\win32\test_run_stdin.c > test_run_stdin.out
|
||||
fc /n test_run_stdin.out ..\win32\test_run_stdin.ref
|
||||
powershell -NoProfile -Command "$before = @(Get-ChildItem -Path $env:TEMP -Filter 'tcc*.tmp' -Name -ErrorAction SilentlyContinue); & .\tcc -B. -run ..\win32\test_run_exit.c; if ($LASTEXITCODE -ne 27) { exit 1 }; $after = @(Get-ChildItem -Path $env:TEMP -Filter 'tcc*.tmp' -Name -ErrorAction SilentlyContinue); if (Compare-Object $before $after) { Write-Host 'Temporary -run file cleanup mismatch'; Compare-Object $before $after; exit 1 }"
|
||||
.\tcc -B. ..\win32\test_arm64.c -o test_arm64.exe && .\test_arm64.exe
|
||||
.\tcc -B. -run ..\examples\ex1.c
|
||||
clang -O0 -I.. ..\win32\test_arm64_libtcc_context.c ..\win32\test_arm64_libtcc_context.S -o test_libtcc_context.exe
|
||||
.\test_libtcc_context.exe
|
||||
.\tcc -B. ..\win32\test_arm64_inline_asm.c -o test_inline_asm.exe && .\test_inline_asm.exe > test_inline_asm.out
|
||||
> test_inline_asm.expect echo inline asm ok
|
||||
fc /n test_inline_asm.out test_inline_asm.expect
|
||||
@ -173,7 +187,10 @@ jobs:
|
||||
apt-get install -q -y gcc make
|
||||
run: |
|
||||
echo "::endgroup::" # flatten 'run container'
|
||||
./configure && make && make test -k
|
||||
./configure
|
||||
make
|
||||
make test -k
|
||||
make -C tests/tests2 tests2.73 tests2.109 tests2.121 tests2.133
|
||||
|
||||
test-riscv64-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@ -175,6 +175,10 @@ In a script, it gives the following header:
|
||||
@example
|
||||
#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11
|
||||
@end example
|
||||
On native Windows ARM64 builds, CLI @option{-run} currently uses a
|
||||
temporary executable for ordinary runs as a compatibility workaround.
|
||||
@code{libtcc} @code{tcc_run()} remains in-process, and CLI keeps the
|
||||
in-process path for @option{-dt} and @option{-rstdin}.
|
||||
|
||||
@item -v
|
||||
Display TCC version.
|
||||
|
||||
37
tcc.c
37
tcc.c
@ -234,6 +234,36 @@ static void print_search_dirs(TCCState *s)
|
||||
|
||||
static void set_environment(TCCState *s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static const char * const names[] = {
|
||||
"C_INCLUDE_PATH",
|
||||
"CPATH",
|
||||
"LIBRARY_PATH",
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
|
||||
DWORD len = GetEnvironmentVariableA(names[i], NULL, 0);
|
||||
char *path;
|
||||
|
||||
if (!len)
|
||||
continue;
|
||||
path = tcc_malloc(len);
|
||||
if (!path)
|
||||
continue;
|
||||
if (!GetEnvironmentVariableA(names[i], path, len)) {
|
||||
tcc_free(path);
|
||||
continue;
|
||||
}
|
||||
if (i == 0)
|
||||
tcc_add_sysinclude_path(s, path);
|
||||
else if (i == 1)
|
||||
tcc_add_include_path(s, path);
|
||||
else
|
||||
tcc_add_library_path(s, path);
|
||||
tcc_free(path);
|
||||
}
|
||||
#else
|
||||
char * path;
|
||||
|
||||
path = getenv("C_INCLUDE_PATH");
|
||||
@ -248,6 +278,7 @@ static void set_environment(TCCState *s)
|
||||
if(path != NULL) {
|
||||
tcc_add_library_path(s, path);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *default_outputfile(TCCState *s, const char *first_file)
|
||||
@ -337,9 +368,6 @@ static int tcc_run_via_temp_exe(TCCState *s, int argc, char **argv)
|
||||
return tcc_error_noabort("could not get temp directory"), -1;
|
||||
if (!GetTempFileNameA(tmpdir, "tcc", 0, tmppath))
|
||||
return tcc_error_noabort("could not create temp file name"), -1;
|
||||
DeleteFileA(tmppath);
|
||||
strcpy(tcc_fileextension(tmppath), ".exe");
|
||||
DeleteFileA(tmppath);
|
||||
|
||||
saved_outfile = s->outfile;
|
||||
saved_output_type = s->output_type;
|
||||
@ -403,6 +431,9 @@ static int tcc_run_via_temp_exe(TCCState *s, int argc, char **argv)
|
||||
|
||||
static int tcc_run_requires_inprocess(const TCCState *s)
|
||||
{
|
||||
/* Temporary Windows ARM64 workaround: keep the generic in-process path
|
||||
where CLI features rely on it, but run ordinary `tcc -run` through a
|
||||
child process until the native runtime path is fully equivalent. */
|
||||
return (s->dflag & 16) || s->run_stdin != NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
2
tcc.h
2
tcc.h
@ -592,7 +592,7 @@ typedef struct Section {
|
||||
typedef struct DLLReference {
|
||||
int level;
|
||||
void *handle;
|
||||
unsigned char found, index;
|
||||
unsigned char found, index, process_scoped;
|
||||
char name[1];
|
||||
} DLLReference;
|
||||
|
||||
|
||||
1
tccgen.c
1
tccgen.c
@ -4226,6 +4226,7 @@ static void struct_layout(CType *type, AttributeDef *ad)
|
||||
}
|
||||
/* some individual align was specified */
|
||||
#ifdef TCC_TARGET_PE
|
||||
/* GNU aligned(n) on a field is a minimum, not a way to lower alignment. */
|
||||
if (a > align)
|
||||
align = a;
|
||||
#else
|
||||
|
||||
43
tccpe.c
43
tccpe.c
@ -29,6 +29,41 @@
|
||||
#include <sys/stat.h> /* chmod() */
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(TCC_IS_NATIVE) && defined(TCC_TARGET_ARM64)
|
||||
static TCCSem pe_msvcrt_sem;
|
||||
|
||||
static HMODULE pe_get_process_msvcrt_handle(void)
|
||||
{
|
||||
static HMODULE handle;
|
||||
HMODULE dll;
|
||||
|
||||
wait_sem(&pe_msvcrt_sem);
|
||||
dll = handle;
|
||||
if (!dll) {
|
||||
dll = LoadLibraryA("msvcrt.dll");
|
||||
if (dll)
|
||||
handle = dll;
|
||||
}
|
||||
post_sem(&pe_msvcrt_sem);
|
||||
return dll;
|
||||
}
|
||||
|
||||
static HMODULE pe_load_runtime_dll(const char *name, unsigned char *process_scoped)
|
||||
{
|
||||
HMODULE dll = NULL;
|
||||
|
||||
*process_scoped = 0;
|
||||
if (0 == PATHCMP(tcc_basename(name), "msvcrt.dll")) {
|
||||
dll = pe_get_process_msvcrt_handle();
|
||||
if (dll)
|
||||
*process_scoped = 1;
|
||||
}
|
||||
if (!dll)
|
||||
dll = LoadLibraryA(name);
|
||||
return dll;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
# define ADDR3264 ULONGLONG
|
||||
# define PE_IMAGE_REL IMAGE_REL_BASED_DIR64
|
||||
@ -995,8 +1030,14 @@ static void pe_build_imports(struct pe_info *pe)
|
||||
#ifdef TCC_IS_NATIVE
|
||||
if (pe->type == PE_RUN) {
|
||||
if (dllref) {
|
||||
if ( !dllref->handle )
|
||||
if (!dllref->handle) {
|
||||
#if defined(_WIN32) && defined(TCC_TARGET_ARM64)
|
||||
dllref->handle = pe_load_runtime_dll(dllref->name,
|
||||
&dllref->process_scoped);
|
||||
#else
|
||||
dllref->handle = LoadLibraryA(dllref->name);
|
||||
#endif
|
||||
}
|
||||
v = (ADDR3264)GetProcAddress(dllref->handle, ordinal?(char*)0+ordinal:name);
|
||||
}
|
||||
if (!v)
|
||||
|
||||
46
tccrun.c
46
tccrun.c
@ -181,10 +181,7 @@ ST_FUNC void tcc_run_free(TCCState *s1)
|
||||
if ( ref->handle )
|
||||
#ifdef _WIN32
|
||||
# if defined(__aarch64__)
|
||||
/* Native ARM64 builds currently host libtcc with the UCRT while
|
||||
generated PE code still imports msvcrt. Unloading msvcrt from
|
||||
nested -run states corrupts teardown, so leave it process-wide. */
|
||||
if (0 == PATHCMP(tcc_basename(ref->name), "msvcrt.dll"))
|
||||
if (ref->process_scoped)
|
||||
continue;
|
||||
# endif
|
||||
FreeLibrary((HMODULE)ref->handle);
|
||||
@ -215,20 +212,28 @@ ST_FUNC void tcc_run_free(TCCState *s1)
|
||||
#ifdef _WIN32
|
||||
static char **rt_get_environ(void)
|
||||
{
|
||||
#ifdef __TINYC__
|
||||
return NULL;
|
||||
char **env = NULL;
|
||||
#ifdef _UCRT
|
||||
char ***penv = __p__environ();
|
||||
if (penv)
|
||||
env = *penv;
|
||||
#else
|
||||
return environ;
|
||||
_get_environ(&env);
|
||||
#endif
|
||||
return env;
|
||||
}
|
||||
|
||||
static wchar_t **rt_get_wenviron(void)
|
||||
{
|
||||
#ifdef __TINYC__
|
||||
return NULL;
|
||||
wchar_t **env = NULL;
|
||||
#ifdef _UCRT
|
||||
wchar_t ***penv = __p__wenviron();
|
||||
if (penv)
|
||||
env = *penv;
|
||||
#else
|
||||
return _wenviron;
|
||||
_get_wenviron(&env);
|
||||
#endif
|
||||
return env;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -260,6 +265,8 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
|
||||
#if defined(__APPLE__)
|
||||
extern char ***_NSGetEnviron(void);
|
||||
char **envp = *_NSGetEnviron();
|
||||
#elif defined(_WIN32)
|
||||
char **envp = rt_get_environ();
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
extern char **environ;
|
||||
char **envp = environ;
|
||||
@ -1442,6 +1449,23 @@ static PVOID rt_exception_handler;
|
||||
#if defined(_WIN64) && defined(__aarch64__) && !defined(CONFIG_TCC_BACKTRACE_ONLY)
|
||||
typedef VOID (__cdecl *rt_restore_context_func_t)(PCONTEXT, struct _EXCEPTION_RECORD *);
|
||||
|
||||
#define RT_ARM64_CONTEXT_ASSERT(name, expr) \
|
||||
typedef char rt_arm64_context_assert_##name[(expr) ? 1 : -1]
|
||||
RT_ARM64_CONTEXT_ASSERT(size, sizeof(CONTEXT) == 0x390);
|
||||
RT_ARM64_CONTEXT_ASSERT(flags_offset, offsetof(CONTEXT, ContextFlags) == 0x000);
|
||||
RT_ARM64_CONTEXT_ASSERT(x_offset, offsetof(CONTEXT, X) == 0x008);
|
||||
RT_ARM64_CONTEXT_ASSERT(fp_offset, offsetof(CONTEXT, Fp) == 0x0f0);
|
||||
RT_ARM64_CONTEXT_ASSERT(lr_offset, offsetof(CONTEXT, Lr) == 0x0f8);
|
||||
RT_ARM64_CONTEXT_ASSERT(sp_offset, offsetof(CONTEXT, Sp) == 0x100);
|
||||
RT_ARM64_CONTEXT_ASSERT(pc_offset, offsetof(CONTEXT, Pc) == 0x108);
|
||||
RT_ARM64_CONTEXT_ASSERT(v_offset, offsetof(CONTEXT, V) == 0x110);
|
||||
RT_ARM64_CONTEXT_ASSERT(v_slot_size, sizeof(((CONTEXT *)0)->V[0]) == 16);
|
||||
RT_ARM64_CONTEXT_ASSERT(fpcr_offset, offsetof(CONTEXT, Fpcr) == 0x310);
|
||||
RT_ARM64_CONTEXT_ASSERT(fpsr_offset, offsetof(CONTEXT, Fpsr) == 0x314);
|
||||
RT_ARM64_CONTEXT_ASSERT(bvr_offset, offsetof(CONTEXT, Bvr) == 0x338);
|
||||
RT_ARM64_CONTEXT_ASSERT(wvr_offset, offsetof(CONTEXT, Wvr) == 0x380);
|
||||
#undef RT_ARM64_CONTEXT_ASSERT
|
||||
|
||||
static rt_restore_context_func_t rt_get_restore_context_func(void)
|
||||
{
|
||||
static rt_restore_context_func_t fn;
|
||||
@ -1479,7 +1503,7 @@ static void rt_restore_context_from_jmpbuf(void *p_jmp_buf, int code)
|
||||
ctx.Sp = jb->Sp;
|
||||
ctx.Pc = jb->Lr;
|
||||
for (i = 0; i < 8; ++i)
|
||||
memcpy(&ctx.V[8 + i], &jb->D[i], sizeof(jb->D[i]));
|
||||
memcpy(&ctx.V[8 + i].D[0], &jb->D[i], sizeof(jb->D[i]));
|
||||
ctx.Fpcr = jb->Fpcr;
|
||||
ctx.Fpsr = jb->Fpsr;
|
||||
fn = rt_get_restore_context_func();
|
||||
|
||||
@ -119,7 +119,6 @@ goto :p3
|
||||
:tarm64
|
||||
set D=%DARM64%
|
||||
set P=%PARM64%
|
||||
set TCC_C=..\tcc.c
|
||||
goto :p3
|
||||
|
||||
:p3
|
||||
|
||||
@ -1423,7 +1423,7 @@ typedef DWORD LCID;
|
||||
|
||||
typedef PRUNTIME_FUNCTION (*PGET_RUNTIME_FUNCTION_CALLBACK)(DWORD64 ControlPc,PVOID Context);
|
||||
|
||||
#ifdef _ARM64_
|
||||
#if defined(_ARM64_) || defined(__aarch64__)
|
||||
|
||||
/* ARM64 Context Definition */
|
||||
#define CONTEXT_ARM64 0x00400000
|
||||
@ -1448,26 +1448,83 @@ typedef DWORD LCID;
|
||||
#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG)
|
||||
#endif
|
||||
|
||||
#ifndef _ARM64_CONTEXT_DECLARED
|
||||
#define _ARM64_CONTEXT_DECLARED
|
||||
typedef struct _CONTEXT {
|
||||
DWORD64 ContextFlags;
|
||||
DWORD64 X[29];
|
||||
DWORD64 Fp;
|
||||
DWORD64 Lr;
|
||||
DWORD64 Sp;
|
||||
DWORD64 Pc;
|
||||
DWORD64 V[32];
|
||||
DWORD Fpcr;
|
||||
DWORD Fpsr;
|
||||
DWORD Bcr[8];
|
||||
DWORD Bvr[8];
|
||||
DWORD Wcr[2];
|
||||
DWORD Wvr[2];
|
||||
} CONTEXT,*PCONTEXT;
|
||||
#ifndef ARM64_MAX_BREAKPOINTS
|
||||
#define ARM64_MAX_BREAKPOINTS 8
|
||||
#endif
|
||||
#ifndef ARM64_MAX_WATCHPOINTS
|
||||
#define ARM64_MAX_WATCHPOINTS 2
|
||||
#endif
|
||||
|
||||
#endif /* _ARM64_ */
|
||||
#ifndef _ARM64_NT_NEON128_DECLARED
|
||||
#define _ARM64_NT_NEON128_DECLARED
|
||||
typedef union _ARM64_NT_NEON128 {
|
||||
struct {
|
||||
ULONGLONG Low;
|
||||
LONGLONG High;
|
||||
} DUMMYSTRUCTNAME;
|
||||
double D[2];
|
||||
float S[4];
|
||||
WORD H[8];
|
||||
BYTE B[16];
|
||||
} ARM64_NT_NEON128,*PARM64_NT_NEON128;
|
||||
#endif
|
||||
|
||||
#ifndef _ARM64_CONTEXT_DECLARED
|
||||
#define _ARM64_CONTEXT_DECLARED
|
||||
typedef struct DECLSPEC_ALIGN(16) _ARM64_NT_CONTEXT {
|
||||
ULONG ContextFlags;
|
||||
ULONG Cpsr;
|
||||
union {
|
||||
struct {
|
||||
DWORD64 X0;
|
||||
DWORD64 X1;
|
||||
DWORD64 X2;
|
||||
DWORD64 X3;
|
||||
DWORD64 X4;
|
||||
DWORD64 X5;
|
||||
DWORD64 X6;
|
||||
DWORD64 X7;
|
||||
DWORD64 X8;
|
||||
DWORD64 X9;
|
||||
DWORD64 X10;
|
||||
DWORD64 X11;
|
||||
DWORD64 X12;
|
||||
DWORD64 X13;
|
||||
DWORD64 X14;
|
||||
DWORD64 X15;
|
||||
DWORD64 X16;
|
||||
DWORD64 X17;
|
||||
DWORD64 X18;
|
||||
DWORD64 X19;
|
||||
DWORD64 X20;
|
||||
DWORD64 X21;
|
||||
DWORD64 X22;
|
||||
DWORD64 X23;
|
||||
DWORD64 X24;
|
||||
DWORD64 X25;
|
||||
DWORD64 X26;
|
||||
DWORD64 X27;
|
||||
DWORD64 X28;
|
||||
DWORD64 Fp;
|
||||
DWORD64 Lr;
|
||||
} DUMMYSTRUCTNAME;
|
||||
DWORD64 X[31];
|
||||
} DUMMYUNIONNAME;
|
||||
DWORD64 Sp;
|
||||
DWORD64 Pc;
|
||||
ARM64_NT_NEON128 V[32];
|
||||
DWORD Fpcr;
|
||||
DWORD Fpsr;
|
||||
DWORD Bcr[ARM64_MAX_BREAKPOINTS];
|
||||
DWORD64 Bvr[ARM64_MAX_BREAKPOINTS];
|
||||
DWORD Wcr[ARM64_MAX_WATCHPOINTS];
|
||||
DWORD64 Wvr[ARM64_MAX_WATCHPOINTS];
|
||||
} ARM64_NT_CONTEXT,*PARM64_NT_CONTEXT;
|
||||
#endif
|
||||
|
||||
typedef ARM64_NT_CONTEXT CONTEXT,*PCONTEXT;
|
||||
|
||||
#endif /* _ARM64_ || __aarch64__ */
|
||||
typedef DWORD (*POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)(HANDLE Process,PVOID TableAddress,PDWORD Entries,PRUNTIME_FUNCTION *Functions);
|
||||
|
||||
#define OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME "OutOfProcessFunctionTableCallback"
|
||||
|
||||
34
win32/test_arm64_libtcc_context.S
Normal file
34
win32/test_arm64_libtcc_context.S
Normal file
@ -0,0 +1,34 @@
|
||||
.text
|
||||
.p2align 2
|
||||
.global arm64_call_with_dregs
|
||||
|
||||
arm64_call_with_dregs:
|
||||
stp x29, x30, [sp, #-32]!
|
||||
stp x19, x20, [sp, #16]
|
||||
mov x29, sp
|
||||
mov x19, x1
|
||||
|
||||
ldr d8, [x0, #0]
|
||||
ldr d9, [x0, #8]
|
||||
ldr d10, [x0, #16]
|
||||
ldr d11, [x0, #24]
|
||||
ldr d12, [x0, #32]
|
||||
ldr d13, [x0, #40]
|
||||
ldr d14, [x0, #48]
|
||||
ldr d15, [x0, #56]
|
||||
|
||||
mov x0, x3
|
||||
blr x2
|
||||
|
||||
str d8, [x19, #0]
|
||||
str d9, [x19, #8]
|
||||
str d10, [x19, #16]
|
||||
str d11, [x19, #24]
|
||||
str d12, [x19, #32]
|
||||
str d13, [x19, #40]
|
||||
str d14, [x19, #48]
|
||||
str d15, [x19, #56]
|
||||
|
||||
ldp x19, x20, [sp, #16]
|
||||
ldp x29, x30, [sp], #32
|
||||
ret
|
||||
126
win32/test_arm64_libtcc_context.c
Normal file
126
win32/test_arm64_libtcc_context.c
Normal file
@ -0,0 +1,126 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "libtcc.h"
|
||||
|
||||
typedef struct libtcc_api {
|
||||
TCCState *(*tcc_new)(void);
|
||||
void (*tcc_delete)(TCCState *s);
|
||||
void (*tcc_set_lib_path)(TCCState *s, const char *path);
|
||||
void (*tcc_set_error_func)(TCCState *s, void *opaque, TCCErrorFunc *error_func);
|
||||
int (*tcc_set_output_type)(TCCState *s, int output_type);
|
||||
int (*tcc_add_include_path)(TCCState *s, const char *pathname);
|
||||
int (*tcc_add_library_path)(TCCState *s, const char *pathname);
|
||||
int (*tcc_compile_string)(TCCState *s, const char *buf);
|
||||
int (*tcc_run)(TCCState *s, int argc, char **argv);
|
||||
} libtcc_api;
|
||||
|
||||
typedef struct test_context {
|
||||
libtcc_api api;
|
||||
} test_context;
|
||||
|
||||
extern int arm64_call_with_dregs(const double *expected, double *actual,
|
||||
int (*fn)(void *opaque), void *opaque);
|
||||
|
||||
static const char run_program[] =
|
||||
"void exit(int);\n"
|
||||
"int main(int argc, char **argv)\n"
|
||||
"{\n"
|
||||
" if (argc != 2)\n"
|
||||
" return 11;\n"
|
||||
" if (argv[1][0] != 'x' || argv[1][1] != '\\0')\n"
|
||||
" return 12;\n"
|
||||
" exit(42);\n"
|
||||
"}\n";
|
||||
|
||||
static void handle_error(void *opaque, const char *msg)
|
||||
{
|
||||
fprintf((FILE *)opaque, "%s\n", msg);
|
||||
}
|
||||
|
||||
static FARPROC load_symbol(HMODULE dll, const char *name)
|
||||
{
|
||||
FARPROC proc = GetProcAddress(dll, name);
|
||||
if (!proc) {
|
||||
fprintf(stderr, "missing libtcc symbol: %s\n", name);
|
||||
exit(1);
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
|
||||
static int run_once(void *opaque)
|
||||
{
|
||||
test_context *ctx = (test_context *)opaque;
|
||||
TCCState *s;
|
||||
char *argv[] = { "arm64_libtcc_context", "x" };
|
||||
int ret;
|
||||
|
||||
s = ctx->api.tcc_new();
|
||||
if (!s) {
|
||||
fprintf(stderr, "tcc_new failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx->api.tcc_set_error_func(s, stderr, handle_error);
|
||||
ctx->api.tcc_set_lib_path(s, ".");
|
||||
ctx->api.tcc_add_include_path(s, ".\\include");
|
||||
ctx->api.tcc_add_library_path(s, ".\\lib");
|
||||
|
||||
ret = ctx->api.tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
|
||||
if (!ret)
|
||||
ret = ctx->api.tcc_compile_string(s, run_program);
|
||||
if (!ret)
|
||||
ret = ctx->api.tcc_run(s, 2, argv);
|
||||
|
||||
ctx->api.tcc_delete(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static const double expected[8] = {
|
||||
1.25, -2.5, 3.75, -4.875,
|
||||
5.5, -6.625, 7.75, -8.875
|
||||
};
|
||||
double actual[8];
|
||||
HMODULE dll;
|
||||
test_context ctx;
|
||||
int i;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
dll = LoadLibraryA("libtcc.dll");
|
||||
if (!dll) {
|
||||
fprintf(stderr, "failed to load libtcc.dll\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx.api.tcc_new = (void *)load_symbol(dll, "tcc_new");
|
||||
ctx.api.tcc_delete = (void *)load_symbol(dll, "tcc_delete");
|
||||
ctx.api.tcc_set_lib_path = (void *)load_symbol(dll, "tcc_set_lib_path");
|
||||
ctx.api.tcc_set_error_func = (void *)load_symbol(dll, "tcc_set_error_func");
|
||||
ctx.api.tcc_set_output_type = (void *)load_symbol(dll, "tcc_set_output_type");
|
||||
ctx.api.tcc_add_include_path = (void *)load_symbol(dll, "tcc_add_include_path");
|
||||
ctx.api.tcc_add_library_path = (void *)load_symbol(dll, "tcc_add_library_path");
|
||||
ctx.api.tcc_compile_string = (void *)load_symbol(dll, "tcc_compile_string");
|
||||
ctx.api.tcc_run = (void *)load_symbol(dll, "tcc_run");
|
||||
|
||||
for (i = 0; i < 32; ++i) {
|
||||
memset(actual, 0, sizeof(actual));
|
||||
if (arm64_call_with_dregs(expected, actual, run_once, &ctx) != 42) {
|
||||
fprintf(stderr, "tcc_run did not return 42 on iteration %d\n", i);
|
||||
FreeLibrary(dll);
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(expected, actual, sizeof(expected)) != 0) {
|
||||
fprintf(stderr, "nonvolatile FP registers were not restored on iteration %d\n", i);
|
||||
FreeLibrary(dll);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
puts("arm64 libtcc context ok");
|
||||
FreeLibrary(dll);
|
||||
return 0;
|
||||
}
|
||||
16
win32/test_pe_field_alignment.c
Normal file
16
win32/test_pe_field_alignment.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct pe_field_align_min {
|
||||
char c;
|
||||
double d __attribute__((aligned(1)));
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("size=%u align=%u off=%u\n",
|
||||
(unsigned)sizeof(struct pe_field_align_min),
|
||||
(unsigned)__alignof__(struct pe_field_align_min),
|
||||
(unsigned)offsetof(struct pe_field_align_min, d));
|
||||
return 0;
|
||||
}
|
||||
1
win32/test_pe_field_alignment.ref
Normal file
1
win32/test_pe_field_alignment.ref
Normal file
@ -0,0 +1 @@
|
||||
size=16 align=8 off=8
|
||||
11
win32/test_run_argv.c
Normal file
11
win32/test_run_argv.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("argc=%d\n", argc);
|
||||
for (i = 1; i < argc; ++i)
|
||||
printf("arg%d=<%s>\n", i, argv[i]);
|
||||
return 0;
|
||||
}
|
||||
4
win32/test_run_argv.ref
Normal file
4
win32/test_run_argv.ref
Normal file
@ -0,0 +1,4 @@
|
||||
argc=4
|
||||
arg1=<alpha>
|
||||
arg2=<two words>
|
||||
arg3=<beta>
|
||||
6
win32/test_run_exit.c
Normal file
6
win32/test_run_exit.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return 27;
|
||||
}
|
||||
11
win32/test_run_stdin.c
Normal file
11
win32/test_run_stdin.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
if (!fgets(buf, sizeof(buf), stdin))
|
||||
return 1;
|
||||
fputs(buf, stdout);
|
||||
return 0;
|
||||
}
|
||||
1
win32/test_run_stdin.ref
Normal file
1
win32/test_run_stdin.ref
Normal file
@ -0,0 +1 @@
|
||||
temp exe stdin
|
||||
Loading…
Reference in New Issue
Block a user