Compare commits

...

42 Commits

Author SHA1 Message Date
Jan Nieuwenhuizen cb08f119ca
README: Update to add info about GNU Mes and bootstrappable-tinycc.
* README: Add header on GNU Mes and bootstrappable-tinycc.
2020-02-22 16:31:18 +01:00
Christian Jullien adfcf3b1dd Temporary remove zfunc test on ARM which generates a relocation error. Waiting for a fix 2018-12-20 08:44:47 +01:00
Christian Jullien 8494f2c318 Fix Thomas commit 776aa0c093 which failedto build on Windows. 2018-12-17 17:05:05 +01:00
Petr Skocik 070646b790 Recognize C11' _Alignof 2018-12-12 20:12:03 +01:00
Michael Matz c4787e3626 Fix type completion for array types as well
like qualifier merging the array sizes are merged as well
for the operands of ?:, and they must not statically influence
the types of decls.

Also fix some style problems, spaces aren't stinky :)
2018-11-30 23:43:30 +01:00
Petr Skocik f7779efe58 Fix incorrect one-sided void handling in ?:
Also make the case when one side in ?: is a ptr
and the other is neither a ptr nor a null-ptr constant
fail cleanly instead of segfaulting.
2018-11-29 13:40:48 +01:00
Petr Skocik 49dfb5755a Make fn designators in ?: decay to ptrs
This
    _Generic((__typeof(1?f:f)*){0}, void (**)(void): (void)0);
should compile like it does in gcc and clang.
2018-11-29 12:43:01 +01:00
Petr Skocik 3058d4116e Fix (Cexpr?struct1:struct2).member
Tcc used to fail this with `lvalue expected`
if the expression was a compile-time constant.
2018-11-29 10:26:56 +01:00
Petr Skocik 9d44b02a49 Fix the fix on type combining (e0012c2)
char **argv;
	    _Generic(argv, char**: (void)0);
	    _Generic(0?(char const*)0:argv[0], char const*: (void)0);
	    _Generic(argv, char**: (void)0);

    would fail because the type of argv would get modified by the
    ternary. Now allocate a separate type on the value stack to
    prevent this error.
