Compare commits
42 Commits
wip-bootst
...
master
Author | SHA1 | Date |
---|---|---|
Jan Nieuwenhuizen | cb08f119ca | |
Christian Jullien | adfcf3b1dd | |
Christian Jullien | 8494f2c318 | |
Petr Skocik | 070646b790 | |
Michael Matz | c4787e3626 | |
Petr Skocik | f7779efe58 | |
Petr Skocik | 49dfb5755a | |
Petr Skocik | 3058d4116e | |
Petr Skocik | 9d44b02a49 | |
Petr Skocik | e0012c2767 | |
Petr Skocik | c81116e29a | |
Petr Skocik | f85b1e393f | |
Petr Skocik | 1e2e5671f7 | |
Petr Skocik | 314843ffc3 | |
Petr Skocik | 73ca09ff32 | |
Petr Skocik | 93a4ddfa63 | |
Michael Matz | 3b9c3fd186 | |
Michael Matz | 61ba9f2299 | |
Michael Matz | 22420ee1ee | |
Jonathan Newman | 0edbed1d52 | |
Michael Matz | d79caa9ff6 | |
Michael Matz | 65c7f19deb | |
Andrey Gursky | 91bdb5a4a3 | |
grischka | 8f6fcb709a | |
grischka | 2b155a8c16 | |
grischka | ace1225492 | |
Michael Matz | 671dcace82 | |
Petr Skocik | ef668aae1e | |
Petr Skocik | d6d3cf00ec | |
Michael Matz | f0a25ca263 | |
Thomas Preud'homme | c41caac02d | |
Thomas Preud'homme | e76058c478 | |
Thomas Preud'homme | 776aa0c093 | |
Michael Matz | 3e6515b64f | |
Michael Matz | 7ad2cf8d68 | |
Michael Matz | 8294285d8f | |
foobar | 988e2ff7fe | |
Michael Matz | 414b224efa | |
Michael Matz | 9e47b18229 | |
Michael Matz | 3b27b3b1d1 | |
Michael Matz | 1b1b270f1e | |
Michael Matz | 3f8225509b |
5
Makefile
5
Makefile
|
@ -349,6 +349,9 @@ test:
|
|||
tests2.%:
|
||||
$(MAKE) -C tests/tests2 $@
|
||||
|
||||
testspp.%:
|
||||
$(MAKE) -C tests/pp $@
|
||||
|
||||
clean:
|
||||
rm -f tcc$(EXESUF) tcc_p$(EXESUF) *-tcc$(EXESUF) tcc.pod
|
||||
rm -f *~ *.o *.a *.so* *.out *.log lib*.def *.exe *.dll a.out tags TAGS
|
||||
|
@ -394,6 +397,8 @@ help:
|
|||
@echo ""
|
||||
@echo "make tests2.all / make tests2.37 / make tests2.37+"
|
||||
@echo " run all/single test(s) from tests2, optionally update .expect"
|
||||
@echo "make testspp.all / make testspp.17"
|
||||
@echo " run all/single test(s) from tests/pp"
|
||||
@echo ""
|
||||
@echo "Other supported make targets:"
|
||||
@echo " install install-strip tags ETAGS tar clean distclean help"
|
||||
|
|
67
README
67
README
|
@ -1,3 +1,70 @@
|
|||
Bootstrappable TCC/TinyCC -- Tiny C Compiler's bootstrappable fork
|
||||
------------------------------------------------------------------
|
||||
|
||||
Bootstrappable TCC is a fork from mainline TCC development, that
|
||||
started spring 2017 from
|
||||
|
||||
commit 307b7b183d4ee56e74090b0e525d6a587840e31f
|
||||
Author: Aron BARATH <baratharon@caesar.elte.hu>
|
||||
Date: Tue May 16 07:03:26 2017 +0200
|
||||
|
||||
the R_X86_64_GOTOFF64 relocation was missing
|
||||
|
||||
and can be compiled by MesCC (https://gnu.org/s/mes).
|
||||
|
||||
Initially the plan was to make TinyCC itself "bootstrappable"
|
||||
(https://bootstrappable.org).
|
||||
|
||||
The best way to do so would be to gradually simplify the
|
||||
implementation of TinyCC by restricting the use of language constructs
|
||||
to a well-defined subset of C. In bootstrapping each stage or
|
||||
compiler adds functionality; a compiler that is written in itself --a
|
||||
so-called `self-hosting' compiler--is not considered to be
|
||||
bootstrappable.
|
||||
|
||||
At the time this vision was not received with much enthousiasm
|
||||
|
||||
https://lists.nongnu.org/archive/html/tinycc-devel/2017-09/msg00019.html
|
||||
|
||||
so I decided to fork TinyCC and instead grow MesCC (a bootstrappable
|
||||
sub-C compiler in a subset of Guile Scheme) into a full C99 compiler.
|
||||
|
||||
Currently, the Reduced Binary Seed Bootstrap of the GNU Guix System
|
||||
(https://guix.gnu.org/blog/2019/guix-reduces-bootstrap-seed-by-50/)
|
||||
uses bootstrappable-tinycc.
|
||||
|
||||
The fork consists of about 12 patches
|
||||
|
||||
cee58e0 build: Support building from bootstrap-mes.
|
||||
39de356 bootstrappable: Force static link.
|
||||
2b6271d bootstrappable: Work around MesCC bug.
|
||||
379c62d bootstrappable: add tcc.h include guards to include location.
|
||||
274bd06 bootstrappable: Handle libtcc1.a.
|
||||
6ae9aa4 bootstrappable: Skip tidy_section_headers.
|
||||
a130ce1 bootstrappable: HAVE_FLOAT.
|
||||
de906df bootstrappable: HAVE_BITFIELD.
|
||||
540ba0b bootstrappable: HAVE_LONG_LONG.
|
||||
306f677 bootstrappable: Work around Nyacc-0.80.42 bug.
|
||||
9c97705 build: bootstrap build scripts.
|
||||
584478f bootstrappable: Remove non-free grep test.
|
||||
|
||||
that work around bugs and missing C language features in MesCC. Only
|
||||
three of these are really interesting: the HAVE_* patches that allow
|
||||
for stepwise introduction of bitfields, doubles/floats and long longs.
|
||||
|
||||
In time, I hope we can remove the need for this fork; either by
|
||||
upstreaming some bootstrappable work or else by maturing MesCC.
|
||||
|
||||
At the time of writing, mainline (non-bootstrappable) tinycc lives
|
||||
here
|
||||
|
||||
https://repo.or.cz/tinycc.git
|
||||
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
|
||||
|
||||
--
|
||||
janneke
|
||||
|
||||
|
||||
Tiny C Compiler - C Scripting Everywhere - The Smallest ANSI C compiler
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
|
|
10
arm-gen.c
10
arm-gen.c
|
@ -1410,6 +1410,16 @@ void gfunc_epilog(void)
|
|||
}
|
||||
}
|
||||
|
||||
ST_FUNC void gen_fill_nops(int bytes)
|
||||
{
|
||||
if ((bytes & 3))
|
||||
tcc_error("alignment of code section not multiple of 4");
|
||||
while (bytes > 0) {
|
||||
o(0xE1A00000);
|
||||
bytes -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate a jump to a label */
|
||||
int gjmp(int t)
|
||||
{
|
||||
|
|
14
arm64-gen.c
14
arm64-gen.c
|
@ -580,8 +580,8 @@ ST_FUNC void store(int r, SValue *sv)
|
|||
|
||||
static void arm64_gen_bl_or_b(int b)
|
||||
{
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||||
assert(!b && (vtop->r & VT_SYM));
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) {
|
||||
assert(!b);
|
||||
greloca(cur_text_section, vtop->sym, ind, R_AARCH64_CALL26, 0);
|
||||
o(0x94000000); // bl .
|
||||
}
|
||||
|
@ -1276,6 +1276,16 @@ ST_FUNC void gfunc_epilog(void)
|
|||
o(0xd65f03c0); // ret
|
||||
}
|
||||
|
||||
ST_FUNC void gen_fill_nops(int bytes)
|
||||
{
|
||||
if ((bytes & 3))
|
||||
tcc_error("alignment of code section not multiple of 4");
|
||||
while (bytes > 0) {
|
||||
o(0xd503201f); // nop
|
||||
bytes -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate forward branch to label:
|
||||
ST_FUNC int gjmp(int t)
|
||||
{
|
||||
|
|
10
c67-gen.c
10
c67-gen.c
|
@ -2035,6 +2035,16 @@ void gfunc_epilog(void)
|
|||
}
|
||||
}
|
||||
|
||||
ST_FUNC void gen_fill_nops(int bytes)
|
||||
{
|
||||
if ((bytes & 3))
|
||||
tcc_error("alignment of code section not multiple of 4");
|
||||
while (bytes > 0) {
|
||||
C67_NOP(4);
|
||||
bytes -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate a jump to a label */
|
||||
int gjmp(int t)
|
||||
{
|
||||
|
|
|
@ -335,7 +335,7 @@ if test -z "$cross_prefix" ; then
|
|||
|
||||
if test "$cpu" = "arm" ; then
|
||||
if test "${triplet%eabihf}" != "$triplet" ; then
|
||||
confvars="$confvars arm_eabihf"
|
||||
confvars="$confvars arm_eabihf arm_vfp"
|
||||
elif test "${triplet%eabi}" != "$triplet" ; then
|
||||
confvars="$confvars arm_eabi"
|
||||
fi
|
||||
|
|
|
@ -157,6 +157,12 @@ static int oad(int c, int s)
|
|||
return t;
|
||||
}
|
||||
|
||||
ST_FUNC void gen_fill_nops(int bytes)
|
||||
{
|
||||
while (bytes--)
|
||||
g(0x90);
|
||||
}
|
||||
|
||||
/* generate jmp to a label */
|
||||
#define gjmp2(instr,lbl) oad(instr,lbl)
|
||||
|
||||
|
|
49
libtcc.c
49
libtcc.c
|
@ -621,14 +621,13 @@ ST_FUNC int tcc_open(TCCState *s1, const char *filename)
|
|||
}
|
||||
|
||||
/* compile the file opened in 'file'. Return non zero if errors. */
|
||||
static int tcc_compile(TCCState *s1)
|
||||
static int tcc_compile(TCCState *s1, int filetype)
|
||||
{
|
||||
Sym *define_start;
|
||||
int filetype, is_asm;
|
||||
int is_asm;
|
||||
|
||||
define_start = define_stack;
|
||||
filetype = s1->filetype;
|
||||
is_asm = filetype == AFF_TYPE_ASM || filetype == AFF_TYPE_ASMPP;
|
||||
is_asm = !!(filetype & (AFF_TYPE_ASM|AFF_TYPE_ASMPP));
|
||||
tccelf_begin_file(s1);
|
||||
|
||||
if (setjmp(s1->error_jmp_buf) == 0) {
|
||||
|
@ -640,7 +639,7 @@ static int tcc_compile(TCCState *s1)
|
|||
tcc_preprocess(s1);
|
||||
} else if (is_asm) {
|
||||
#ifdef CONFIG_TCC_ASM
|
||||
tcc_assemble(s1, filetype == AFF_TYPE_ASMPP);
|
||||
tcc_assemble(s1, !!(filetype & AFF_TYPE_ASMPP));
|
||||
#else
|
||||
tcc_error_noabort("asm not supported");
|
||||
#endif
|
||||
|
@ -667,7 +666,7 @@ LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
|
|||
len = strlen(str);
|
||||
tcc_open_bf(s, "<string>", len);
|
||||
memcpy(file->buffer, str, len);
|
||||
ret = tcc_compile(s);
|
||||
ret = tcc_compile(s, s->filetype);
|
||||
tcc_close();
|
||||
return ret;
|
||||
}
|
||||
|
@ -733,7 +732,6 @@ LIBTCCAPI TCCState *tcc_new(void)
|
|||
tcc_state = s;
|
||||
++nb_states;
|
||||
|
||||
s->alacarte_link = 1;
|
||||
s->nocommon = 1;
|
||||
s->warn_implicit_function_declaration = 1;
|
||||
s->ms_extensions = 1;
|
||||
|
@ -1042,7 +1040,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
|||
break;
|
||||
#endif
|
||||
case AFF_BINTYPE_AR:
|
||||
ret = tcc_load_archive(s1, fd);
|
||||
ret = tcc_load_archive(s1, fd, !(flags & AFF_WHOLE_ARCHIVE));
|
||||
break;
|
||||
#ifdef TCC_TARGET_COFF
|
||||
case AFF_BINTYPE_C67:
|
||||
|
@ -1061,7 +1059,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
ret = tcc_compile(s1);
|
||||
ret = tcc_compile(s1, flags);
|
||||
}
|
||||
tcc_close();
|
||||
return ret;
|
||||
|
@ -1070,8 +1068,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
|||
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
|
||||
{
|
||||
int filetype = s->filetype;
|
||||
int flags = AFF_PRINT_ERROR;
|
||||
if (filetype == 0) {
|
||||
if (0 == (filetype & AFF_TYPE_MASK)) {
|
||||
/* use a file extension to detect a filetype */
|
||||
const char *ext = tcc_fileextension(filename);
|
||||
if (ext[0]) {
|
||||
|
@ -1083,13 +1080,12 @@ LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
|
|||
else if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i"))
|
||||
filetype = AFF_TYPE_C;
|
||||
else
|
||||
flags |= AFF_TYPE_BIN;
|
||||
filetype |= AFF_TYPE_BIN;
|
||||
} else {
|
||||
filetype = AFF_TYPE_C;
|
||||
}
|
||||
s->filetype = filetype;
|
||||
}
|
||||
return tcc_add_file_internal(s, filename, flags);
|
||||
return tcc_add_file_internal(s, filename, filetype | AFF_PRINT_ERROR);
|
||||
}
|
||||
|
||||
LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
|
||||
|
@ -1141,9 +1137,10 @@ LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
|
|||
const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
|
||||
const char **pp = s->static_link ? libs + 1 : libs;
|
||||
#endif
|
||||
int flags = s->filetype & AFF_WHOLE_ARCHIVE;
|
||||
while (*pp) {
|
||||
if (0 == tcc_add_library_internal(s, *pp,
|
||||
libraryname, 0, s->library_paths, s->nb_library_paths))
|
||||
libraryname, flags, s->library_paths, s->nb_library_paths))
|
||||
return 0;
|
||||
++pp;
|
||||
}
|
||||
|
@ -1368,6 +1365,8 @@ static int tcc_set_linker(TCCState *s, const char *option)
|
|||
ignoring = 1;
|
||||
} else if (link_option(option, "export-all-symbols", &p)) {
|
||||
s->rdynamic = 1;
|
||||
} else if (link_option(option, "export-dynamic", &p)) {
|
||||
s->rdynamic = 1;
|
||||
} else if (link_option(option, "rpath=", &p)) {
|
||||
copy_linker_arg(&s->rpath, p, ':');
|
||||
} else if (link_option(option, "enable-new-dtags", &p)) {
|
||||
|
@ -1409,7 +1408,10 @@ static int tcc_set_linker(TCCState *s, const char *option)
|
|||
goto err;
|
||||
#endif
|
||||
} else if (ret = link_option(option, "?whole-archive", &p), ret) {
|
||||
s->alacarte_link = ret < 0;
|
||||
if (ret > 0)
|
||||
s->filetype |= AFF_WHOLE_ARCHIVE;
|
||||
else
|
||||
s->filetype &= ~AFF_WHOLE_ARCHIVE;
|
||||
} else if (p) {
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -1500,7 +1502,7 @@ static const TCCOption tcc_options[] = {
|
|||
{ "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||
{ "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
|
||||
{ "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
|
||||
{ "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||
{ "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG },
|
||||
{ "bench", TCC_OPTION_bench, 0 },
|
||||
#ifdef CONFIG_TCC_BACKTRACE
|
||||
{ "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
|
||||
|
@ -1595,7 +1597,6 @@ static void args_parser_add_file(TCCState *s, const char* filename, int filetype
|
|||
{
|
||||
struct filespec *f = tcc_malloc(sizeof *f + strlen(filename));
|
||||
f->type = filetype;
|
||||
f->alacarte = s->alacarte_link;
|
||||
strcpy(f->name, filename);
|
||||
dynarray_add(&s->files, &s->nb_files, f);
|
||||
}
|
||||
|
@ -1742,7 +1743,7 @@ reparse:
|
|||
tcc_set_lib_path(s, optarg);
|
||||
break;
|
||||
case TCC_OPTION_l:
|
||||
args_parser_add_file(s, optarg, AFF_TYPE_LIB);
|
||||
args_parser_add_file(s, optarg, AFF_TYPE_LIB | (s->filetype & ~AFF_TYPE_MASK));
|
||||
s->nb_libraries++;
|
||||
break;
|
||||
case TCC_OPTION_pthread:
|
||||
|
@ -1896,14 +1897,18 @@ reparse:
|
|||
exit(0);
|
||||
break;
|
||||
case TCC_OPTION_x:
|
||||
x = 0;
|
||||
if (*optarg == 'c')
|
||||
s->filetype = AFF_TYPE_C;
|
||||
x = AFF_TYPE_C;
|
||||
else if (*optarg == 'a')
|
||||
s->filetype = AFF_TYPE_ASMPP;
|
||||
x = AFF_TYPE_ASMPP;
|
||||
else if (*optarg == 'b')
|
||||
x = AFF_TYPE_BIN;
|
||||
else if (*optarg == 'n')
|
||||
s->filetype = AFF_TYPE_NONE;
|
||||
x = AFF_TYPE_NONE;
|
||||
else
|
||||
tcc_warning("unsupported language '%s'", optarg);
|
||||
s->filetype = x | (s->filetype & ~AFF_TYPE_MASK);
|
||||
break;
|
||||
case TCC_OPTION_O:
|
||||
last_o = atoi(optarg);
|
||||
|
|
|
@ -322,6 +322,11 @@ Binary image (only for executable output)
|
|||
COFF output format (only for executable output for TMS320C67xx target)
|
||||
@end table
|
||||
|
||||
@item -Wl,--export-all-symbols
|
||||
@item -Wl,--export-dynamic
|
||||
Export global symbols to the dynamic linker. It is useful when a library
|
||||
opened with @code{dlopen()} needs to access executable symbols.
|
||||
|
||||
@item -Wl,-subsystem=console/gui/wince/...
|
||||
Set type for PE (Windows) executables.
|
||||
|
||||
|
@ -543,6 +548,7 @@ instead of
|
|||
@cindex stdcall attribute
|
||||
@cindex regparm attribute
|
||||
@cindex dllexport attribute
|
||||
@cindex nodecorate attribute
|
||||
|
||||
@item The keyword @code{__attribute__} is handled to specify variable or
|
||||
function attributes. The following attributes are supported:
|
||||
|
@ -570,6 +576,8 @@ registers @code{%eax}, @code{%edx} and @code{%ecx}.
|
|||
|
||||
@item @code{dllexport}: export function from dll/executable (win32 only)
|
||||
|
||||
@item @code{nodecorate}: do not apply any decorations that would otherwise be applied when exporting function from dll/executable (win32 only)
|
||||
|
||||
@end itemize
|
||||
|
||||
Here are some examples:
|
||||
|
|
18
tcc.c
18
tcc.c
|
@ -62,7 +62,7 @@ static const char help[] =
|
|||
" -bt N show N callers in stack traces\n"
|
||||
#endif
|
||||
"Misc. options:\n"
|
||||
" -x[c|a|n] specify type of the next infile\n"
|
||||
" -x[c|a|b|n] specify type of the next infile (C,ASM,BIN,NONE)\n"
|
||||
" -nostdinc do not use standard system include paths\n"
|
||||
" -nostdlib do not link with standard crt and libraries\n"
|
||||
" -Bdir set tcc's private include/library dir\n"
|
||||
|
@ -117,6 +117,7 @@ static const char help2[] =
|
|||
" -nostdlib do not link with standard crt/libs\n"
|
||||
" -[no-]whole-archive load lib(s) fully/only as needed\n"
|
||||
" -export-all-symbols same as -rdynamic\n"
|
||||
" -export-dynamic same as -rdynamic\n"
|
||||
" -image-base= -Ttext= set base address of executable\n"
|
||||
" -section-alignment= set section alignment in executable\n"
|
||||
#ifdef TCC_TARGET_PE
|
||||
|
@ -182,8 +183,10 @@ static void print_search_dirs(TCCState *s)
|
|||
/* print_dirs("programs", NULL, 0); */
|
||||
print_dirs("include", s->sysinclude_paths, s->nb_sysinclude_paths);
|
||||
print_dirs("libraries", s->library_paths, s->nb_library_paths);
|
||||
#ifdef TCC_TARGET_PE
|
||||
printf("libtcc1:\n %s/lib/"TCC_LIBTCC1"\n", s->tcc_lib_path);
|
||||
#else
|
||||
printf("libtcc1:\n %s/"TCC_LIBTCC1"\n", s->tcc_lib_path);
|
||||
#ifndef TCC_TARGET_PE
|
||||
print_dirs("crt", s->crt_paths, s->nb_crt_paths);
|
||||
printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s));
|
||||
#endif
|
||||
|
@ -287,7 +290,7 @@ redo:
|
|||
tcc_error("no input files\n");
|
||||
|
||||
if (s->output_type == TCC_OUTPUT_PREPROCESS) {
|
||||
if (s->outfile) {
|
||||
if (s->outfile && 0!=strcmp("-",s->outfile)) {
|
||||
ppfp = fopen(s->outfile, "w");
|
||||
if (!ppfp)
|
||||
tcc_error("could not write '%s'", s->outfile);
|
||||
|
@ -298,8 +301,10 @@ redo:
|
|||
if (n > 1 && s->outfile)
|
||||
tcc_error("cannot specify output file with -c many files");
|
||||
} else {
|
||||
if (s->option_pthread)
|
||||
if (s->option_pthread) {
|
||||
tcc_set_options(s, "-lpthread");
|
||||
n = s->nb_files;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->do_bench)
|
||||
|
@ -320,8 +325,7 @@ redo:
|
|||
for (first_file = NULL, ret = 0;;) {
|
||||
struct filespec *f = s->files[s->nb_files - n];
|
||||
s->filetype = f->type;
|
||||
s->alacarte_link = f->alacarte;
|
||||
if (f->type == AFF_TYPE_LIB) {
|
||||
if (f->type & AFF_TYPE_LIB) {
|
||||
if (tcc_add_library_err(s, f->name) < 0)
|
||||
ret = 1;
|
||||
} else {
|
||||
|
@ -332,8 +336,6 @@ redo:
|
|||
if (tcc_add_file(s, f->name) < 0)
|
||||
ret = 1;
|
||||
}
|
||||
s->filetype = 0;
|
||||
s->alacarte_link = 1;
|
||||
if (--n == 0 || ret
|
||||
|| (s->output_type == TCC_OUTPUT_OBJ && !s->option_r))
|
||||
break;
|
||||
|
|
17
tcc.h
17
tcc.h
|
@ -439,8 +439,9 @@ struct SymAttr {
|
|||
weak : 1,
|
||||
visibility : 2,
|
||||
dllexport : 1,
|
||||
nodecorate : 1,
|
||||
dllimport : 1,
|
||||
unused : 5;
|
||||
unused : 4;
|
||||
};
|
||||
|
||||
/* function attributes or temporary attributes for parsing */
|
||||
|
@ -638,7 +639,6 @@ struct sym_attr {
|
|||
};
|
||||
|
||||
struct TCCState {
|
||||
|
||||
int verbose; /* if true, display some information during compilation */
|
||||
int nostdinc; /* if true, no standard headers are added */
|
||||
int nostdlib; /* if true, no standard libraries are added */
|
||||
|
@ -646,7 +646,7 @@ struct TCCState {
|
|||
int static_link; /* if true, static linking is performed */
|
||||
int rdynamic; /* if true, all symbols are exported */
|
||||
int symbolic; /* if true, resolve symbols in the current module first */
|
||||
int alacarte_link; /* if true, only link in referenced objects from archive */
|
||||
int filetype; /* file type for compilation (NONE,C,ASM) */
|
||||
|
||||
char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */
|
||||
char *soname; /* as specified on the command line (-soname) */
|
||||
|
@ -811,7 +811,6 @@ struct TCCState {
|
|||
struct filespec **files; /* files seen on command line */
|
||||
int nb_files; /* number thereof */
|
||||
int nb_libraries; /* number of libs thereof */
|
||||
int filetype;
|
||||
char *outfile; /* output filename */
|
||||
int option_r; /* option -r */
|
||||
int do_bench; /* option -bench */
|
||||
|
@ -824,7 +823,6 @@ struct TCCState {
|
|||
|
||||
struct filespec {
|
||||
char type;
|
||||
char alacarte;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
|
@ -1150,12 +1148,14 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
|||
#define AFF_PRINT_ERROR 0x10 /* print error if file not found */
|
||||
#define AFF_REFERENCED_DLL 0x20 /* load a referenced dll from another dll */
|
||||
#define AFF_TYPE_BIN 0x40 /* file to add is binary */
|
||||
#define AFF_WHOLE_ARCHIVE 0x80 /* load all objects from archive */
|
||||
/* s->filetype: */
|
||||
#define AFF_TYPE_NONE 0
|
||||
#define AFF_TYPE_C 1
|
||||
#define AFF_TYPE_ASM 2
|
||||
#define AFF_TYPE_ASMPP 3
|
||||
#define AFF_TYPE_LIB 4
|
||||
#define AFF_TYPE_ASMPP 4
|
||||
#define AFF_TYPE_LIB 8
|
||||
#define AFF_TYPE_MASK (15 | AFF_TYPE_BIN)
|
||||
/* values from tcc_object_type(...) */
|
||||
#define AFF_BINTYPE_REL 1
|
||||
#define AFF_BINTYPE_DYN 2
|
||||
|
@ -1423,7 +1423,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s);
|
|||
|
||||
ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
|
||||
ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
|
||||
ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
|
||||
ST_FUNC int tcc_load_archive(TCCState *s1, int fd, int alacarte);
|
||||
ST_FUNC void tcc_add_bcheck(TCCState *s1);
|
||||
ST_FUNC void tcc_add_runtime(TCCState *s1);
|
||||
|
||||
|
@ -1475,6 +1475,7 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *reg
|
|||
ST_FUNC void gfunc_call(int nb_args);
|
||||
ST_FUNC void gfunc_prolog(CType *func_type);
|
||||
ST_FUNC void gfunc_epilog(void);
|
||||
ST_FUNC void gen_fill_nops(int);
|
||||
ST_FUNC int gjmp(int t);
|
||||
ST_FUNC void gjmp_addr(int a);
|
||||
ST_FUNC int gtst(int inv, int t);
|
||||
|
|
37
tccelf.c
37
tccelf.c
|
@ -866,13 +866,16 @@ static void relocate_rel(TCCState *s1, Section *sr)
|
|||
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
|
||||
{
|
||||
ElfW_Rel *rel;
|
||||
int sym_index, type, count;
|
||||
int type, count;
|
||||
|
||||
count = 0;
|
||||
for_each_elem(sr, 0, rel, ElfW_Rel) {
|
||||
sym_index = ELFW(R_SYM)(rel->r_info);
|
||||
type = ELFW(R_TYPE)(rel->r_info);
|
||||
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
|
||||
int sym_index = ELFW(R_SYM)(rel->r_info);
|
||||
#endif
|
||||
switch(type) {
|
||||
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
|
||||
#if defined(TCC_TARGET_I386)
|
||||
case R_386_32:
|
||||
if (!get_sym_attr(s1, sym_index, 0)->dyn_index
|
||||
|
@ -896,6 +899,7 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
|
|||
if (get_sym_attr(s1, sym_index, 0)->dyn_index)
|
||||
count++;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1144,7 +1148,6 @@ static void add_init_array_defines(TCCState *s1, const char *section_name)
|
|||
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
|
||||
s->sh_num, sym_end);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tcc_add_support(TCCState *s1, const char *filename)
|
||||
{
|
||||
|
@ -1152,6 +1155,7 @@ static int tcc_add_support(TCCState *s1, const char *filename)
|
|||
snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, filename);
|
||||
return tcc_add_file(s1, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
ST_FUNC void tcc_add_bcheck(TCCState *s1)
|
||||
{
|
||||
|
@ -1187,8 +1191,10 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1)
|
|||
/* add tcc runtime libraries */
|
||||
ST_FUNC void tcc_add_runtime(TCCState *s1)
|
||||
{
|
||||
s1->filetype = 0;
|
||||
tcc_add_bcheck(s1);
|
||||
tcc_add_pragma_libs(s1);
|
||||
#ifndef TCC_TARGET_PE
|
||||
/* add libc */
|
||||
if (!s1->nostdlib) {
|
||||
tcc_add_library_err(s1, "c");
|
||||
|
@ -1205,6 +1211,7 @@ ST_FUNC void tcc_add_runtime(TCCState *s1)
|
|||
if (s1->output_type != TCC_OUTPUT_MEMORY)
|
||||
tcc_add_crt(s1, "crtn.o");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* add various standard linker symbols (must be done after the
|
||||
|
@ -1359,6 +1366,8 @@ ST_FUNC void fill_got(TCCState *s1)
|
|||
static void fill_local_got_entries(TCCState *s1)
|
||||
{
|
||||
ElfW_Rel *rel;
|
||||
if (!s1->got->reloc)
|
||||
return;
|
||||
for_each_elem(s1->got->reloc, 0, rel, ElfW_Rel) {
|
||||
if (ELFW(R_TYPE)(rel->r_info) == R_RELATIVE) {
|
||||
int sym_index = ELFW(R_SYM) (rel->r_info);
|
||||
|
@ -1531,7 +1540,7 @@ static int alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
|
|||
prepare_dynamic_rel(s1, s)) {
|
||||
if (s1->sections[s->sh_info]->sh_flags & SHF_EXECINSTR)
|
||||
textrel = 1;
|
||||
} else if (s1->do_debug ||
|
||||
} else if ((s1->do_debug && s->sh_type != SHT_RELX) ||
|
||||
file_type == TCC_OUTPUT_OBJ ||
|
||||
(s->sh_flags & SHF_ALLOC) ||
|
||||
i == (s1->nb_sections - 1)) {
|
||||
|
@ -1832,14 +1841,7 @@ static int final_sections_reloc(TCCState *s1)
|
|||
/* XXX: ignore sections with allocated relocations ? */
|
||||
for(i = 1; i < s1->nb_sections; i++) {
|
||||
s = s1->sections[i];
|
||||
#if defined(TCC_TARGET_I386) || defined(TCC_MUSL)
|
||||
if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
|
||||
/* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC
|
||||
checking is removed */
|
||||
#else
|
||||
if (s->reloc && s != s1->got)
|
||||
/* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */
|
||||
#endif
|
||||
relocate_section(s1, s);
|
||||
}
|
||||
|
||||
|
@ -2445,8 +2447,11 @@ ST_FUNC int tcc_load_object_file(TCCState *s1,
|
|||
a = (Stab_Sym *)(s->data + sm_table[stab_index].offset);
|
||||
b = (Stab_Sym *)(s->data + s->data_offset);
|
||||
o = sm_table[stabstr_index].offset;
|
||||
while (a < b)
|
||||
a->n_strx += o, a++;
|
||||
while (a < b) {
|
||||
if (a->n_strx)
|
||||
a->n_strx += o;
|
||||
a++;
|
||||
}
|
||||
}
|
||||
|
||||
/* second short pass to update sh_link and sh_info fields of new
|
||||
|
@ -2633,7 +2638,7 @@ static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize)
|
|||
}
|
||||
|
||||
/* load a '.a' file */
|
||||
ST_FUNC int tcc_load_archive(TCCState *s1, int fd)
|
||||
ST_FUNC int tcc_load_archive(TCCState *s1, int fd, int alacarte)
|
||||
{
|
||||
ArchiveHeader hdr;
|
||||
char ar_size[11];
|
||||
|
@ -2667,10 +2672,10 @@ ST_FUNC int tcc_load_archive(TCCState *s1, int fd)
|
|||
size = (size + 1) & ~1;
|
||||
if (!strcmp(ar_name, "/")) {
|
||||
/* coff symbol table : we handle it */
|
||||
if(s1->alacarte_link)
|
||||
if (alacarte)
|
||||
return tcc_load_alacarte(s1, fd, size, 4);
|
||||
} else if (!strcmp(ar_name, "/SYM64/")) {
|
||||
if(s1->alacarte_link)
|
||||
if (alacarte)
|
||||
return tcc_load_alacarte(s1, fd, size, 8);
|
||||
} else {
|
||||
ElfW(Ehdr) ehdr;
|
||||
|
|
294
tccgen.c
294
tccgen.c
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
|
||||
#include "tcc.h"
|
||||
|
||||
/********************************************************/
|
||||
/* global variables */
|
||||
|
||||
|
@ -392,6 +391,9 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num,
|
|||
#ifdef TCC_TARGET_PE
|
||||
if (sym_type == STT_FUNC && sym->type.ref) {
|
||||
Sym *ref = sym->type.ref;
|
||||
if (ref->a.nodecorate) {
|
||||
can_add_underscore = 0;
|
||||
}
|
||||
if (ref->f.func_call == FUNC_STDCALL && can_add_underscore) {
|
||||
sprintf(buf1, "_%s@%d", name, ref->f.func_args * PTR_SIZE);
|
||||
name = buf1;
|
||||
|
@ -2049,9 +2051,10 @@ static void gen_opif(int op)
|
|||
case '*': f1 *= f2; break;
|
||||
case '/':
|
||||
if (f2 == 0.0) {
|
||||
if (const_wanted)
|
||||
tcc_error("division by zero in constant");
|
||||
goto general_case;
|
||||
/* If not in initializer we need to potentially generate
|
||||
FP exceptions at runtime, otherwise we want to fold. */
|
||||
if (!const_wanted)
|
||||
goto general_case;
|
||||
}
|
||||
f1 /= f2;
|
||||
break;
|
||||
|
@ -2093,9 +2096,11 @@ static inline int is_null_pointer(SValue *p)
|
|||
return ((p->type.t & VT_BTYPE) == VT_INT && (uint32_t)p->c.i == 0) ||
|
||||
((p->type.t & VT_BTYPE) == VT_LLONG && p->c.i == 0) ||
|
||||
((p->type.t & VT_BTYPE) == VT_PTR &&
|
||||
(PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0));
|
||||
(PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0) &&
|
||||
((pointed_type(&p->type)->t & VT_BTYPE) == VT_VOID) &&
|
||||
0 == (pointed_type(&p->type)->t & (VT_CONSTANT | VT_VOLATILE))
|
||||
);
|
||||
}
|
||||
|
||||
static inline int is_integer_btype(int bt)
|
||||
{
|
||||
return (bt == VT_BYTE || bt == VT_SHORT ||
|
||||
|
@ -2632,18 +2637,22 @@ static void gen_cast(CType *type)
|
|||
tcc_warning("nonportable conversion from pointer to char/short");
|
||||
}
|
||||
force_charshort_cast(dbt);
|
||||
#if PTR_SIZE == 4
|
||||
} else if ((dbt & VT_BTYPE) == VT_INT) {
|
||||
/* scalar to int */
|
||||
if ((sbt & VT_BTYPE) == VT_LLONG) {
|
||||
#if PTR_SIZE == 4
|
||||
/* from long long: just take low order word */
|
||||
lexpand();
|
||||
vpop();
|
||||
}
|
||||
#else
|
||||
vpushi(0xffffffff);
|
||||
vtop->type.t |= VT_UNSIGNED;
|
||||
gen_op('&');
|
||||
#endif
|
||||
}
|
||||
/* if lvalue and single word type, nothing to do because
|
||||
the lvalue already contains the real type size (see
|
||||
VT_LVAL_xxx constants) */
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) {
|
||||
|
@ -2653,6 +2662,7 @@ static void gen_cast(CType *type)
|
|||
| (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE);
|
||||
}
|
||||
vtop->type = *type;
|
||||
vtop->type.t &= ~ ( VT_CONSTANT | VT_VOLATILE | VT_ARRAY );
|
||||
}
|
||||
|
||||
/* return type size as known at compile time. Put alignment at 'a' */
|
||||
|
@ -2791,8 +2801,6 @@ static int is_compatible_func(CType *type1, CType *type2)
|
|||
|
||||
/* return true if type1 and type2 are the same. If unqualified is
|
||||
true, qualifiers on the types are ignored.
|
||||
|
||||
- enums are not checked as gcc __builtin_types_compatible_p ()
|
||||
*/
|
||||
static int compare_types(CType *type1, CType *type2, int unqualified)
|
||||
{
|
||||
|
@ -2815,15 +2823,20 @@ static int compare_types(CType *type1, CType *type2, int unqualified)
|
|||
if (t1 != t2)
|
||||
return 0;
|
||||
/* test more complicated cases */
|
||||
bt1 = t1 & VT_BTYPE;
|
||||
bt1 = t1 & (VT_BTYPE | VT_ARRAY);
|
||||
if (bt1 == VT_PTR) {
|
||||
type1 = pointed_type(type1);
|
||||
type2 = pointed_type(type2);
|
||||
return is_compatible_types(type1, type2);
|
||||
} else if (bt1 & VT_ARRAY) {
|
||||
return type1->ref->c < 0 || type2->ref->c < 0
|
||||
|| type1->ref->c == type2->ref->c;
|
||||
} else if (bt1 == VT_STRUCT) {
|
||||
return (type1->ref == type2->ref);
|
||||
} else if (bt1 == VT_FUNC) {
|
||||
return is_compatible_func(type1, type2);
|
||||
} else if (IS_ENUM(type1->t) || IS_ENUM(type2->t)) {
|
||||
return type1->ref == type2->ref;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
@ -2933,22 +2946,34 @@ static void type_to_str(char *buf, int buf_size,
|
|||
break;
|
||||
case VT_FUNC:
|
||||
s = type->ref;
|
||||
type_to_str(buf, buf_size, &s->type, varstr);
|
||||
pstrcat(buf, buf_size, "(");
|
||||
buf1[0]=0;
|
||||
if (varstr && '*' == *varstr) {
|
||||
pstrcat(buf1, sizeof(buf1), "(");
|
||||
pstrcat(buf1, sizeof(buf1), varstr);
|
||||
pstrcat(buf1, sizeof(buf1), ")");
|
||||
}
|
||||
pstrcat(buf1, buf_size, "(");
|
||||
sa = s->next;
|
||||
while (sa != NULL) {
|
||||
type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
|
||||
pstrcat(buf, buf_size, buf1);
|
||||
char buf2[256];
|
||||
type_to_str(buf2, sizeof(buf2), &sa->type, NULL);
|
||||
pstrcat(buf1, sizeof(buf1), buf2);
|
||||
sa = sa->next;
|
||||
if (sa)
|
||||
pstrcat(buf, buf_size, ", ");
|
||||
pstrcat(buf1, sizeof(buf1), ", ");
|
||||
}
|
||||
pstrcat(buf, buf_size, ")");
|
||||
if (s->f.func_type == FUNC_ELLIPSIS)
|
||||
pstrcat(buf1, sizeof(buf1), ", ...");
|
||||
pstrcat(buf1, sizeof(buf1), ")");
|
||||
type_to_str(buf, buf_size, &s->type, buf1);
|
||||
goto no_var;
|
||||
case VT_PTR:
|
||||
s = type->ref;
|
||||
if (t & VT_ARRAY) {
|
||||
snprintf(buf1, sizeof(buf1), "%s[%d]", varstr ? varstr : "", s->c);
|
||||
if (varstr && '*' == *varstr)
|
||||
snprintf(buf1, sizeof(buf1), "(%s)[%d]", varstr, s->c);
|
||||
else
|
||||
snprintf(buf1, sizeof(buf1), "%s[%d]", varstr ? varstr : "", s->c);
|
||||
type_to_str(buf, buf_size, &s->type, buf1);
|
||||
goto no_var;
|
||||
}
|
||||
|
@ -2975,22 +3000,14 @@ static void gen_assign_cast(CType *dt)
|
|||
{
|
||||
CType *st, *type1, *type2;
|
||||
char buf1[256], buf2[256];
|
||||
int dbt, sbt;
|
||||
int dbt, sbt, qualwarn, lvl;
|
||||
|
||||
st = &vtop->type; /* source type */
|
||||
dbt = dt->t & VT_BTYPE;
|
||||
sbt = st->t & VT_BTYPE;
|
||||
if (sbt == VT_VOID || dbt == VT_VOID) {
|
||||
if (sbt == VT_VOID && dbt == VT_VOID)
|
||||
; /*
|
||||
It is Ok if both are void
|
||||
A test program:
|
||||
void func1() {}
|
||||
void func2() {
|
||||
return func1();
|
||||
}
|
||||
gcc accepts this program
|
||||
*/
|
||||
; /* It is Ok if both are void */
|
||||
else
|
||||
tcc_error("cannot cast from/to void");
|
||||
}
|
||||
|
@ -3001,43 +3018,49 @@ static void gen_assign_cast(CType *dt)
|
|||
/* special cases for pointers */
|
||||
/* '0' can also be a pointer */
|
||||
if (is_null_pointer(vtop))
|
||||
goto type_ok;
|
||||
break;
|
||||
/* accept implicit pointer to integer cast with warning */
|
||||
if (is_integer_btype(sbt)) {
|
||||
tcc_warning("assignment makes pointer from integer without a cast");
|
||||
goto type_ok;
|
||||
break;
|
||||
}
|
||||
type1 = pointed_type(dt);
|
||||
/* a function is implicitly a function pointer */
|
||||
if (sbt == VT_FUNC) {
|
||||
if ((type1->t & VT_BTYPE) != VT_VOID &&
|
||||
!is_compatible_types(pointed_type(dt), st))
|
||||
tcc_warning("assignment from incompatible pointer type");
|
||||
goto type_ok;
|
||||
}
|
||||
if (sbt != VT_PTR)
|
||||
if (sbt == VT_PTR)
|
||||
type2 = pointed_type(st);
|
||||
else if (sbt == VT_FUNC)
|
||||
type2 = st; /* a function is implicitly a function pointer */
|
||||
else
|
||||
goto error;
|
||||
type2 = pointed_type(st);
|
||||
if ((type1->t & VT_BTYPE) == VT_VOID ||
|
||||
(type2->t & VT_BTYPE) == VT_VOID) {
|
||||
/* void * can match anything */
|
||||
} else {
|
||||
//printf("types %08x %08x\n", type1->t, type2->t);
|
||||
/* exact type match, except for qualifiers */
|
||||
if (!is_compatible_unqualified_types(type1, type2)) {
|
||||
if (is_compatible_types(type1, type2))
|
||||
break;
|
||||
for (qualwarn = lvl = 0;; ++lvl) {
|
||||
if (((type2->t & VT_CONSTANT) && !(type1->t & VT_CONSTANT)) ||
|
||||
((type2->t & VT_VOLATILE) && !(type1->t & VT_VOLATILE)))
|
||||
qualwarn = 1;
|
||||
dbt = type1->t & (VT_BTYPE|VT_LONG);
|
||||
sbt = type2->t & (VT_BTYPE|VT_LONG);
|
||||
if (dbt != VT_PTR || sbt != VT_PTR)
|
||||
break;
|
||||
type1 = pointed_type(type1);
|
||||
type2 = pointed_type(type2);
|
||||
}
|
||||
if (!is_compatible_unqualified_types(type1, type2)) {
|
||||
if ((dbt == VT_VOID || sbt == VT_VOID) && lvl == 0) {
|
||||
/* void * can match anything */
|
||||
} else if (dbt == sbt
|
||||
&& is_integer_btype(sbt & VT_BTYPE)
|
||||
&& IS_ENUM(type1->t) + IS_ENUM(type2->t)
|
||||
+ !!((type1->t ^ type2->t) & VT_UNSIGNED) < 2) {
|
||||
/* Like GCC don't warn by default for merely changes
|
||||
in pointer target signedness. Do warn for different
|
||||
base types, though, in particular for unsigned enums
|
||||
and signed int targets. */
|
||||
if ((type1->t & (VT_BTYPE|VT_LONG)) != (type2->t & (VT_BTYPE|VT_LONG))
|
||||
|| IS_ENUM(type1->t) || IS_ENUM(type2->t)
|
||||
)
|
||||
tcc_warning("assignment from incompatible pointer type");
|
||||
}
|
||||
} else {
|
||||
tcc_warning("assignment from incompatible pointer type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* check const and volatile */
|
||||
if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
|
||||
(!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
|
||||
if (qualwarn)
|
||||
tcc_warning("assignment discards qualifiers from pointer target type");
|
||||
break;
|
||||
case VT_BYTE:
|
||||
|
@ -3061,7 +3084,6 @@ static void gen_assign_cast(CType *dt)
|
|||
}
|
||||
break;
|
||||
}
|
||||
type_ok:
|
||||
gen_cast(dt);
|
||||
}
|
||||
|
||||
|
@ -3428,6 +3450,9 @@ redo:
|
|||
case TOK_DLLEXPORT:
|
||||
ad->a.dllexport = 1;
|
||||
break;
|
||||
case TOK_NODECORATE:
|
||||
ad->a.nodecorate = 1;
|
||||
break;
|
||||
case TOK_DLLIMPORT:
|
||||
ad->a.dllimport = 1;
|
||||
break;
|
||||
|
@ -3900,6 +3925,7 @@ do_decl:
|
|||
get_tok_str(v, NULL));
|
||||
}
|
||||
if ((type1.t & VT_BTYPE) == VT_FUNC ||
|
||||
(type1.t & VT_BTYPE) == VT_VOID ||
|
||||
(type1.t & VT_STORAGE))
|
||||
tcc_error("invalid type for '%s'",
|
||||
get_tok_str(v, NULL));
|
||||
|
@ -4164,7 +4190,7 @@ static int parse_btype(CType *type, AttributeDef *ad)
|
|||
u = ad->attr_mode -1;
|
||||
t = (t & ~(VT_BTYPE|VT_LONG)) | u;
|
||||
}
|
||||
break;
|
||||
continue;
|
||||
/* GNUC typeof */
|
||||
case TOK_TYPEOF1:
|
||||
case TOK_TYPEOF2:
|
||||
|
@ -4284,7 +4310,6 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
|
|||
type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
|
||||
if ((pt.t & VT_BTYPE) == VT_VOID)
|
||||
tcc_error("parameter declared as void");
|
||||
arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
|
||||
} else {
|
||||
n = tok;
|
||||
if (n < TOK_UIDENT)
|
||||
|
@ -4293,6 +4318,7 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
|
|||
next();
|
||||
}
|
||||
convert_parameter_type(&pt);
|
||||
arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
|
||||
s = sym_push(n | SYM_FIELD, &pt, 0, 0);
|
||||
*plast = s;
|
||||
plast = &s->next;
|
||||
|
@ -4335,8 +4361,23 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
|
|||
int saved_nocode_wanted = nocode_wanted;
|
||||
/* array definition */
|
||||
next();
|
||||
if (tok == TOK_RESTRICT1)
|
||||
next();
|
||||
while (1) {
|
||||
/* XXX The optional type-quals and static should only be accepted
|
||||
in parameter decls. The '*' as well, and then even only
|
||||
in prototypes (not function defs). */
|
||||
switch (tok) {
|
||||
case TOK_RESTRICT1: case TOK_RESTRICT2: case TOK_RESTRICT3:
|
||||
case TOK_CONST1:
|
||||
case TOK_VOLATILE1:
|
||||
case TOK_STATIC:
|
||||
case '*':
|
||||
next();
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
n = -1;
|
||||
t1 = 0;
|
||||
if (tok != ']') {
|
||||
|
@ -4788,6 +4829,7 @@ ST_FUNC void unary(void)
|
|||
case TOK_SIZEOF:
|
||||
case TOK_ALIGNOF1:
|
||||
case TOK_ALIGNOF2:
|
||||
case TOK_ALIGNOF3:
|
||||
t = tok;
|
||||
next();
|
||||
in_sizeof++;
|
||||
|
@ -4995,11 +5037,16 @@ ST_FUNC void unary(void)
|
|||
int has_match = 0;
|
||||
int learn = 0;
|
||||
TokenString *str = NULL;
|
||||
int saved_const_wanted = const_wanted;
|
||||
|
||||
next();
|
||||
skip('(');
|
||||
const_wanted = 0;
|
||||
expr_type(&controlling_type, expr_eq);
|
||||
controlling_type.t &= ~(VT_CONSTANT | VT_VOLATILE | VT_ARRAY);
|
||||
if ((controlling_type.t & VT_BTYPE) == VT_FUNC)
|
||||
mk_pointer(&controlling_type);
|
||||
const_wanted = saved_const_wanted;
|
||||
for (;;) {
|
||||
learn = 0;
|
||||
skip(',');
|
||||
|
@ -5051,17 +5098,18 @@ ST_FUNC void unary(void)
|
|||
}
|
||||
// special qnan , snan and infinity values
|
||||
case TOK___NAN__:
|
||||
vpush64(VT_DOUBLE, 0x7ff8000000000000ULL);
|
||||
n = 0x7fc00000;
|
||||
special_math_val:
|
||||
vpushi(n);
|
||||
vtop->type.t = VT_FLOAT;
|
||||
next();
|
||||
break;
|
||||
case TOK___SNAN__:
|
||||
vpush64(VT_DOUBLE, 0x7ff0000000000001ULL);
|
||||
next();
|
||||
break;
|
||||
n = 0x7f800001;
|
||||
goto special_math_val;
|
||||
case TOK___INF__:
|
||||
vpush64(VT_DOUBLE, 0x7ff0000000000000ULL);
|
||||
next();
|
||||
break;
|
||||
n = 0x7f800000;
|
||||
goto special_math_val;
|
||||
|
||||
default:
|
||||
tok_identifier:
|
||||
|
@ -5518,7 +5566,9 @@ static void expr_cond(void)
|
|||
if (!g)
|
||||
gexpr();
|
||||
|
||||
type1 = vtop->type;
|
||||
if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
|
||||
mk_pointer(&vtop->type);
|
||||
type1 = vtop->type;
|
||||
sv = *vtop; /* save value to handle it later */
|
||||
vtop--; /* no vpop so that FP stack is not flushed */
|
||||
skip(':');
|
||||
|
@ -5536,15 +5586,20 @@ static void expr_cond(void)
|
|||
if (c == 1)
|
||||
nocode_wanted--;
|
||||
|
||||
type2 = vtop->type;
|
||||
if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
|
||||
mk_pointer(&vtop->type);
|
||||
type2=vtop->type;
|
||||
t1 = type1.t;
|
||||
bt1 = t1 & VT_BTYPE;
|
||||
t2 = type2.t;
|
||||
bt2 = t2 & VT_BTYPE;
|
||||
type.ref = NULL;
|
||||
|
||||
|
||||
/* cast operands to correct type according to ISOC rules */
|
||||
if (is_float(bt1) || is_float(bt2)) {
|
||||
if (bt1 == VT_VOID || bt2 == VT_VOID) {
|
||||
type.t = VT_VOID; /* NOTE: as an extension, we accept void on only one side */
|
||||
} else if (is_float(bt1) || is_float(bt2)) {
|
||||
if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
|
||||
type.t = VT_LDOUBLE;
|
||||
|
||||
|
@ -5565,25 +5620,57 @@ static void expr_cond(void)
|
|||
(t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
|
||||
type.t |= VT_UNSIGNED;
|
||||
} else if (bt1 == VT_PTR || bt2 == VT_PTR) {
|
||||
/* If one is a null ptr constant the result type
|
||||
is the other. */
|
||||
if (is_null_pointer (vtop))
|
||||
type = type1;
|
||||
else if (is_null_pointer (&sv))
|
||||
type = type2;
|
||||
/* XXX: test pointer compatibility, C99 has more elaborate
|
||||
rules here. */
|
||||
else
|
||||
type = type1;
|
||||
} else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
|
||||
/* XXX: test function pointer compatibility */
|
||||
type = bt1 == VT_FUNC ? type1 : type2;
|
||||
/* http://port70.net/~nsz/c/c99/n1256.html#6.5.15p6 */
|
||||
/* If one is a null ptr constant the result type
|
||||
is the other. */
|
||||
if (is_null_pointer (vtop)) type = type1;
|
||||
else if (is_null_pointer (&sv)) type = type2;
|
||||
else if (bt1 != bt2)
|
||||
tcc_error("incompatible types in conditional expressions");
|
||||
else {
|
||||
CType *pt1 = pointed_type(&type1);
|
||||
CType *pt2 = pointed_type(&type2);
|
||||
int pbt1 = pt1->t & VT_BTYPE;
|
||||
int pbt2 = pt2->t & VT_BTYPE;
|
||||
int newquals, copied = 0;
|
||||
/* pointers to void get preferred, otherwise the
|
||||
pointed to types minus qualifs should be compatible */
|
||||
type = (pbt1 == VT_VOID) ? type1 : type2;
|
||||
if (pbt1 != VT_VOID && pbt2 != VT_VOID) {
|
||||
if(!compare_types(pt1, pt2, 1/*unqualif*/))
|
||||
tcc_warning("pointer type mismatch in conditional expression\n");
|
||||
}
|
||||
/* combine qualifs */
|
||||
newquals = ((pt1->t | pt2->t) & (VT_CONSTANT | VT_VOLATILE));
|
||||
if ((~pointed_type(&type)->t & (VT_CONSTANT | VT_VOLATILE))
|
||||
& newquals)
|
||||
{
|
||||
/* copy the pointer target symbol */
|
||||
type.ref = sym_push(SYM_FIELD, &type.ref->type,
|
||||
0, type.ref->c);
|
||||
copied = 1;
|
||||
pointed_type(&type)->t |= newquals;
|
||||
}
|
||||
/* pointers to incomplete arrays get converted to
|
||||
pointers to completed ones if possible */
|
||||
if (pt1->t & VT_ARRAY
|
||||
&& pt2->t & VT_ARRAY
|
||||
&& pointed_type(&type)->ref->c < 0
|
||||
&& (pt1->ref->c > 0 || pt2->ref->c > 0))
|
||||
{
|
||||
if (!copied)
|
||||
type.ref = sym_push(SYM_FIELD, &type.ref->type,
|
||||
0, type.ref->c);
|
||||
pointed_type(&type)->ref =
|
||||
sym_push(SYM_FIELD, &pointed_type(&type)->ref->type,
|
||||
0, pointed_type(&type)->ref->c);
|
||||
pointed_type(&type)->ref->c =
|
||||
0 < pt1->ref->c ? pt1->ref->c : pt2->ref->c;
|
||||
}
|
||||
}
|
||||
} else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
|
||||
/* XXX: test structure compatibility */
|
||||
type = bt1 == VT_STRUCT ? type1 : type2;
|
||||
} else if (bt1 == VT_VOID || bt2 == VT_VOID) {
|
||||
/* NOTE: as an extension, we accept void on only one side */
|
||||
type.t = VT_VOID;
|
||||
} else {
|
||||
/* integer operations */
|
||||
type.t = VT_INT | (VT_LONG & (t1 | t2));
|
||||
|
@ -5595,7 +5682,6 @@ static void expr_cond(void)
|
|||
/* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so
|
||||
that `(expr ? a : b).mem` does not error with "lvalue expected" */
|
||||
islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE);
|
||||
islv &= c < 0;
|
||||
|
||||
/* now we convert second operand */
|
||||
if (c != 1) {
|
||||
|
@ -5640,7 +5726,7 @@ static void expr_cond(void)
|
|||
gaddrof();
|
||||
}
|
||||
|
||||
if (c < 0) {
|
||||
if (c < 0 || islv) {
|
||||
r1 = gv(rc);
|
||||
move_reg(r2, r1, type.t);
|
||||
vtop->r = r2;
|
||||
|
@ -6086,6 +6172,8 @@ static void block(int *bsym, int *csym, int is_expr)
|
|||
skip(TOK_WHILE);
|
||||
skip('(');
|
||||
gsym(b);
|
||||
if (b)
|
||||
nocode_wanted = saved_nocode_wanted;
|
||||
gexpr();
|
||||
c = gvtst(0, 0);
|
||||
gsym_addr(c, d);
|
||||
|
@ -6826,11 +6914,16 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
|||
Sym *sym = NULL;
|
||||
int saved_nocode_wanted = nocode_wanted;
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
int bcheck = tcc_state->do_bounds_check && !NODATA_WANTED;
|
||||
int bcheck;
|
||||
#endif
|
||||
|
||||
if (type->t & VT_STATIC)
|
||||
nocode_wanted |= NODATA_WANTED ? 0x40000000 : 0x80000000;
|
||||
/* Always allocate static or global variables */
|
||||
if (v && (r & VT_VALMASK) == VT_CONST)
|
||||
nocode_wanted |= 0x80000000;
|
||||
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
bcheck = tcc_state->do_bounds_check && !NODATA_WANTED;
|
||||
#endif
|
||||
|
||||
flexible_array = NULL;
|
||||
if ((type->t & VT_BTYPE) == VT_STRUCT) {
|
||||
|
@ -6896,7 +6989,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
|||
align = 1;
|
||||
}
|
||||
|
||||
if (NODATA_WANTED)
|
||||
if (!v && NODATA_WANTED)
|
||||
size = 0, align = 1;
|
||||
|
||||
if ((r & VT_VALMASK) == VT_LOCAL) {
|
||||
|
@ -7055,6 +7148,11 @@ static void gen_function(Sym *sym)
|
|||
{
|
||||
nocode_wanted = 0;
|
||||
ind = cur_text_section->data_offset;
|
||||
if (sym->a.aligned) {
|
||||
size_t newoff = section_add(cur_text_section, 0,
|
||||
1 << (sym->a.aligned - 1));
|
||||
gen_fill_nops(newoff - ind);
|
||||
}
|
||||
/* NOTE: we patch the symbol size later */
|
||||
put_extern_sym(sym, cur_text_section, ind, 0);
|
||||
funcname = get_tok_str(sym->v, NULL);
|
||||
|
@ -7071,6 +7169,15 @@ static void gen_function(Sym *sym)
|
|||
local_scope = 0;
|
||||
rsym = 0;
|
||||
block(NULL, NULL, 0);
|
||||
if (!(nocode_wanted & 0x20000000)
|
||||
&& ((func_vt.t & VT_BTYPE) == VT_INT)
|
||||
&& !strcmp (funcname, "main"))
|
||||
{
|
||||
nocode_wanted = 0;
|
||||
vpushi(0);
|
||||
gen_assign_cast(&func_vt);
|
||||
gfunc_return(&func_vt);
|
||||
}
|
||||
nocode_wanted = 0;
|
||||
gsym(rsym);
|
||||
gfunc_epilog();
|
||||
|
@ -7316,6 +7423,9 @@ found:
|
|||
}
|
||||
sym->a = ad.a;
|
||||
sym->f = ad.f;
|
||||
} else if ((type.t & VT_BTYPE) == VT_VOID
|
||||
&& !(type.t & VT_EXTERN)) {
|
||||
tcc_error("declaration of void object");
|
||||
} else {
|
||||
r = 0;
|
||||
if ((type.t & VT_BTYPE) == VT_FUNC) {
|
||||
|
|
4
tccpe.c
4
tccpe.c
|
@ -1866,8 +1866,6 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
|
|||
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
|
||||
SHN_UNDEF, start_symbol);
|
||||
|
||||
tcc_add_pragma_libs(s1);
|
||||
|
||||
if (0 == s1->nostdlib) {
|
||||
static const char *libs[] = {
|
||||
TCC_LIBTCC1, "msvcrt", "kernel32", "", "user32", "gdi32", NULL
|
||||
|
@ -1948,7 +1946,7 @@ ST_FUNC int pe_output_file(TCCState *s1, const char *filename)
|
|||
pe.filename = filename;
|
||||
pe.s1 = s1;
|
||||
|
||||
tcc_add_bcheck(s1);
|
||||
tcc_add_runtime(s1);
|
||||
pe_add_runtime(s1, &pe);
|
||||
resolve_common_syms(s1);
|
||||
pe_set_options(s1, &pe);
|
||||
|
|
44
tccrun.c
44
tccrun.c
|
@ -169,9 +169,12 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
|
|||
}
|
||||
|
||||
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
|
||||
/* To avoid that x86 processors would reload cached instructions
|
||||
each time when data is written in the near, we need to make
|
||||
sure that code and data do not share the same 64 byte unit */
|
||||
#define RUN_SECTION_ALIGNMENT 63
|
||||
#else
|
||||
#define RUN_SECTION_ALIGNMENT 15
|
||||
#define RUN_SECTION_ALIGNMENT 0
|
||||
#endif
|
||||
|
||||
/* relocate code. Return -1 on error, required size if ptr is NULL,
|
||||
|
@ -179,8 +182,8 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
|
|||
static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
|
||||
{
|
||||
Section *s;
|
||||
unsigned offset, length, fill, i, k;
|
||||
addr_t mem;
|
||||
unsigned offset, length, align, max_align, i, k, f;
|
||||
addr_t mem, addr;
|
||||
|
||||
if (NULL == ptr) {
|
||||
s1->nb_errors = 0;
|
||||
|
@ -195,39 +198,32 @@ static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
|
|||
return -1;
|
||||
}
|
||||
|
||||
offset = 0, mem = (addr_t)ptr;
|
||||
fill = -mem & RUN_SECTION_ALIGNMENT;
|
||||
offset = max_align = 0, mem = (addr_t)ptr;
|
||||
#ifdef _WIN64
|
||||
offset += sizeof (void*);
|
||||
offset += sizeof (void*); /* space for function_table pointer */
|
||||
#endif
|
||||
for (k = 0; k < 2; ++k) {
|
||||
f = 0, addr = k ? mem : mem + ptr_diff;
|
||||
for(i = 1; i < s1->nb_sections; i++) {
|
||||
s = s1->sections[i];
|
||||
if (0 == (s->sh_flags & SHF_ALLOC))
|
||||
continue;
|
||||
if (k != !(s->sh_flags & SHF_EXECINSTR))
|
||||
continue;
|
||||
offset += fill;
|
||||
if (!mem)
|
||||
s->sh_addr = 0;
|
||||
else if (s->sh_flags & SHF_EXECINSTR)
|
||||
s->sh_addr = mem + offset + ptr_diff;
|
||||
else
|
||||
s->sh_addr = mem + offset;
|
||||
align = s->sh_addralign - 1;
|
||||
if (++f == 1 && align < RUN_SECTION_ALIGNMENT)
|
||||
align = RUN_SECTION_ALIGNMENT;
|
||||
if (max_align < align)
|
||||
max_align = align;
|
||||
offset += -(addr + offset) & align;
|
||||
s->sh_addr = mem ? addr + offset : 0;
|
||||
offset += s->data_offset;
|
||||
#if 0
|
||||
if (mem)
|
||||
printf("%-16s +%02lx %p %04x\n",
|
||||
s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
|
||||
printf("%-16s %p len %04x align %2d\n",
|
||||
s->name, (void*)s->sh_addr, (unsigned)s->data_offset, align + 1);
|
||||
#endif
|
||||
offset += s->data_offset;
|
||||
fill = -(mem + offset) & 15;
|
||||
}
|
||||
#if RUN_SECTION_ALIGNMENT > 15
|
||||
/* To avoid that x86 processors would reload cached instructions each time
|
||||
when data is written in the near, we need to make sure that code and data
|
||||
do not share the same 64 byte unit */
|
||||
fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* relocate symbols */
|
||||
|
@ -236,7 +232,7 @@ static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
|
|||
return -1;
|
||||
|
||||
if (0 == mem)
|
||||
return offset + RUN_SECTION_ALIGNMENT;
|
||||
return offset + max_align;
|
||||
|
||||
#ifdef TCC_TARGET_PE
|
||||
s1->pe_imagebase = mem;
|
||||
|
|
2
tcctok.h
2
tcctok.h
|
@ -53,6 +53,7 @@
|
|||
DEF(TOK_ATTRIBUTE2, "__attribute__")
|
||||
DEF(TOK_ALIGNOF1, "__alignof")
|
||||
DEF(TOK_ALIGNOF2, "__alignof__")
|
||||
DEF(TOK_ALIGNOF3, "_Alignof")
|
||||
DEF(TOK_TYPEOF1, "typeof")
|
||||
DEF(TOK_TYPEOF2, "__typeof")
|
||||
DEF(TOK_TYPEOF3, "__typeof__")
|
||||
|
@ -132,6 +133,7 @@
|
|||
|
||||
DEF(TOK_DLLEXPORT, "dllexport")
|
||||
DEF(TOK_DLLIMPORT, "dllimport")
|
||||
DEF(TOK_NODECORATE, "nodecorate")
|
||||
DEF(TOK_NORETURN1, "noreturn")
|
||||
DEF(TOK_NORETURN2, "__noreturn__")
|
||||
DEF(TOK_VISIBILITY1, "visibility")
|
||||
|
|
|
@ -279,7 +279,7 @@ cache: tcc_g
|
|||
clean:
|
||||
rm -f *~ *.o *.a *.bin *.i *.ref *.out *.out? *.out?b *.cc *.gcc
|
||||
rm -f *-cc *-gcc *-tcc *.exe hello libtcc_test vla_test tcctest[1234]
|
||||
rm -f asm-c-connect$(EXESUF)
|
||||
rm -f asm-c-connect$(EXESUF) asm-c-connect-sep$(EXESUF)
|
||||
rm -f ex? tcc_g weaktest.*.txt *.def
|
||||
@$(MAKE) -C tests2 $@
|
||||
@$(MAKE) -C pp $@
|
||||
|
|
|
@ -10,7 +10,7 @@ VPATH = $(SRC)
|
|||
files = $(patsubst %.$1,%.test,$(notdir $(wildcard $(SRC)/*.$1)))
|
||||
TESTS = $(call files,c) $(call files,S)
|
||||
|
||||
all test : $(sort $(TESTS))
|
||||
all test testspp.all: $(sort $(TESTS))
|
||||
|
||||
DIFF_OPTS = -Nu -b -B
|
||||
|
||||
|
@ -29,6 +29,8 @@ FILTER = 2>&1 | sed 's,$(SRC)/,,g'
|
|||
diff $(DIFF_OPTS) $(SRC)/$*.expect $*.output \
|
||||
&& rm -f $*.output
|
||||
|
||||
testspp.%: %.test ;
|
||||
|
||||
# automatically generate .expect files with gcc:
|
||||
%.expect: # %.c
|
||||
gcc -E -P $*.[cS] >$*.expect 2>&1
|
||||
|
|
|
@ -2234,6 +2234,9 @@ void float_test(void)
|
|||
double da, db;
|
||||
int a;
|
||||
unsigned int b;
|
||||
static double nan2 = 0.0/0.0;
|
||||
static double inf1 = 1.0/0.0;
|
||||
static double inf2 = 1e5000;
|
||||
|
||||
printf("float_test:\n");
|
||||
printf("sizeof(float) = %d\n", sizeof(float));
|
||||
|
@ -2255,6 +2258,7 @@ void float_test(void)
|
|||
b = 4000000000;
|
||||
db = b;
|
||||
printf("db = %f\n", db);
|
||||
printf("nan != nan = %d, inf1 = %f, inf2 = %f\n", nan2 != nan2, inf1, inf2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2266,6 +2270,8 @@ int fib(int n)
|
|||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
|
||||
void __attribute__((aligned(16))) aligned_function(int i) {}
|
||||
|
||||
void funcptr_test()
|
||||
{
|
||||
void (*func)(int);
|
||||
|
@ -2296,6 +2302,10 @@ void funcptr_test()
|
|||
func(42);
|
||||
(func + diff)(42);
|
||||
(num + a)(43);
|
||||
|
||||
/* Check that we can align functions */
|
||||
func = aligned_function;
|
||||
printf("aligned_function (should be zero): %d\n", ((int)func) & 15);
|
||||
}
|
||||
|
||||
void lloptest(long long a, long long b)
|
||||
|
@ -2487,6 +2497,11 @@ void longlong_test(void)
|
|||
a = 0x123;
|
||||
long long *p = &a;
|
||||
llshift(*p, 5);
|
||||
|
||||
/* shortening followed by widening */
|
||||
unsigned long long u = 0x8000000000000001ULL;
|
||||
u = (unsigned)(u + 1);
|
||||
printf("long long u=" ULONG_LONG_FORMAT "\n", u);
|
||||
}
|
||||
|
||||
void manyarg_test(void)
|
||||
|
|
|
@ -15,6 +15,19 @@ void qfunc()
|
|||
printf("qfunc()\n");
|
||||
}
|
||||
|
||||
#if !defined(__ARMEL__)
|
||||
/*
|
||||
* At least on ARM (like RPi), zfunc below fails with something like:
|
||||
* +tcc: error: can't relocate value at 1ef93bc,1
|
||||
* Test is temporary removed for this architecture until ARM maintainers
|
||||
* see what happens with this test.
|
||||
*/
|
||||
void zfunc()
|
||||
{
|
||||
((void (*)(void))0) ();
|
||||
}
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("%d\n", myfunc(3));
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
void foo(int [5]);
|
||||
void fooc(int x[const 5]);
|
||||
void foos(int x[static 5]);
|
||||
void foov(int x[volatile 5]);
|
||||
void foor(int x[restrict 5]);
|
||||
void fooc(int [const 5]);
|
||||
void foos(int [static 5]);
|
||||
void foov(int [volatile 5]);
|
||||
void foor(int [restrict 5]);
|
||||
void fooc(int (* const x));
|
||||
void foos(int *x);
|
||||
void foov(int * volatile x);
|
||||
void foor(int * restrict x);
|
||||
void fooc(int x[volatile 5])
|
||||
{
|
||||
x[3] = 42;
|
||||
#ifdef INVALID
|
||||
x = 0;
|
||||
#endif
|
||||
}
|
||||
void foovm(int x[const *]);
|
||||
void foovm(int * const x);
|
||||
#ifdef INVALID
|
||||
void wrongc(int x[3][const 4]);
|
||||
void wrongvm(int x[static *]);
|
||||
void foovm(int x[const *])
|
||||
{
|
||||
x[2] = 1;
|
||||
}
|
||||
#endif
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,30 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
char arr[1];
|
||||
static void f (void){}
|
||||
void (*fp)(void) = f;
|
||||
void call_fp()
|
||||
{
|
||||
(fp?f:f)();
|
||||
(fp?fp:fp)();
|
||||
(fp?fp:&f)();
|
||||
(fp?&f:fp)();
|
||||
(fp?&f:&f)();
|
||||
_Generic(0?arr:arr, char*: (void)0);
|
||||
_Generic(0?&arr[0]:arr, char*: (void)0);
|
||||
_Generic(0?arr:&arr[0], char*: (void)0);
|
||||
_Generic(1?arr:arr, char*: (void)0);
|
||||
_Generic(1?&arr[0]:arr, char*: (void)0);
|
||||
_Generic(1?arr:&arr[0], char*: (void)0);
|
||||
_Generic((__typeof(1?f:f)*){0}, void (**)(void): (void)0);
|
||||
(fp?&f:f)();
|
||||
(fp?f:&f)();
|
||||
_Generic((__typeof(fp?0L:(void)0)*){0}, void*: (void)0);
|
||||
|
||||
//Should cleanly fail, not segfault:
|
||||
/*(fp?f:1);*/
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
|
@ -9,6 +35,19 @@ int main()
|
|||
printf("%d\n", (Count < 5) ? (Count*Count) : (Count * 3));
|
||||
}
|
||||
|
||||
{
|
||||
int c = 0;
|
||||
#define ASSERT(X) assert(X)
|
||||
static struct stru { int x; } a={'A'},b={'B'};
|
||||
ASSERT('A'==(*(1?&a:&b)).x);
|
||||
ASSERT('A'==(1?a:b).x);
|
||||
ASSERT('A'==(c?b:a).x);
|
||||
ASSERT('A'==(0?b:a).x);
|
||||
c=1;
|
||||
ASSERT('A'==(c?a:b).x);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,4 +48,80 @@ enum rgb3 c = 42;
|
|||
#elif defined test_74_non_const_init
|
||||
int i = i++;
|
||||
|
||||
#elif defined test_pointer_assignment
|
||||
|
||||
void (*f1)(void);
|
||||
void f2(void) {}
|
||||
|
||||
struct s1 *ps1;
|
||||
struct s2 *ps2;
|
||||
|
||||
void *v1, **v2, ***v3;
|
||||
|
||||
enum e1 { a = 4 } e10, *e11, *e12;
|
||||
enum e2 { b = -4 } e20, *e21;
|
||||
enum e3 { c = 5000000000LL } e30;
|
||||
|
||||
int *ip;
|
||||
unsigned int *up;
|
||||
long *lp;
|
||||
long long *llp;
|
||||
|
||||
char **c1;
|
||||
char const **c2;
|
||||
unsigned char **u1;
|
||||
|
||||
int no_main ()
|
||||
{
|
||||
// function
|
||||
f1 = f2;
|
||||
// struct
|
||||
ps1 = ps2;
|
||||
// void*
|
||||
v1 = v3;
|
||||
v2 = v3;
|
||||
|
||||
// enum
|
||||
e11 = e12;
|
||||
e11 = e21;
|
||||
e11 = &e10;
|
||||
ip = &e10;
|
||||
ip = &e20;
|
||||
up = &e10;
|
||||
up = &e20;
|
||||
up = &e30;
|
||||
|
||||
lp = ip;
|
||||
lp = llp;
|
||||
|
||||
// constness
|
||||
c1 = c2;
|
||||
*c1 = *c2;
|
||||
**c1 = **c2;
|
||||
|
||||
// unsigned = signed
|
||||
u1 = c2;
|
||||
*u1 = *c2;
|
||||
**u1 = **c2;
|
||||
|
||||
c2 = c1;
|
||||
*c2 = *c1;
|
||||
**c2 = **c1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#elif defined test_enum_compat
|
||||
enum e4;
|
||||
enum e5;
|
||||
void f3(enum e4 e);
|
||||
void f3(enum e5 e);
|
||||
|
||||
#elif defined test_ptr_to_str
|
||||
void f() { _Generic((int const *[]){0}, int:0); }
|
||||
#elif defined test_fnptr_to_str
|
||||
void f() { _Generic((int (*(*)(float,char))(double,int)){0}, int:0); }
|
||||
#elif defined test_array_to_str
|
||||
void f() { _Generic((int(*)[3]){0}, int:0); }
|
||||
#endif
|
||||
|
|
|
@ -26,3 +26,30 @@
|
|||
|
||||
[test_74_non_const_init]
|
||||
60_errors_and_warnings.c:49: error: initializer element is not constant
|
||||
|
||||
[test_pointer_assignment]
|
||||
60_errors_and_warnings.c:79: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:82: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:86: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:88: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:91: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:92: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:94: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:95: warning: assignment from incompatible pointer type
|
||||
60_errors_and_warnings.c:98: warning: assignment discards qualifiers from pointer target type
|
||||
60_errors_and_warnings.c:99: warning: assignment discards qualifiers from pointer target type
|
||||
60_errors_and_warnings.c:103: warning: assignment discards qualifiers from pointer target type
|
||||
60_errors_and_warnings.c:104: warning: assignment discards qualifiers from pointer target type
|
||||
60_errors_and_warnings.c:109: warning: assignment of read-only location
|
||||
|
||||
[test_enum_compat]
|
||||
60_errors_and_warnings.c:119: error: incompatible types for redefinition of 'f3'
|
||||
|
||||
[test_ptr_to_str]
|
||||
60_errors_and_warnings.c:122: error: type 'const int **' does not match any association
|
||||
|
||||
[test_fnptr_to_str]
|
||||
60_errors_and_warnings.c:124: error: type 'int (*(*)(float, char))(double, int)' does not match any association
|
||||
|
||||
[test_array_to_str]
|
||||
60_errors_and_warnings.c:126: error: type 'int (*)[3]' does not match any association
|
||||
|
|
|
@ -16,4 +16,25 @@ void __attribute__((stdcall)) foo (void)
|
|||
{
|
||||
}
|
||||
|
||||
int main () { return 0; }
|
||||
/* The actual attribute isn't important, must just be
|
||||
parsable. */
|
||||
#define ATTR __attribute__((__noinline__))
|
||||
int ATTR actual_function() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
extern int printf (const char *, ...);
|
||||
int main()
|
||||
{
|
||||
void *function_pointer = &actual_function;
|
||||
|
||||
int a = ((ATTR int(*) (void)) function_pointer)();
|
||||
printf("%i\n", a);
|
||||
|
||||
/* In the following we once misparsed 'ATTR *' is a btype
|
||||
and hence the whole type was garbled. */
|
||||
int b = ( (int(ATTR *)(void)) function_pointer)();
|
||||
printf("%i\n", b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
42
|
||||
42
|
|
@ -26,6 +26,36 @@ static void kb_wait_1(void)
|
|||
timeout--;
|
||||
} while (timeout);
|
||||
}
|
||||
|
||||
static int global;
|
||||
|
||||
static void foo(int i)
|
||||
{
|
||||
global+=i;
|
||||
printf ("g=%d\n", global);
|
||||
}
|
||||
|
||||
static int check(void)
|
||||
{
|
||||
printf ("check %d\n", global);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void dowhile(void)
|
||||
{
|
||||
do {
|
||||
foo(1);
|
||||
if (global == 1) {
|
||||
continue;
|
||||
} else if (global == 2) {
|
||||
continue;
|
||||
}
|
||||
/* The following break shouldn't disable the check() call,
|
||||
as it's reachable by the continues above. */
|
||||
break;
|
||||
} while (check());
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
int i = 1;
|
||||
|
@ -118,5 +148,8 @@ enterloop3:
|
|||
printf ("error4\n");
|
||||
}
|
||||
}
|
||||
|
||||
dowhile();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,3 +16,8 @@ once3
|
|||
twice3
|
||||
caseok
|
||||
caseok2
|
||||
g=1
|
||||
check 1
|
||||
g=2
|
||||
check 2
|
||||
g=3
|
||||
|
|
|
@ -20,6 +20,12 @@ int b_f()
|
|||
return 10;
|
||||
}
|
||||
|
||||
typedef int (*fptr)(int);
|
||||
int foo(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
typedef int int_type1;
|
||||
|
||||
#define gen_sw(a) _Generic(a, const char *: 1, default: 8, int: 123);
|
||||
|
@ -60,5 +66,50 @@ int main()
|
|||
long long: "long long"));
|
||||
i = _Generic(l, long: 1, int: 2);
|
||||
printf("%d\n", i);
|
||||
i = _Generic(foo, fptr: 3, int: 4);
|
||||
printf("%d\n", i);
|
||||
|
||||
(void)_Generic((int(*)[2]){0}, int(*)[2]:0, int(*)[4]:0); //shouldn't match twice
|
||||
|
||||
//should accept ({ }) in the controlling expr of _Generic even in const_wanted contexts
|
||||
struct { _Bool x_0: _Generic(({0;}),default:1); } my_x;
|
||||
|
||||
_Generic((__typeof((float const)((float const){42}))*){0}, float*: 0); //casts lose top-level qualifiers
|
||||
int const x = 42; __typeof((__typeof(x))x) *xp = 0; (void)_Generic(xp, int*: 0); //casts lose top-level qualifiers
|
||||
|
||||
//TEST TERNARY:
|
||||
//Same type
|
||||
_Generic( 0?(long*)0:(long*)0, long*: (void)0);
|
||||
//combining of qualifiers
|
||||
_Generic( 0?(long volatile*)0:(long const*)0, long const volatile*: (void)0);
|
||||
//nul-ptr constant selects other type
|
||||
_Generic( 0?(long*)0:0, long*: (void)0);
|
||||
_Generic( 0?(long*)0:(void*)0, long*: (void)0);
|
||||
|
||||
//void ptrs get chosen preferentially; qualifs still combine
|
||||
_Generic( 0?(int volatile*)0: (void const*)1, void volatile const*: (void)0);
|
||||
//like gcc but not clang, don't treat (void* const as the null-ptr constant)
|
||||
_Generic( 0?(int volatile*)0: (void const*)0, void volatile const*: (void)0);
|
||||
|
||||
//ptrs to incomplete types get completed
|
||||
(void)(sizeof(struct { int x:_Generic( 0?(int (*)[4])0 : (int (*)[])0, int (*)[4]:+1, int (*)[5]:(void)0); }));
|
||||
(void)(sizeof(struct { int x:_Generic( 0?(int (*)[])0 : (int (*)[4])0, int (*)[4]:+1, int (*)[5]:(void)0); }));
|
||||
|
||||
{
|
||||
/* completion shouldn't affect the type of decl */
|
||||
char **argv;
|
||||
_Generic(argv, char**: (void)0);
|
||||
_Generic(0?(char const*)0:argv[0], char const*: (void)0);
|
||||
_Generic(argv, char**: (void)0);
|
||||
}
|
||||
{
|
||||
extern int (*ar)[];
|
||||
(void)(sizeof(struct { int x:_Generic( 0?(int (*)[4])0 : (int (*)[])0, int (*)[4]:+1, int (*)[5]:(void)0); }));
|
||||
(void)(sizeof(struct { int x:_Generic( 0?(int (*)[])0 : (int (*)[4])0, int (*)[4]:+1, int (*)[5]:(void)0); }));
|
||||
(void)(sizeof(struct { int x:_Generic( 0?ar : (int (*)[4])0, int (*)[4]:+1, int (*)[5]:(void)0); }));
|
||||
(void)(sizeof(struct { int x:_Generic( 0?(int (*)[4])0 : ar, int (*)[4]:+1, int (*)[5]:(void)0); }));
|
||||
(void)(sizeof(struct { int x:_Generic( 0?(int (*)[5])0 : ar, int (*)[5]:+1, int (*)[4]:(void)0); }));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,4 +10,5 @@
|
|||
3
|
||||
4
|
||||
long
|
||||
1
|
||||
1
|
||||
3
|
||||
|
|
|
@ -55,11 +55,10 @@ te0:;
|
|||
static char ds1 = 0;
|
||||
ts1:;
|
||||
if (!SKIP) {
|
||||
static void *p = (void*)&main;
|
||||
static char cc[] = "static string";
|
||||
static double d = 8.0;
|
||||
|
||||
static struct __attribute__((packed)) {
|
||||
void *p = (void*)&main;
|
||||
char cc[] = "static string";
|
||||
double d = 8.0;
|
||||
struct __attribute__((packed)) {
|
||||
unsigned x : 12;
|
||||
unsigned char y : 7;
|
||||
unsigned z : 28, a: 4, b: 5;
|
||||
|
@ -81,4 +80,19 @@ te1:;
|
|||
/*printf("# %d/%d\n", dl, tl);*/
|
||||
}
|
||||
|
||||
#elif defined test_static_data
|
||||
|
||||
#include <stdio.h>
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
goto there;
|
||||
if (0) {
|
||||
static int a = 1;
|
||||
printf("hello\n"); /* the "hello\n" string is still suppressed */
|
||||
there:
|
||||
printf("a = %d\n", a);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,3 +21,6 @@ size of data/text:
|
|||
[test_data_suppression_on]
|
||||
size of data/text:
|
||||
zero/zero
|
||||
|
||||
[test_static_data]
|
||||
a = 1
|
||||
|
|
|
@ -3,7 +3,9 @@ include $(TOP)/Makefile
|
|||
SRC = $(TOPSRC)/tests/tests2
|
||||
VPATH = $(SRC)
|
||||
|
||||
TESTS = $(patsubst %.c,%.test,$(sort $(notdir $(wildcard $(SRC)/*.c))))
|
||||
TESTS = $(patsubst %.c,%.test,\
|
||||
$(sort $(notdir $(wildcard $(SRC)/??_*.c)))\
|
||||
$(sort $(notdir $(wildcard $(SRC)/???_*.c))))
|
||||
|
||||
# some tests do not pass on all platforms, remove them for now
|
||||
SKIP = 34_array_assignment.test # array assignment is not in C standard
|
||||
|
|
|
@ -81,9 +81,9 @@ extern "C" {
|
|||
#else
|
||||
#define _fstat _fstat64i32
|
||||
#define _fstati64 _fstat64
|
||||
#define _stat _stat64i32
|
||||
#define _stat _stat64
|
||||
#define _stati64 _stat64
|
||||
#define _wstat _wstat64i32
|
||||
#define _wstat _wstat64
|
||||
#define _wstati64 _wstat64
|
||||
#endif
|
||||
|
||||
|
|
40
x86_64-gen.c
40
x86_64-gen.c
|
@ -623,20 +623,13 @@ static void gcall_or_jmp(int is_jmp)
|
|||
{
|
||||
int r;
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&
|
||||
((vtop->r & VT_SYM) || (vtop->c.i-4) == (int)(vtop->c.i-4))) {
|
||||
/* constant case */
|
||||
if (vtop->r & VT_SYM) {
|
||||
/* relocation case */
|
||||
((vtop->r & VT_SYM) && (vtop->c.i-4) == (int)(vtop->c.i-4))) {
|
||||
/* constant symbolic case -> simple relocation */
|
||||
#ifdef TCC_TARGET_PE
|
||||
greloca(cur_text_section, vtop->sym, ind + 1, R_X86_64_PC32, (int)(vtop->c.i-4));
|
||||
greloca(cur_text_section, vtop->sym, ind + 1, R_X86_64_PC32, (int)(vtop->c.i-4));
|
||||
#else
|
||||
greloca(cur_text_section, vtop->sym, ind + 1, R_X86_64_PLT32, (int)(vtop->c.i-4));
|
||||
greloca(cur_text_section, vtop->sym, ind + 1, R_X86_64_PLT32, (int)(vtop->c.i-4));
|
||||
#endif
|
||||
} else {
|
||||
/* put an empty PC32 relocation */
|
||||
put_elf_reloca(symtab_section, cur_text_section,
|
||||
ind + 1, R_X86_64_PC32, 0, (int)(vtop->c.i-4));
|
||||
}
|
||||
oad(0xe8 + is_jmp, 0); /* call/jmp im */
|
||||
} else {
|
||||
/* otherwise, indirect call */
|
||||
|
@ -877,19 +870,18 @@ void gfunc_call(int nb_args)
|
|||
if (is_sse_float(vtop->type.t)) {
|
||||
if (tcc_state->nosse)
|
||||
tcc_error("SSE disabled");
|
||||
gv(RC_XMM0); /* only use one float register */
|
||||
if (arg >= REGN) {
|
||||
gv(RC_XMM0);
|
||||
/* movq %xmm0, j*8(%rsp) */
|
||||
gen_offs_sp(0xd60f66, 0x100, arg*8);
|
||||
} else {
|
||||
/* movaps %xmm0, %xmmN */
|
||||
o(0x280f);
|
||||
o(0xc0 + (arg << 3));
|
||||
/* Load directly to xmmN register */
|
||||
gv(RC_XMM0 << arg);
|
||||
d = arg_prepare_reg(arg);
|
||||
/* mov %xmm0, %rxx */
|
||||
/* mov %xmmN, %rxx */
|
||||
o(0x66);
|
||||
orex(1,d,0, 0x7e0f);
|
||||
o(0xc0 + REG_VALUE(d));
|
||||
o(0xc0 + arg*8 + REG_VALUE(d));
|
||||
}
|
||||
} else {
|
||||
if (bt == VT_STRUCT) {
|
||||
|
@ -989,7 +981,8 @@ void gfunc_prolog(CType *func_type)
|
|||
if (reg_param_index < REGN) {
|
||||
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
|
||||
}
|
||||
sym_push(sym->v & ~SYM_FIELD, type, VT_LLOCAL | VT_LVAL, addr);
|
||||
sym_push(sym->v & ~SYM_FIELD, type,
|
||||
VT_LLOCAL | lvalue_type(type->t), addr);
|
||||
} else {
|
||||
if (reg_param_index < REGN) {
|
||||
/* save arguments passed by register */
|
||||
|
@ -1002,7 +995,8 @@ void gfunc_prolog(CType *func_type)
|
|||
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
|
||||
}
|
||||
}
|
||||
sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr);
|
||||
sym_push(sym->v & ~SYM_FIELD, type,
|
||||
VT_LOCAL | lvalue_type(type->t), addr);
|
||||
}
|
||||
addr += 8;
|
||||
reg_param_index++;
|
||||
|
@ -1568,7 +1562,7 @@ void gfunc_prolog(CType *func_type)
|
|||
default: break; /* nothing to be done for x86_64_mode_none */
|
||||
}
|
||||
sym_push(sym->v & ~SYM_FIELD, type,
|
||||
VT_LOCAL | VT_LVAL, param_addr);
|
||||
VT_LOCAL | lvalue_type(type->t), param_addr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
|
@ -1639,6 +1633,12 @@ void gfunc_epilog(void)
|
|||
|
||||
#endif /* not PE */
|
||||
|
||||
ST_FUNC void gen_fill_nops(int bytes)
|
||||
{
|
||||
while (bytes--)
|
||||
g(0x90);
|
||||
}
|
||||
|
||||
/* generate a jump to a label */
|
||||
int gjmp(int t)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue