Hello,
My company is looking for a tiny compiler to embed in its product, in order to
build some generated source code. As TCC seems to fit our needs, I have
launched our compiler validation test base on it. I actually found several
bugs, which are not blocking for our purposes, but may interest you. Here's a
list of the bugs followed by reproduction steps, in the form of source code to
compile. Most of them were first tested against gcc and worked well with it.
->Tcc refuses to compile bitfield assignment when a side effect occurs on a
double.
Reproduction steps:
struct {
int field : 8;
int field2 : 8;
unsigned int ufield : 8;
unsigned int ufield2 : 8;
} bit;
double d1;
void main() {
bit.field = ++d1;
}
->In some cases, Tcc refuses to compile codes defining types with the same
names but in different scopes.
Reproduction steps:
int f(struct xyz { int xyz; } *xyz)
{
return xyz->xyz;
}
void main() {
struct xyz { int xyz; } buf;
}
->In some cases, Tcc refuses to compile codes defining variables with the same
names but in different scopes.
Reproduction steps:
#include <assert.h>
float float_foo = 2.0;
void test() {
static float foo = 5.0;
{
extern int foo;
foo = 15;
}
float_foo = foo;
}
int foo = 1;
void main() {
assert(foo == 1);
assert(float_foo == 2.0);
test();
assert(foo == 15);
assert(float_foo == 5.0);
}
->Tcc refuses to compile codes with a static initializer containing boolean
operation on doubles.
Reproduction steps:
static int t5 = 1 && 2.1;
->Tcc returns a strange compilation error "missing ;" when the code contains
extern having initializer.
Reproduction steps:
extern int i4 = 10;
->Tcc doesn't support correctly typedefs to incomplete types.
Reproduction steps:
typedef int table[];
table one = { 1 };
table two = { 1, 2 }; // error: index too large
->Tcc doesn't fetch float argument in old style definition as double, causing
wrong values to be passed.
Reproduction steps:
int bug(f, d)
float f;
int d;
{
return d == 42;
}
void main()
{
assert(bug(3.14, 42) == 1);
}
->Some complex sizeof are not computed correctly by Tcc, returning wrong
results.
Reproduction steps:
int x = 0;
char a[100];
char b[50];
void main()
{
assert(sizeof(x == 0 ? a : b) == sizeof(char *));
}
->Problem with implicit casts on bitfields. Assigning a bitfield to an unsigned
short or unsigned char without an explicit cast leads to a wrong value.
Reproduction steps:
#include <assert.h>
struct bit {
int dummy;
int f0 : 7;
int f1 : 13;
int f5 : 32 - 2;
int f3 : 32 - 1;
int f4 : 32 ;
};
int main() {
struct bit bit;
unsigned short ush0, ush1, ush3, ush4;
unsigned char uch0, uch1, uch3, uch4;
unsigned int mask = 0xffffU;
bit.f0 = 63;
bit.f1 = 4095;
bit.f3 = 1023441823;
bit.f4 = 2113483647;
/* Those assignments give wrong results */
ush0 = bit.f0;
ush1 = bit.f1;
ush3 = bit.f3;
ush4 = bit.f4;
assert(ush0 == 63);
assert(ush1 == 4095);
assert(ush3 == (bit.f3 & mask));
assert(ush4 == (bit.f4 & mask));
mask = 0xffU;
/* Those assignments give wrong results */
uch0 = bit.f0;
uch1 = bit.f1;
uch3 = bit.f3;
uch4 = bit.f4;
assert(uch0 == 63);
assert(uch1 == (bit.f1 & mask));
assert(uch3 == (bit.f3 & mask));
assert(uch4 == (bit.f4 & mask));
return 0;
}
->Problem with promotion on bitfields. Some integral promotion are wrong with
bitfields.
Reproduction steps:
#include <assert.h>
int main() {
struct {
unsigned int ufield : 7;
int field : 7;
int field2 : 7;
} bit;
int i1;
short sh1;
char ch1;
bit.ufield = 10;
i1 = -11;
assert((bit.ufield + i1) < 0);
sh1 = -11;
assert((bit.ufield + sh1) < 0);
bit.field = -11;
assert((bit.ufield + bit.field) < 0);
ch1 = -11;
assert((bit.ufield + ch1) < 0);
return 0;
}
->When declaring local extern variables, the compiler should look for global
variables before looking for local ones.
Reproduction steps:
int v = 3;
void main()
{
int v = 4;
{
extern int v;
assert(v == 3);
}
}
->When returning an element with an implicit cast to float, the value is wrong.
Reproduction steps:
float f_dbl(double dbl)
{
return dbl;
}
float f_ui(unsigned int ui)
{
return ui;
}
void main() {
float f;
double d;
unsigned int ui;
d = -5.9;
f = d;;
assert(f == f_dbl(d));
ui = 0;
ui = ~ui;
f = ui;
assert(f == f_ui(ui));
}
->The padding is not generated correctly when initializing unions statically.
Reproduction steps:
union {
char c;
double d;
} u[2] = { 'a', 'b' };
void main()
{
u[0].d = 3.4;
assert(u[1].c == 'b');
}
->Tcc refuses to compile code when an overflow occurs on static initialization.
A warning should only be issued.
Reproduction steps:
int x = 123456789012345678901234567;
-> A memcpy or memset is sometimes introduced for functions returning a struct
and causes compilation errors is string.h is included afterward.
Reproduction steps:
#include <assert.h>
struct s {
int a;
};
struct s test() {
struct s ms;
ms.a = 1;
return ms;
}
int main() {
struct s ls;
ls = test();
assert(ls.a == 1);
return 0;
}
#include <string.h>
-> Incomplete types lead to an error if not defined, whereas they should be
assumed as having only one element.
Reproduction steps:
char test[];
int main() {
test[0] = 0;
}
-> Computations on very big values stored in long doubles gives wrong results.
#include <float.h>
#include <assert.h>
#define X 5.9486574767861588254287966331400356538172e4931L
int main() {
long double test;
int exponent = 0;
test = X;
test *= 2.0L;
assert(test > LDBL_MAX);
return 0;
}
Hope this helps!
Regards,
Yann Bourrigault.
_______________________________________________
Tinycc-devel mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/tinycc-devel