2018-11-29 10:26:35 +01:00
Petr Skocik e0012c2767 Fix ptr type combining inside the ternary operator
Make it match http://port70.net/~nsz/c/c99/n1256.html#6.5.15p6
(or http://port70.net/~nsz/c/c11/n1570.html#6.5.15p6).
2018-11-28 10:36:58 +01:00
Petr Skocik c81116e29a Make casts lose top-level qualifiers
TODO: also make them lose lvalue status
2018-11-20 19:24:24 +01:00
Petr Skocik f85b1e393f Always allow ({ }) in the ctrl-expr of _Generic
tcc would reject e.g.,
    void f(){ struct {_Bool x:_Generic(({0;}),default:1);} my_x; }
with `expected constant`. This patch makes it accept it.

(The patch also makes tcc's _Generic a little more "generic" than that
 of gcc and clang in that that tcc now also accepts
`struct {_Bool x:_Generic(({0;}),default:1);} my_x;` in file scope
while gcc and clang don't, but I think there's no harm in that
and gcc and clang might as well accept it in filescope too, given
that they have no problem with
e.g., `/*filescope:*/int x=1, y=2, z=_Generic(x+y, int:3);`)
2018-11-13 12:51:16 +01:00
Petr Skocik 1e2e5671f7 Make tcc accept `-l lib` as well as `-llib`.
The POSIX spec for `c99` specifically favors the space-containing version
(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html)
and the `-l lib` syntax is also supported by gcc and clang.
2018-11-12 20:52:53 +01:00
Petr Skocik 314843ffc3 Fix how _Generic treats pointers to arrays.
_Generic should distinguish pointers to differently sized
arrays such as `int(*)[2]` and `int(*)[4]`.
2018-11-12 20:52:14 +01:00
Petr Skocik 73ca09ff32 Fix array pointer stringification.
`int (*)[4];` should not be stringified as `int *[4];`
Add stringification tests.
2018-11-12 20:50:51 +01:00
Petr Skocik 93a4ddfa63 Treat the - output as stdout. 2018-11-12 20:50:33 +01:00
Michael Matz 3b9c3fd186 Fix noreturn in main()
ISO C requires 'main' falling through the end without explicit
returns to implicitely return 0 (if declared as returning int).
2018-11-03 22:17:20 +01:00
Michael Matz 61ba9f2299 Check for void type in top-level decls
give an error message from the parser on things like "void x;" instead
of relying on codegen erroring out.
2018-08-03 22:51:35 +02:00
Michael Matz 22420ee1ee Fix misparsed type in presence of attributes
The type within the cast (int (__attribute__((foo)) *)(void))
was misparsed because of the presence of the attribute (parse_btype
prematurely concluded that (__attribute__() *) is a type.

Also see testcase.  This construct is used in sqlite it seems.
2018-07-28 18:55:54 +02:00
Jonathan Newman 0edbed1d52 Implement __attribute__((nodecorate))
Prevent any decoration that would otherwise affect an exported
PE function. For example, given the following:

__declspec(dllexport) __stdcall
int decorated(int arg) {
    return 0;
}

__declspec(dllexport) __stdcall __attribute__((nodecorate))
int undecorated(int arg) {
    return 0;
}

The following exported functions can now be seen in the DLL:
_decorated@4
undecorated

The attribute is recognised for all targets but only
affects PE codegen. I'm not sure whether this would be
useful for other targets; its intended use was to allow
the creation of a DLL matching an existing set of signatures.
2018-07-22 00:54:01 +01:00
Michael Matz d79caa9ff6 x86-64: Fix calls via absolute function pointers
linkers don't treat relocations using symindex 0 (undefined)
very well, it can't be misused as indicator for an absolute number.
Just don't bother with special casing this, rather emit an indirect
call/jump right away. ARM64 needs the same (and didn't handle
calls via constant absolute func pointers before).

The testcase as is doesn't fail without the patch, it actually
needs separate compilation (to -fPIC .o file, then to shared lib)
to fail.
2018-07-02 01:57:29 +02:00
Michael Matz 65c7f19deb Fix stored type of arguments on x86-64
the lvalue Syms for arguments didn't correctly reflect
their own types in all cases (a side-effect of the type being
stored in type->t and the ->r members (as VT_LVAL_xxx), so the below
used an int load (not a byte load) in the conditional.

extern void bar (void);
void foo (signed char c)
{
  signed char x = c;
  if (c)
    bar();
}
2018-06-24 20:12:51 +02:00
Andrey Gursky 91bdb5a4a3 Add linker's --export-dynamic flag alias
Since 9336fa7ae5 --export-dynamic is
supported. Add this conventional flag as alias.
2018-06-11 18:26:04 +02:00
grischka 8f6fcb709a misc fixes
misc fixes including:
- tcc.c: fix "tcc -vv" for libtcc1.a on win32/PE
- tccelf.c: fix a crash when GOT has no relocs (witn -nostdlib)
- tccelf.c: fix stab linkage for zero n_strx
- tccgen.c: fix stdcall decoration for array parameters
    int __stdcall func(char buf[10]) is _func@4 (was _func@12)
- tccgen.c: fix static variables with nocode/nodata_wanted
    see tests2/96_nodata_wanted.c
- tccrun.c: align sections using sh_addralign (for reliable function_alignment)
- tests2/Makefile sort 100 after 99
- win32/include/sys/stat.h fix _stat and _wstat
- x86_64-gen.c: win64/gfunc_call: fix a bug with xmmN register args
    previously overwrote valid other xmmN registers eventually
2018-06-01 12:52:01 +02:00
grischka 2b155a8c16 tccgen.c: fix warning for incompatible struct- and function pointers
see tests2/60_errors_and_warnings.c
2018-06-01 12:41:21 +02:00
grischka ace1225492 tcc_add_file(): preserve s->filetype
This is supposed to fix a bug where libtcc eventually was trying to
compile libtcc1.a as C source code.

Anyway, there is now only two functions that refer to s->filetype,
tcc_add_file() and tcc_add_library().
2018-06-01 12:41:17 +02:00
Michael Matz 671dcace82 Implement function alignment via attributes
which requires being able to emit an arbitrary number of NOP
instructions, which is also implemented here.  For x86 we
could emit other sequences but these are the easiest.
2018-04-06 23:02:42 +02:00
Petr Skocik ef668aae1e Don't fail on const/restrict/static/* inside []
This patch makes tcc ignore them.

Normally (as per the C standard), They should
be only applicable inside parameter arrays
and affect (const/restrict) the pointer the
array gets converted to.

[matz: fix formatting, add volatile handling, add testcase,
add comment about above deficiency]
2018-04-01 00:48:09 +02:00
Petr Skocik d6d3cf00ec patch type_to_str to handle complex function-ptr decls better
Code like:

    #include <signal.h>
    int main() { _Generic(signal, int: 0); }

should fail with
    error: type 'extern void (*(int, void (*)(int)))(int)' does not match any association
not
    error: type 'extern void *(int)(int, void *(int))' does not match any association

[matz: fix formatting, fix function-to-pointer decay for operands of
_Generic, add testcase for this]
2018-04-01 00:38:11 +02:00
Michael Matz f0a25ca263 Fix shortening casts of long long
see added testcase.
2018-03-31 21:52:20 +02:00
Thomas Preud'homme c41caac02d Select VFP if triplet is arm-linux-gnueabihf
A target triplet of arm-linux-gnueabihf indicates that the compiler
should use the VFP variant of the calling convention which as its name
implies requires VFP. This commit enables VFP when triplet is
arm-linux-gnueabihf.
2018-03-15 23:05:25 +00:00
Thomas Preud'homme e76058c478 Remove asm-c-connect-sep in tests clean target 2018-03-09 20:10:36 +00:00
Thomas Preud'homme 776aa0c093 Prevent dead code on !x86 in prepare_dynamic_rel
In prepare_dynamic_rel() on non x86 targets the count++ statements
appear before any case label and are therefore dead code. This triggers
build failure when building with -Werror. This patch adds an extra guard
around all the x86 case labels and their associated action, leaving just
the default case label for non x86 targets which builds fine.

Origin: vendor
Forwarded: no
Last-Updated: 2018-02-24
2018-02-24 19:35:15 +00:00
Michael Matz 3e6515b64f Add make testspp.all/testspp.20
like we have already make tests2.XX.
2018-01-05 02:19:26 +01:00
Michael Matz 7ad2cf8d68 Code suppression fixes
See adjusted testcase, a lone break; in a do while loop was
incorrectly disabling the exit test.
2018-01-05 02:19:26 +01:00
Michael Matz 8294285d8f Don't emit applied .rel sections
for a final link we shouldn't emit relocation sections that are applied
already.  For now we need to emit ALLOCed .rel sections as they contain
dynamic relocs, they should be put into their own (new) section instead.
2018-01-01 05:29:46 +01:00
foobar 988e2ff7fe fix debug info with musl on x86_64
http://lists.nongnu.org/archive/html/tinycc-devel/2017-12/msg00031.html
2017-12-28 15:05:27 +00:00
Michael Matz 414b224efa Accept more floating point constant expressions
the rules for constant expressions in static initializers are more
relaxed than for integer constant expressions.  We need to accept
0.0/0.0 in static initializers (in non-static initializers the potential
exceptions need to be raised though, so no translation-time calculation
then).
2017-12-25 12:44:29 +01:00
Michael Matz 9e47b18229 Make type of __nan__ __inf__ and __snan__ be float
so that those builtins can be used directly for the C99 NAN and
INFINITY math.h macros.
2017-12-24 13:16:09 +01:00
Michael Matz 3b27b3b1d1 Fix -pthread in a different way 2017-12-23 14:49:07 +01:00
Michael Matz 1b1b270f1e Revert "Fix -pthread option handling"
This reverts commit 3f8225509b.
It fixes the linking case but introduces an ugly error with -c
about adding a library.  To fix both uses the old code structure is
better.
2017-12-23 14:46:27 +01:00
Michael Matz 3f8225509b Fix -pthread option handling
adding -pthread confused option parsing as the number of file counting
came out wrong.  Simplify and fit it, can be handled purely within
option parsing, no need for a state flag.
2017-12-23 14:14:57 +01:00
36 changed files with 781 additions and 208 deletions

View File

@ -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
View File

@ -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
-----------------------------------------------------------------------

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

2
configure vendored
View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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
View File

@ -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
View File

@ -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);

View File

@ -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
View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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")

View File

@ -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 $@

View File

@ -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

View File

@ -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)

View File

@ -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));

View File

@ -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;
}

View File

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -0,0 +1,2 @@
42
42

View File

@ -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;
}

View File

@ -16,3 +16,8 @@ once3
twice3
caseok
caseok2
g=1
check 1
g=2
check 2
g=3

View File

@ -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;
}

View File

@ -10,4 +10,5 @@
3
4
long
1
1
3

View File

@ -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

View File

@ -21,3 +21,6 @@ size of data/text:
[test_data_suppression_on]
size of data/text:
zero/zero
[test_static_data]
a = 1

View File

@ -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

View File

@ -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

View File

@ -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)
{