tccgen.c:gv() when loading long long from lvalue, before
was saving all registers which caused problems in the arm
function call register parameter preparation, as with
void foo(long long y, int x);
int main(void)
{
unsigned int *xx[1], x;
unsigned long long *yy[1], y;
foo(**yy, **xx);
return 0;
}
Now only the modified register is saved if necessary,
as in this case where it is used to store the result
of the post-inc:
long long *p, v, **pp;
v = 1;
p = &v;
p[0]++;
printf("another long long spill test : %lld\n", *p);
i386-gen.c :
- found a similar problem with TOK_UMULL caused by the
vstack juggle in tccgen:gen_opl()
(bug seen only when using EBX as 4th register)
On 2016-08-11 09:24 +0100, Balazs Kezes wrote:
> I think it's just that that copy_params() never restores the spilled
> registers. Maybe it needs some extra code at the end to see if any
> parameters have been spilled to stack and then restore them?
I've spent some time on this and I've found an alternative solution.
Although I'm not entirely sure about it but I've attached a patch
nevertheless.
And while poking at that I've found another problem affecting the
unsigned long long division on arm and I've attached a patch for that
too.
More details in the patches themselves. Please review and consider them
for merging! Thank you!
--
Balazs
[PATCH 1/2] Fix slow unsigned long long division on ARM
The macro AEABI_UXDIVMOD expands to this bit:
#define AEABI_UXDIVMOD(name,type, rettype, typemacro) \
...
while (num >= den) { \
...
while ((q << 1) * den <= num && q * den <= typemacro ## _MAX / 2) \
q <<= 1; \
...
With the current ULONG_MAX version the inner loop goes only until 4
billion so the outer loop will progress very slowly if num is large.
With ULLONG_MAX the inner loop works as expected. The current version is
probably a result of a typo.
The following bash snippet demonstrates the bug:
$ uname -a
Linux eper 4.4.16-2-ARCH #1 Wed Aug 10 20:03:13 MDT 2016 armv6l GNU/Linux
$ cat div.c
int printf(const char *, ...);
int main(void) {
unsigned long long num, denom;
num = 12345678901234567ULL;
denom = 7;
printf("%lld\n", num / denom);
return 0;
}
$ time tcc -run div.c
1763668414462081
real 0m16.291s
user 0m15.860s
sys 0m0.020s
[PATCH 2/2] Fix long long dereference during argument passing on ARMv6
For some reason the code spills the register to the stack. copy_params
in arm-gen.c doesn't expect this so bad code is generated. It's not
entirely clear why the saving part is necessary. It was added in commit
59c35638 with the comment "fixed long long code gen bug" with no further
clarification. Given that tcctest.c passes without this, maybe it's no
longer needed? Let's remove it.
Also add a new testcase just for this. After I've managed to make the
tests compile on a raspberry pi, I get the following diff without this
patch:
--- test.ref 2016-08-22 22:12:43.380000000 +0100
+++ test.out3 2016-08-22 22:12:49.990000000 +0100
@@ -499,7 +499,7 @@
2
1 0 1 0
4886718345
-shift: 9 9 9312
+shift: 291 291 291
shiftc: 36 36 2328
shiftc: 0 0 9998683865088
manyarg_test:
More discussion on this thread:
https://lists.nongnu.org/archive/html/tinycc-devel/2016-08/msg00004.html
fixes 5c35ba66c5
Implementation was consistent within tcc but incompatible
with the ABI (for example library functions vprintf etc)
Also:
- tccpp.c/get_tok_str() : avoid "unknown format "%llu" warning
- x86_64_gen.c/gen_vla_alloc() : fix vstack leak
* fix some macro expansion issues
* add some pp tests in tests/pp
* improved tcc -E output for better diff'ability
* remove -dD feature (quirky code, exotic feature,
didn't work well)
Based partially on ideas / researches from PipCet
Some issues remain with VA_ARGS macros (if used in a
rather tricky way).
Also, to keep it simple, the pp doesn't automtically
add any extra spaces to separate tokens which otherwise
would form wrong tokens if re-read from tcc -E output
(such as '+' '=') GCC does that, other compilers don't.
* cleanups
- #line 01 "file" / # 01 "file" processing
- #pragma comment(lib,"foo")
- tcc -E: forward some pragmas to output (pack, comment(lib))
- fix macro parameter list parsing mess from
a3fc543459a715d7143d
(some coffee might help, next time ;)
- introduce TOK_PPSTR - to have character constants as
written in the file (similar to TOK_PPNUM)
- allow '\' appear in macros
- new functions begin/end_macro to:
- fix switching macro levels during expansion
- allow unget_tok to unget more than one tok
- slight speedup by using bitflags in isidnum_table
Also:
- x86_64.c : fix decl after statements
- i386-gen,c : fix a vstack leak with VLA on windows
- configure/Makefile : build on windows (MSYS) was broken
- tcc_warning: fflush stderr to keep output order (win32)
From: Matteo Cypriani <mcy@lm7.fr>
Date: Fri, 5 Sep 2014 23:22:56 -0400
Subject: Disable floating-point test for ARM soft-float
tcc is not yet capable of doing softfloat floating-point operations on
ARM, therefore we disable this test for these platforms. Note that tcc
displays a warning to warn ARM users about this limitation
(debian)
This provides a simple implementation of alloca for ARM (and enables
the associated testcase). As tcc for ARM doesn't contain an assembler,
we'll have to resort using gcc for compiling it.
Result of float to unsigned integer conversion is undefined if float is
negative. This commit take the absolute value of the float before doing
the conversion to unsigned integer and add more float to integer
conversion test.
negate(x) is subtract(-0,x), not subtract(+0,x), which makes
a difference with signed zeros. Also +x was expressed as x+0,
in order for the integer promotions to happen, but also mangles signed
zeros, so just don't do that with floating types.
I really should do this when less tired; I keep breaking one platform
while fixing another. I've also fixed some Windows issues with tcctest
since Windows printf() uses different format flags to those on Linux,
and removed some conditional compilation tests in tcctest since they
now should work.
Modified tcctest.c so that it uses 'double' in place of 'long double'
with MinGW since this is what TCC does, and what Visual C++ does. Added
an option -norunsrc to tcc to allow argv[0] to be set independently of
the compiled source when using tcc -run, which allows tests that rely on
the value of argv[0] to work in out-of-tree builds.
Also added Makefile rules to automatically update out-of-tree build
Makefiles when in-tree Makefiles have changed.
tests:
- add "hello" to test first basic compilation to file/memory
- add "more" test (tests2 suite)
- remove some tests
tests2:
- move into tests dir
- Convert some files from DOS to unix LF
- remove 2>&1 redirection
win32:
- tccrun.c: modify exception filter to exit correctly (needed for btest)
- tcctest.c: exclude weak_test() (feature does not exist on win32)
gcc fails the builtin_frame_address test on ARM so we disable it. As a
consequence, the diff between gcc and tcc's output is unecessarily
bigger. Given the big size of the diff currently, this doesn't make a
big difference but may allow to detect a regression in tcc's
implementation of builtin_frame_address.
Continuing d6072d37 (Add __builtin_frame_address(0)) implement
__builtin_frame_address for levels greater than zero, in order for
tinycc to be able to compile its own lib/bcheck.c after
cffb7af9 (lib/bcheck: Prevent __bound_local_new / __bound_local_delete
from being miscompiled).
I'm new to the internals, and used the most simple way to do it.
Generated code is not very good for levels >= 2, compare
gcc tcc
level=0 mov %ebp,%eax lea 0x0(%ebp),%eax
level=1 mov 0x0(%ebp),%eax mov 0x0(%ebp),%eax
level=2 mov 0x0(%ebp),%eax mov 0x0(%ebp),%eax
mov (%eax),%eax mov %eax,-0x10(%ebp)
mov -0x10(%ebp),%eax
mov (%eax),%eax
level=3 mov 0x0(%ebp),%eax mov 0x0(%ebp),%eax
mov (%eax),%eax mov (%eax),%ecx
mov (%eax),%eax mov (%ecx),%eax
But this is still an improvement and for bcheck we need level=1 for
which the code is good.
For the tests I had to force gcc use -O0 to not inline the functions.
And -fno-omit-frame-pointer just in case.
If someone knows how to improve the generated code - help is
appreciated.
Thanks,
Kirill
Cc: Michael Matz <matz@suse.de>
Cc: Shinichiro Hamaji <shinichiro.hamaji@gmail.com>
Loads of VT_LLOCAL values (which effectively represent saved
addresses of lvalues) were done in VT_INT type, loosing the upper
32 bits. Needs to be done in VT_PTR type.
Sometimes the result of a comparison is not directly used in a jump,
but in arithmetic or further comparisons. If those further things
do a vswap() with the VT_CMP as current top, and then generate
instructions for the new top, this most probably destroys the flags
(e.g. if it's a bitfield load like in the example).
vswap() must do the same like vsetc() and not allow VT_CMP vtops
to be moved down.
This matters when sizeof is directly used in arithmetic,
ala "uintptr_t t; t &= -sizeof(long)" (for alignment). When sizeof
isn't size_t (as it's specified to be) this masking will truncate
the high bits of the uintptr_t object (if uintptr_t is larger than
uint).
This needs to be accepted:
typedef int foo;
void f (void) { foo: return; }
namespaces for labels and types are different. The problem is that
the block parser always tries to find a decl first and that routine
doesn't peek enough to detect this case. Needs some adjustments
to unget_tok() so that we can call it even when we already called
it once, but next() didn't come around restoring the buffer yet.
(It lazily does so not when the buffer becomes empty, but rather
when the next call detects that the buffer is empty, i.e. it requires
two next() calls until the unget buffer gets switched back).
When offsetted addresses of global non-static data are computed
multiple times in the same statement the x86_64 backend uses
gen_gotpcrel with offset, which implements an add insn on the
register given. load() uses the R member of the to-be-loaded
value, which doesn't yet have a reg assigned in all cases.
So use the register we're supposed to load the value into as
that register.
The first loop setting up struct arguments must not remove
elements from the vstack (via vtop--), as gen_reg needs them to
potentially evict some argument still held in registers to stack.
Swapping the arg in question to top (and back to its place) also
simplifies the vstore call itself, as not funny save/restore
or some "non-existing" stack elements need to be done.
Generally for a stack a vop-- operation conceptually clobbers
that element, so further references to it aren't allowed anymore.
Removes a premature optimization of char/short loads
rewriting the source type. It did so also for bitfield
loads, thereby removing all the shifts/maskings.
(cond ? 0 : ptr)->member wasn't handled correctly. If one arm
is a null pointer constant (which also can be a pointer) the result
type is that of the other arm.
array_test is declared and called with no parameters but defined with
one parameter. Compilation succeed (definition is after the use so the
compiler consider the declaration) as well as link (the function exist
and has the right name) but running the test segfault on i386 platforms.
This patch moves the parameter to local variable. If the intention was
to call it with an array parameter then feel free to fix it again.
* Disable C99 VLA detection when alloca is unavailable and protect the
new reference to TOK_alloca in decl_initializer in order to compile
and run for architecture without working alloca.
Not all code of C99 VLA is commented as it would required many ifdef
stanza. Just the detection is commented so that VT_VLA is never set
any type and the C99 VLA code is compiled but never called. However
vpush_global_sym(&func_old_type, TOK_alloca) in decl_initializer needs
to be protected by an ifdef stanza as well because it uses TOK_alloca.
* include alloca and C99 VLA tests according to availability of
TOK_alloca instead of relying on the current architecture
Implement C99 Variable Length Arrays in tinycc:
- Support VLA with multiple level (nested vla)
- Update documentation with regards to VT_VLA
- Add a testsuite in tcctest.c
- add __builtin_va_arg_types to check how arguments were passed
- move most code of stdarg into libtcc1.c
- remove __builtin_malloc and __builtin_free
- add a test case based on the bug report
(http://www.mail-archive.com/tinycc-devel@nongnu.org/msg03036.html)
- Fix a wrong calculation for size of struct
- Handle cases where struct size isn't multple of 8
- Recover vstack after memcpy for pushing struct
- Add a float parameter for struct_assign_test1 to check SSE alignment
(Because GNU's alloca.h unconditionally #undef's alloca)
Also, remove gcc specific sections in headers. and
instead change tests such that gcc does not use them.