mirror of
https://github.com/frida/tinycc
synced 2024-12-22 13:12:34 +03:00
72729d8e36
This allows creation of TCCStates and operation with API calls independently from each other, even from threads. Frontend (option parsing/libtcc.c) and backend (linker/tccelf.c) now depend only on the TCCState (s1) argument. Compilation per se (tccpp.c, tccgen.c) is still using globals for convenience. There is only one entry point to this section which is tcc_compile() which is protected by a semaphore. There are some hacks involved to avoid too many changes, as well as some changes in order to avoid too many hacks ;) The test libtcc_test_mt.c shows the feature. Except this new file the patch adds 87 lines overall.
293 lines
6.2 KiB
C
293 lines
6.2 KiB
C
/*
|
|
* Multi-thread Test for libtcc
|
|
*/
|
|
|
|
#ifndef FIB
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "libtcc.h"
|
|
|
|
#define M 20 /* number of states */
|
|
#define F(n) (n % 20 + 2) /* fib argument */
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#define TF_TYPE(func, param) DWORD WINAPI func(void *param)
|
|
typedef TF_TYPE(ThreadFunc, x);
|
|
HANDLE hh[M];
|
|
void create_thread(ThreadFunc f, int n)
|
|
{
|
|
DWORD tid;
|
|
hh[n] = CreateThread(NULL, 0, f, (void*)(size_t)n, 0, &tid);
|
|
}
|
|
void wait_threads(int n)
|
|
{
|
|
WaitForMultipleObjects(n, hh, TRUE, INFINITE);
|
|
while (n)
|
|
CloseHandle(hh[--n]);
|
|
}
|
|
void sleep_ms(unsigned n)
|
|
{
|
|
Sleep(n);
|
|
}
|
|
#else
|
|
#include <pthread.h>
|
|
#define TF_TYPE(func, param) void* func(void *param)
|
|
typedef TF_TYPE(ThreadFunc, x);
|
|
pthread_t hh[M];
|
|
void create_thread(ThreadFunc f, int n)
|
|
{
|
|
pthread_create(&hh[n], NULL, f, (void*)(size_t)n);
|
|
}
|
|
void wait_threads(int n)
|
|
{
|
|
while (n)
|
|
pthread_join(hh[--n], NULL);
|
|
|
|
}
|
|
void sleep_ms(unsigned n)
|
|
{
|
|
usleep(n * 1000);
|
|
}
|
|
#endif
|
|
|
|
void handle_error(void *opaque, const char *msg)
|
|
{
|
|
fprintf(opaque, "%s\n", msg);
|
|
}
|
|
|
|
/* this function is called by the generated code */
|
|
int add(int a, int b)
|
|
{
|
|
return a + b;
|
|
}
|
|
|
|
#define _str(s) #s
|
|
#define str(s) _str(s)
|
|
/* as a trick, prepend #line directive for better error/warning messages */
|
|
#define PROG(lbl) \
|
|
char lbl[] = "#line " str(__LINE__) " " str(__FILE__) "\n\n"
|
|
|
|
PROG(my_program)
|
|
"#include <tcclib.h>\n" /* include the "Simple libc header for TCC" */
|
|
"int add(int a, int b);\n"
|
|
"int fib(int n)\n"
|
|
"{\n"
|
|
" if (n <= 2)\n"
|
|
" return 1;\n"
|
|
" else\n"
|
|
" return add(fib(n-1),fib(n-2));\n"
|
|
"}\n"
|
|
"\n"
|
|
"int foo(int n)\n"
|
|
"{\n"
|
|
" printf(\" %d\", fib(n));\n"
|
|
" return 0;\n"
|
|
"# warning is this the correct file:line...\n"
|
|
"}\n";
|
|
|
|
int g_argc; char **g_argv;
|
|
|
|
void parse_args(TCCState *s)
|
|
{
|
|
int i;
|
|
/* if tcclib.h and libtcc1.a are not installed, where can we find them */
|
|
for (i = 1; i < g_argc; ++i) {
|
|
char *a = g_argv[i];
|
|
if (a[0] == '-') {
|
|
if (a[1] == 'B')
|
|
tcc_set_lib_path(s, a+2);
|
|
else if (a[1] == 'I')
|
|
tcc_add_include_path(s, a+2);
|
|
else if (a[1] == 'L')
|
|
tcc_add_library_path(s, a+2);
|
|
}
|
|
}
|
|
}
|
|
|
|
TCCState *new_state(int w)
|
|
{
|
|
TCCState *s = tcc_new();
|
|
if (!s) {
|
|
fprintf(stderr, __FILE__ ": could not create tcc state\n");
|
|
exit(1);
|
|
}
|
|
tcc_set_error_func(s, stdout, handle_error);
|
|
parse_args(s);
|
|
if (!w) tcc_set_options(s, "-w");
|
|
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
|
|
return s;
|
|
}
|
|
|
|
void *reloc_state(TCCState *s, const char *entry)
|
|
{
|
|
void *func;
|
|
tcc_add_symbol(s, "add", add);
|
|
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) {
|
|
fprintf(stderr, __FILE__ ": could not relocate tcc state.\n");
|
|
return NULL;
|
|
}
|
|
func = tcc_get_symbol(s, entry);
|
|
if (!func)
|
|
fprintf(stderr, __FILE__ ": could not get entry symbol.\n");
|
|
return func;
|
|
}
|
|
|
|
/* work with several states at the same time */
|
|
int state_test(void)
|
|
{
|
|
TCCState *s[M];
|
|
int (*func[M])(int);
|
|
int n;
|
|
|
|
for (n = 0; n < M + 4; ++n) {
|
|
unsigned a = n, b = n - 1, c = n - 2, d = n - 3, e = n - 4;
|
|
if (a < M)
|
|
s[a] = new_state(0);
|
|
if (b < M)
|
|
if (tcc_compile_string(s[b], my_program) == -1)
|
|
break;
|
|
if (c < M)
|
|
func[c] = reloc_state(s[c], "foo");
|
|
if (d < M && func[d])
|
|
func[d](F(d));
|
|
if (e < M)
|
|
tcc_delete(s[e]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* simple compilation in threads */
|
|
TF_TYPE(thread_test_simple, vn)
|
|
{
|
|
TCCState *s;
|
|
int (*func)(int);
|
|
int ret;
|
|
int n = (size_t)vn;
|
|
|
|
s = new_state(0);
|
|
sleep_ms(1);
|
|
ret = tcc_compile_string(s, my_program);
|
|
sleep_ms(1);
|
|
if (ret >= 0) {
|
|
func = reloc_state(s, "foo");
|
|
if (func)
|
|
func(F(n));
|
|
}
|
|
tcc_delete(s);
|
|
return 0;
|
|
}
|
|
|
|
/* more complex compilation in threads */
|
|
TF_TYPE(thread_test_complex, vn)
|
|
{
|
|
TCCState *s;
|
|
int ret;
|
|
int n = (size_t)vn;
|
|
char *argv[30], b[10];
|
|
int argc = 0, i;
|
|
|
|
sprintf(b, "%d", F(n));
|
|
|
|
argv[argc++] = "../tcc.c";
|
|
for (i = 1; i < g_argc; ++i) argv[argc++] = g_argv[i];
|
|
#if 0
|
|
argv[argc++] = "-run";
|
|
argv[argc++] = "../tcc.c";
|
|
for (i = 1; i < g_argc; ++i) argv[argc++] = g_argv[i];
|
|
#endif
|
|
argv[argc++] = "-g";
|
|
argv[argc++] = "-DFIB";
|
|
argv[argc++] = "-run";
|
|
argv[argc++] = __FILE__;
|
|
argv[argc++] = b;
|
|
argv[argc] = NULL;
|
|
|
|
s = new_state(1);
|
|
sleep_ms(1);
|
|
ret = tcc_add_file(s, argv[0]);
|
|
sleep_ms(1);
|
|
if (ret >= 0)
|
|
tcc_run(s, argc, argv);
|
|
tcc_delete(s);
|
|
return 0;
|
|
}
|
|
|
|
void time_tcc(int n)
|
|
{
|
|
TCCState *s;
|
|
int ret;
|
|
while (--n >= 0) {
|
|
s = new_state(1);
|
|
ret = tcc_add_file(s, "../tcc.c");
|
|
tcc_delete(s);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static unsigned getclock_ms(void)
|
|
{
|
|
#ifdef _WIN32
|
|
return GetTickCount();
|
|
#else
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return tv.tv_sec*1000 + (tv.tv_usec+500)/1000;
|
|
#endif
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int n;
|
|
unsigned t;
|
|
|
|
g_argc = argc;
|
|
g_argv = argv;
|
|
|
|
#if 1
|
|
printf("----- libtest : mixed calls -------\n"), fflush(stdout);
|
|
t = getclock_ms();
|
|
state_test();
|
|
printf("\n(%u ms)\n", getclock_ms() - t);
|
|
#endif
|
|
#if 1
|
|
printf("----- libtest : threads ------------\n"), fflush(stdout);
|
|
t = getclock_ms();
|
|
for (n = 0; n < M; ++n)
|
|
create_thread(thread_test_simple, n);
|
|
wait_threads(n);
|
|
printf("\n(%u ms)\n", getclock_ms() - t);
|
|
#endif
|
|
#if 1
|
|
printf("----- libtest : tcc in threads -----\n"), fflush(stdout);
|
|
t = getclock_ms();
|
|
for (n = 0; n < M; ++n)
|
|
create_thread(thread_test_complex, n);
|
|
wait_threads(n);
|
|
printf("\n(%u ms)\n", getclock_ms() - t);
|
|
#endif
|
|
#if 1
|
|
printf("----- compilation of tcc -----------\n"), fflush(stdout);
|
|
t = getclock_ms();
|
|
time_tcc(10);
|
|
printf("(%u ms)\n", (getclock_ms() - t) / 10), fflush(stdout);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
#include <tcclib.h>
|
|
int fib(n)
|
|
{
|
|
return (n <= 2) ? 1 : fib(n-1) + fib(n-2);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
printf(" %d", fib(atoi(argv[1]), 2));
|
|
return 0;
|
|
}
|
|
#endif
|