tinycc-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Tinycc-devel] Bugs found in TCC


From: grischka
Subject: Re: [Tinycc-devel] Bugs found in TCC
Date: Mon, 25 Jan 2010 17:08:38 +0100
User-agent: Thunderbird 2.0.0.23 (Windows/20090812)

Yann Bourrigault wrote:

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

Thanks for that.

Just on a note, there are two that might work correctly with current
master branch (http://repo.or.cz/w/tinycc.git)

That is:
> ->The padding is not generated correctly when initializing unions
> statically.

and:
> -> A memcpy or memset is sometimes introduced for functions returning
> a struct and causes compilation errors is string.h is included afterward.


Also, I'm confused by this one ("unsigned + signed" should be unsigned, no?)
Maybe someone else can comment:

> ->Problem with promotion on bitfields. Some integral promotion are wrong
> with bitfields.
>
> 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;
> }

For those who like to play with it, I attached the test cases in a more
runnable form. (run "tcc -D T1..17 -run bugs.c")

--- grischka
#include <stdio.h>
#include <stdlib.h>
int my_assert(const char *msg, const char *file, int line)
{
    fprintf(stderr, "%s:%d: error: assert (%s)\n", file, line, msg);
    exit(-1);
    return 0;
}
#define assert(_Expr) ((_Expr) || my_assert(#_Expr,__FILE__,__LINE__))

/*---------------------------------------------------------------------*/
/*
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.
*/

/*---------------------------------------------------------------------*/
#ifdef T1
/*
->Tcc refuses to compile bitfield assignment when a side effect occurs on a 
double.
*/

struct
{
    int field : 8;
    int field2 : 8;
    unsigned int ufield : 8;
    unsigned int ufield2 : 8;
} bit;

double d1;

int main()
{
    bit.field = ++d1;
    return 0;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T2
/*
->In some cases, Tcc refuses to compile codes defining types with the same names
but in different scopes.
*/

int f(struct xyz { int xyz; } *xyz)
{
    return xyz->xyz;
}

int main()
{
    struct xyz { int xyz; } buf;
    return 0;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T3
/*
->In some cases, Tcc refuses to compile codes defining variables with the same
names but in different scopes.
*/

float float_foo = 2.0;

void test()
{
    static float foo = 5.0;
    {
        extern int foo;
        foo = 15;
    }
    float_foo = foo;
}

int foo = 1;

int main()
{
    assert(foo == 1);
    assert(float_foo == 2.0);
    test();
    assert(foo == 15);
    assert(float_foo == 5.0);
    return 0;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T4
/*
->Tcc refuses to compile codes with a static initializer containing boolean
operation on doubles.
*/

static int t5 = 1 && 2.1;

int main() { return 0; }

#endif

/*---------------------------------------------------------------------*/
#ifdef T5
/*
->Tcc returns a strange compilation error "missing ;" when the code contains
extern having initializer.
*/
extern int i4 = 10;

int main() { return 0; }
#endif

/*---------------------------------------------------------------------*/
#ifdef T6
/*
->Tcc doesn't support correctly typedefs to incomplete types.
*/
typedef int table[];

table one = { 1 };
table two = { 1, 2 }; // error: index too large

int main() { return 0; }
#endif

/*---------------------------------------------------------------------*/
#ifdef T7
/*
->Tcc doesn't fetch float argument in old style definition as double, causing
wrong values to be passed.
*/

int bug(f, d)
    float f;
    int d;
{
    return d == 42;
}

int main()
{
    assert(bug(3.14, 42) == 1);
    return 0;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T8
/*
->Some complex sizeof are not computed correctly by Tcc, returning wrong 
results.
*/

int x = 0;
char a[100];
char b[50];
int main()
{
    assert(sizeof(x == 0 ? a : b) == sizeof(char *));
    return 0;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T9
/*
->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.
*/

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;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T10
/*
->Problem with promotion on bitfields. Some integral promotion are wrong with 
bitfields.
*/

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;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T11
/*
->When declaring local extern variables, the compiler should look for global
variables before looking for local ones.
*/

int v = 3;

int main()
{
    int v = 4;
    {
        extern int v;
        assert(v == 3);
    }
    return 0;
}

#endif

/*---------------------------------------------------------------------*/
#ifdef T12
/*
->When returning an element with an implicit cast to float, the value is wrong.
*/

float f_dbl(double dbl)
{
    return dbl;
}

float f_ui(unsigned int ui)
{
    return ui;
}

int 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));
    return 0;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T13
/*
->The padding is not generated correctly when initializing unions statically.
*/

union
{
    char c;
    double d;
} u[2] = { 'a', 'b' };

int main()
{
   u[0].d = 3.4;
   assert(u[1].c == 'b');
   return 0;
}
#endif

/*---------------------------------------------------------------------*/
#ifdef T14
/*
->Tcc refuses to compile code when an overflow occurs on static initialization.
A warning should only be issued.
*/
int x = 123456789012345678901234567;

int main() { return 0;}
#endif

/*---------------------------------------------------------------------*/
#ifdef T15
/*
-> A memcpy or memset is sometimes introduced for functions returning a struct
and causes compilation errors is string.h is included afterward.
*/

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>

#endif

/*---------------------------------------------------------------------*/
#ifdef T16
/*
-> Incomplete types lead to an error if not defined, whereas they should be
assumed as having only one element.
*/

char test[];

int main()
{
    test[0] = 0;
    return 0;
}

#endif

/*---------------------------------------------------------------------*/
#ifdef T17
/*
-> Computations on very big values stored in long doubles gives wrong results.
*/

#include <float.h>

#define X 5.9486574767861588254287966331400356538172e4931L

int main()
{
    long double test;
    int exponent = 0;

    test = X;
    test *= 2.0L;
    assert(test > LDBL_MAX);
    return 0;
}

#endif

/*---------------------------------------------------------------------*/
#ifdef T18
int main()
{
    struct
    {
        int a:3;
        unsigned int b:3;
    } s;

    int i;
    unsigned u = 10;

    s.a = 3;
    s.b = 3;

    assert(s.a - 4 < 0);
    assert(s.b - 4 < 0);
    return 0;
}

#endif

/*---------------------------------------------------------------------*/
/*
Hope this helps!

Regards,

Yann Bourrigault.
*/

reply via email to

[Prev in Thread] Current Thread [Next in Thread]