Hi,

I was bored the last weekend and stumbled over tinycc, tried compiling GCC with it, which breaks in multiple ways, sometimes not parsing stuff, sometimes miscompiling it, and thought about hacking some other compiler than GCC :) I just pushed nine patches to mob to fix all the problems I encountered (also attached for reference).

I've added testcases for all the issues to tcctest.c.

I can now build current GCC (well, r186469 plus a modification, see below) with tinycc on x86_64 (all language frontends written in C, i.e. c, c++, objc, objc++ and fortran), and that so built GCC is able to compile all its own target libraries.

The nice thing is that I even found a real bug in GCC (fixing it is required to make it work with tinycc on x86_64), which I'm going to apply upstream later, but for completeness I attach it here too in case anyone wants to play with it right now (fix-diag-vaend.diff) :)


Have fun,
Michael.
From 6471ec0a2bd523edd4bbc79277a33d0b853940fa Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Sat, 14 Apr 2012 23:50:21 +0200
Subject: [PATCH 1/9] Fix conversion in a?0:ptr.

(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.
---
 tccgen.c        |   19 ++++++++++++++-----
 tests/tcctest.c |   13 +++++++++++++
 2 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/tccgen.c b/tccgen.c
index 5aa21c6..b0cec4a 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -1489,7 +1489,8 @@ static inline int is_null_pointer(SValue *p)
     if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
         return 0;
     return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) ||
-        ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0);
+        ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0) ||
+	((p->type.t & VT_BTYPE) == VT_PTR && p->c.ptr == 0);
 }
 
 static inline int is_integer_btype(int bt)
@@ -4116,14 +4117,22 @@ static void expr_cond(void)
                     (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED))
                     type.t |= VT_UNSIGNED;
             } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
-                /* XXX: test pointer compatibility */
-                type = type1;
+		/* 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 = type1;
+                type = bt1 == VT_FUNC ? type1 : type2;
             } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
                 /* XXX: test structure compatibility */
-                type = type1;
+                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;
diff --git a/tests/tcctest.c b/tests/tcctest.c
index f0d82cf..723adc1 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -2486,3 +2486,16 @@ void const_warn_test(void)
 {
     const_func(1);
 }
+
+struct condstruct {
+  int i;
+};
+
+int getme (struct condstruct *s, int i)
+{
+  int i1 = (i == 0 ? 0 : s)->i;
+  int i2 = (i == 0 ? s : 0)->i;
+  int i3 = (i == 0 ? (void*)0 : s)->i;
+  int i4 = (i == 0 ? s : (void*)0)->i;
+  return i1 + i2 + i3 + i4;
+}
-- 
1.7.3.4

From 5c0a2366a3bb5bdeb3b3617389d4584375d5bfbc Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Sun, 15 Apr 2012 01:06:46 +0200
Subject: [PATCH 2/9] Fix bitfield loads into char/short.

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.
---
 tccgen.c        |    5 +++--
 tests/tcctest.c |    5 +++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/tccgen.c b/tccgen.c
index b0cec4a..2c759e1 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -2320,8 +2320,9 @@ ST_FUNC void vstore(void)
     ft = vtop[-1].type.t;
     sbt = vtop->type.t & VT_BTYPE;
     dbt = ft & VT_BTYPE;
-    if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
-        (sbt == VT_INT && dbt == VT_SHORT)) {
+    if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
+         (sbt == VT_INT && dbt == VT_SHORT))
+	&& !(vtop->type.t & VT_BITFIELD)) {
         /* optimize char/short casts */
         delayed_cast = VT_MUSTCAST;
         vtop->type.t = ft & (VT_TYPE & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)));
diff --git a/tests/tcctest.c b/tests/tcctest.c
index 723adc1..afb0825 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -1461,6 +1461,8 @@ void c99_bool_test(void)
 void bitfield_test(void)
 {
     int a;
+    short sa;
+    unsigned char ca;
     struct sbf1 {
         int f1 : 3;
         int : 2;
@@ -1482,6 +1484,9 @@ void bitfield_test(void)
     st1.f5++;
     printf("%d %d %d %d %d\n",
            st1.f1, st1.f2, st1.f3, st1.f4, st1.f5);
+    sa = st1.f5;
+    ca = st1.f5;
+    printf("%d %d\n", sa, ca);
 
     st1.f1 = 7;
     if (st1.f1 == -1) 
-- 
1.7.3.4

From 86ac6b9beec1c142867b53350006c02e20e4a7ef Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Sun, 15 Apr 2012 05:12:43 +0200
Subject: [PATCH 3/9] x86_64: Fix indirection in struct paramaters

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.
---
 tests/tcctest.c |   29 +++++++++++++++++------------
 x86_64-gen.c    |   39 +++++++++++++++++++++++----------------
 2 files changed, 40 insertions(+), 28 deletions(-)

diff --git a/tests/tcctest.c b/tests/tcctest.c
index afb0825..fcd9822 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -1122,25 +1122,30 @@ struct structa1 struct_assign_test2(struct structa1 s1, int t)
 
 void struct_assign_test(void)
 {
-    struct structa1 lsta1, lsta2;
+    struct S {
+      struct structa1 lsta1, lsta2;
+      int i;
+    } s, *ps;
     
+    ps = &s;
+    ps->i = 4;
 #if 0
     printf("struct_assign_test:\n");
 
-    lsta1.f1 = 1;
-    lsta1.f2 = 2;
-    printf("%d %d\n", lsta1.f1, lsta1.f2);
-    lsta2 = lsta1;
-    printf("%d %d\n", lsta2.f1, lsta2.f2);
+    s.lsta1.f1 = 1;
+    s.lsta1.f2 = 2;
+    printf("%d %d\n", s.lsta1.f1, s.lsta1.f2);
+    s.lsta2 = s.lsta1;
+    printf("%d %d\n", s.lsta2.f1, s.lsta2.f2);
 #else
-    lsta2.f1 = 1;
-    lsta2.f2 = 2;
+    s.lsta2.f1 = 1;
+    s.lsta2.f2 = 2;
 #endif
-    struct_assign_test1(lsta2, 3, 4.5);
+    struct_assign_test1(ps->lsta2, 3, 4.5);
     
-    printf("before call: %d %d\n", lsta2.f1, lsta2.f2);
-    lsta2 = struct_assign_test2(lsta2, 4);
-    printf("after call: %d %d\n", lsta2.f1, lsta2.f2);
+    printf("before call: %d %d\n", s.lsta2.f1, s.lsta2.f2);
+    ps->lsta2 = struct_assign_test2(ps->lsta2, ps->i);
+    printf("after call: %d %d\n", ps->lsta2.f1, ps->lsta2.f2);
 
     static struct {
         void (*elem)();
diff --git a/x86_64-gen.c b/x86_64-gen.c
index 0b0e69e..49cfdd1 100644
--- a/x86_64-gen.c
+++ b/x86_64-gen.c
@@ -820,7 +820,6 @@ static const uint8_t arg_regs[REGN] = {
 void gfunc_call(int nb_args)
 {
     int size, align, r, args_size, i;
-    SValue *orig_vtop;
     int nb_reg_args = 0;
     int nb_sse_args = 0;
     int sse_reg, gen_reg;
@@ -845,7 +844,6 @@ void gfunc_call(int nb_args)
     /* for struct arguments, we need to call memcpy and the function
        call breaks register passing arguments we are preparing.
        So, we process arguments which will be passed by stack first. */
-    orig_vtop = vtop;
     gen_reg = nb_reg_args;
     sse_reg = nb_sse_args;
 
@@ -861,6 +859,14 @@ void gfunc_call(int nb_args)
     }
 
     for(i = 0; i < nb_args; i++) {
+	/* Swap argument to top, it will possibly be changed here,
+	   and might use more temps.  All arguments must remain on the
+	   stack, so that get_reg can correctly evict some of them onto
+	   stack.  We could use also use a vrott(nb_args) at the end
+	   of this loop, but this seems faster.  */
+        SValue tmp = vtop[0];
+	vtop[0] = vtop[-i];
+	vtop[-i] = tmp;
         if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
             size = type_size(&vtop->type, &align);
             /* align to stack align size */
@@ -872,18 +878,9 @@ void gfunc_call(int nb_args)
             r = get_reg(RC_INT);
             orex(1, r, 0, 0x89); /* mov %rsp, r */
             o(0xe0 + REG_VALUE(r));
-            {
-                /* following code breaks vtop[1], vtop[2], and vtop[3] */
-                SValue tmp1 = vtop[1];
-                SValue tmp2 = vtop[2];
-                SValue tmp3 = vtop[3];
-                vset(&vtop->type, r | VT_LVAL, 0);
-                vswap();
-                vstore();
-                vtop[1] = tmp1;
-                vtop[2] = tmp2;
-                vtop[3] = tmp3;
-            }
+	    vset(&vtop->type, r | VT_LVAL, 0);
+	    vswap();
+	    vstore();
             args_size += size;
         } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
             gv(RC_ST0);
@@ -913,10 +910,14 @@ void gfunc_call(int nb_args)
                 args_size += 8;
             }
         }
-        vtop--;
+
+	/* And swap the argument back to it's original position.  */
+        tmp = vtop[0];
+	vtop[0] = vtop[-i];
+	vtop[-i] = tmp;
     }
-    vtop = orig_vtop;
 
+    /* XXX This should be superfluous.  */
     save_regs(0); /* save used temporary registers */
 
     /* then, we prepare register passing arguments.
@@ -953,6 +954,12 @@ void gfunc_call(int nb_args)
         vtop--;
     }
 
+    /* We shouldn't have many operands on the stack anymore, but the
+       call address itself is still there, and it might be in %eax
+       (or edx/ecx) currently, which the below writes would clobber.
+       So evict all remaining operands here.  */
+    save_regs(0);
+
     /* Copy R10 and R11 into RDX and RCX, respectively */
     if (nb_reg_args > 2) {
         o(0xd2894c); /* mov %r10, %rdx */
-- 
1.7.3.4

From 1d0a5c251553d8d9199ad18d4780bf356f456298 Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Sun, 15 Apr 2012 19:29:45 +0200
Subject: [PATCH 4/9] x86_64: Fix segfault for global data

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.
---
 tests/tcctest.c |   30 ++++++++++++++++++++++++++++++
 x86_64-gen.c    |    2 +-
 2 files changed, 31 insertions(+), 1 deletions(-)

diff --git a/tests/tcctest.c b/tests/tcctest.c
index fcd9822..03be501 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -85,6 +85,7 @@ void statement_expr_test(void);
 void asm_test(void);
 void builtin_test(void);
 void weak_test(void);
+void global_data_test(void);
 
 int fib(int n);
 void num(int n);
@@ -583,6 +584,7 @@ int main(int argc, char **argv)
     asm_test();
     builtin_test();
     weak_test();
+    global_data_test();
     return 0; 
 }
 
@@ -2509,3 +2511,31 @@ int getme (struct condstruct *s, int i)
   int i4 = (i == 0 ? s : (void*)0)->i;
   return i1 + i2 + i3 + i4;
 }
+
+struct global_data
+{
+  int a[40];
+  int *b[40];
+};
+
+struct global_data global_data;
+
+int global_data_getstuff (int *, int);
+
+void global_data_callit (int i)
+{
+  *global_data.b[i] = global_data_getstuff (global_data.b[i], 1);
+}
+
+int global_data_getstuff (int *p, int i)
+{
+  return *p + i;
+}
+
+void global_data_test (void)
+{
+  global_data.a[0] = 42;
+  global_data.b[0] = &global_data.a[0];
+  global_data_callit (0);
+  printf ("%d\n", global_data.a[0]);
+}
diff --git a/x86_64-gen.c b/x86_64-gen.c
index 49cfdd1..0f86b3a 100644
--- a/x86_64-gen.c
+++ b/x86_64-gen.c
@@ -414,7 +414,7 @@ void load(int r, SValue *sv)
                 } else {
                     orex(1,0,r,0x8b);
                     o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */
-                    gen_gotpcrel(fr, sv->sym, fc);
+                    gen_gotpcrel(r, sv->sym, fc);
                 }
 #endif
             } else if (is64_type(ft)) {
-- 
1.7.3.4

From 15f4ac2b1a4453b73904bb2ff4441498d5911a64 Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Sun, 15 Apr 2012 22:17:51 +0200
Subject: [PATCH 5/9] Fix detection of labels with a typedef name

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).
---
 tccgen.c        |   21 ++++++++++++++++++++-
 tccpp.c         |   12 ++++++++++--
 tests/tcctest.c |    3 +++
 3 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/tccgen.c b/tccgen.c
index 2c759e1..6031611 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -4286,6 +4286,25 @@ static int is_label(void)
     }
 }
 
+static void label_or_decl(int l)
+{
+    int last_tok;
+
+    /* fast test first */
+    if (tok >= TOK_UIDENT)
+      {
+	/* no need to save tokc because tok is an identifier */
+	last_tok = tok;
+	next();
+	if (tok == ':') {
+	    unget_tok(last_tok);
+	    return;
+	}
+	unget_tok(last_tok);
+      }
+    decl(l);
+}
+
 static void block(int *bsym, int *csym, int *case_sym, int *def_sym, 
                   int case_reg, int is_expr)
 {
@@ -4359,7 +4378,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
             }
         }
         while (tok != '}') {
-            decl(VT_LOCAL);
+            label_or_decl(VT_LOCAL);
             if (tok != '}') {
                 if (is_expr)
                     vpop();
diff --git a/tccpp.c b/tccpp.c
index 913bded..f3dd232 100644
--- a/tccpp.c
+++ b/tccpp.c
@@ -2995,8 +2995,16 @@ ST_INLN void unget_tok(int last_tok)
 {
     int i, n;
     int *q;
-    unget_saved_macro_ptr = macro_ptr;
-    unget_buffer_enabled = 1;
+    if (unget_buffer_enabled)
+      {
+        /* assert(macro_ptr == unget_saved_buffer + 1);
+	   assert(*macro_ptr == 0);  */
+      }
+    else
+      {
+	unget_saved_macro_ptr = macro_ptr;
+	unget_buffer_enabled = 1;
+      }
     q = unget_saved_buffer;
     macro_ptr = q;
     *q++ = tok;
diff --git a/tests/tcctest.c b/tests/tcctest.c
index 03be501..eee7039 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -432,6 +432,7 @@ void loop_test()
     printf("\n");
 }
 
+typedef int typedef_and_label;
 
 void goto_test()
 {
@@ -440,6 +441,8 @@ void goto_test()
 
     printf("goto:\n");
     i = 0;
+    /* This needs to parse as label, not as start of decl.  */
+ typedef_and_label:
  s_loop:
     if (i >= 10) 
         goto s_end;
-- 
1.7.3.4

From 4c0d70ab07b4a5a5040e53259f8e9cedd4ec89b2 Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Mon, 16 Apr 2012 00:21:40 +0200
Subject: [PATCH 6/9] Fix parsing function macro invocations

If a function macro name is separated from the parentheses in
an macro invocation the substitution doesn't take place.
Fix this by handling comments.
---
 tccpp.c         |   21 ++++++++++++++++++---
 tests/tcctest.c |    4 ++++
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/tccpp.c b/tccpp.c
index f3dd232..aff5a53 100644
--- a/tccpp.c
+++ b/tccpp.c
@@ -2697,10 +2697,25 @@ static int macro_subst_tok(TokenString *tok_str,
                     goto redo;
                 }
             } else {
-                /* XXX: incorrect with comments */
                 ch = file->buf_ptr[0];
-                while (is_space(ch) || ch == '\n')
-                    cinp();
+                while (is_space(ch) || ch == '\n' || ch == '/')
+		  {
+		    if (ch == '/')
+		      {
+			int c;
+			uint8_t *p = file->buf_ptr;
+			PEEKC(c, p);
+			if (c == '*') {
+			    p = parse_comment(p);
+			    file->buf_ptr = p - 1;
+			} else if (c == '/') {
+			    p = parse_line_comment(p);
+			    file->buf_ptr = p - 1;
+			} else
+			  break;
+		      }
+		    cinp();
+		  }
                 t = ch;
             }
             if (t != '(') /* no macro subst */
diff --git a/tests/tcctest.c b/tests/tcctest.c
index eee7039..eeabb7c 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -288,6 +288,10 @@ comment
     /* test function macro substitution when the function name is
        substituted */
     TEST2();
+
+    /* And again when the name and parenthes are separated by a
+       comment.  */
+    TEST2 /* the comment */ ();
 }
 
 
-- 
1.7.3.4

From b068e29df753192bdcec5b1e41401bdc21812136 Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Mon, 16 Apr 2012 00:39:24 +0200
Subject: [PATCH 7/9] x86_64: Implement GET_CALLER_FP

TCC always uses %rbp frames, so we can use that one.
---
 lib/bcheck.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/lib/bcheck.c b/lib/bcheck.c
index 9996649..48d7606 100644
--- a/lib/bcheck.c
+++ b/lib/bcheck.c
@@ -216,6 +216,14 @@ BOUND_PTR_INDIR(16)
     __asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\
     fp = fp1[0];\
 }
+#elif defined(__x86_64__)
+/* TCC always creates %rbp frames also on x86_64, so use them.  */
+#define GET_CALLER_FP(fp)\
+{\
+    unsigned long *fp1;\
+    __asm__ __volatile__ ("movq %%rbp,%0" :"=g" (fp1));\
+    fp = fp1[0];\
+}
 #else
 #error put code to extract the calling frame pointer
 #endif
-- 
1.7.3.4

From 718fd591fa93ff254a9ca68a0ad2ff8a55756489 Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Mon, 16 Apr 2012 01:13:25 +0200
Subject: [PATCH 8/9] Make sizeof() be of type size_t

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).
---
 libtcc.c        |    6 ++++++
 tcc.h           |    2 +-
 tccgen.c        |   17 ++++++++++++++---
 tests/tcctest.c |   16 ++++++++++++++++
 4 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/libtcc.c b/libtcc.c
index c6b8d38..01280de 100644
--- a/libtcc.c
+++ b/libtcc.c
@@ -745,6 +745,12 @@ static int tcc_compile(TCCState *s1)
     char_pointer_type.t = VT_BYTE;
     mk_pointer(&char_pointer_type);
 
+#if PTR_SIZE == 4
+    size_type.t = VT_INT;
+#else
+    size_type.t = VT_LLONG;
+#endif
+
     func_old_type.t = VT_FUNC;
     func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
 
diff --git a/tcc.h b/tcc.h
index 95d5030..a674f92 100644
--- a/tcc.h
+++ b/tcc.h
@@ -1117,7 +1117,7 @@ ST_DATA Sym *local_stack;
 ST_DATA Sym *local_label_stack;
 ST_DATA Sym *global_label_stack;
 ST_DATA Sym *define_stack;
-ST_DATA CType char_pointer_type, func_old_type, int_type;
+ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
 ST_DATA SValue vstack[VSTACK_SIZE], *vtop;
 ST_DATA int rsym, anon_sym, ind, loc;
 
diff --git a/tccgen.c b/tccgen.c
index 6031611..36a1879 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -64,7 +64,7 @@ ST_DATA int func_vc;
 ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
 ST_DATA char *funcname;
 
-ST_DATA CType char_pointer_type, func_old_type, int_type;
+ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
 
 /* ------------------------------------------------------------------------- */
 static void gen_cast(CType *type);
@@ -325,6 +325,17 @@ ST_FUNC void vpushi(int v)
     vsetc(&int_type, VT_CONST, &cval);
 }
 
+/* push a pointer sized constant */
+static void vpushs(long long v)
+{
+  CValue cval;
+  if (PTR_SIZE == 4)
+    cval.i = (int)v;
+  else
+    cval.ull = v;
+  vsetc(&size_type, VT_CONST, &cval);
+}
+
 /* push long long constant */
 static void vpushll(long long v)
 {
@@ -3575,12 +3586,12 @@ ST_FUNC void unary(void)
             if (!(type.t & VT_VLA)) {
                 if (size < 0)
                     tcc_error("sizeof applied to an incomplete type");
-                vpushi(size);
+                vpushs(size);
             } else {
                 vla_runtime_type_size(&type, &align);
             }
         } else {
-            vpushi(align);
+            vpushs(align);
         }
         vtop->type.t |= VT_UNSIGNED;
         break;
diff --git a/tests/tcctest.c b/tests/tcctest.c
index eeabb7c..9598fa4 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -2155,6 +2155,8 @@ void c99_vla_test(int size1, int size2)
 #endif
 }
 
+typedef __SIZE_TYPE__ uintptr_t;
+
 void sizeof_test(void)
 {
     int a;
@@ -2175,6 +2177,20 @@ void sizeof_test(void)
     ptr = NULL;
     printf("sizeof(**ptr) = %d\n", sizeof (**ptr));
 
+    /* The type of sizeof should be as large as a pointer, actually
+       it should be size_t.  */
+    printf("sizeof(sizeof(int) = %d\n", sizeof(sizeof(int)));
+    uintptr_t t = 1;
+    uintptr_t t2;
+    /* Effectively <<32, but defined also on 32bit machines.  */
+    t <<= 16;
+    t <<= 16;
+    t++;
+    /* This checks that sizeof really can be used to manipulate 
+       uintptr_t objects, without truncation.  */
+    t2 = t & -sizeof(uintptr_t);
+    printf ("%lu %lu\n", t, t2);
+
     /* some alignof tests */
     printf("__alignof__(int) = %d\n", __alignof__(int));
     printf("__alignof__(unsigned int) = %d\n", __alignof__(unsigned int));
-- 
1.7.3.4

From 9ca9c82ff8a4001299a4b9666d85352f5806d565 Mon Sep 17 00:00:00 2001
From: Michael Matz <m...@suse.de>
Date: Mon, 16 Apr 2012 02:52:15 +0200
Subject: [PATCH 9/9] Fix comparing comparisons

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.
---
 tccgen.c        |    8 ++++++++
 tests/tcctest.c |   30 ++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 0 deletions(-)

diff --git a/tccgen.c b/tccgen.c
index 36a1879..9339b5f 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -452,6 +452,14 @@ ST_FUNC void vswap(void)
 {
     SValue tmp;
 
+    /* cannot let cpu flags if other instruction are generated. Also
+       avoid leaving VT_JMP anywhere except on the top of the stack
+       because it would complicate the code generator. */
+    if (vtop >= vstack) {
+        int v = vtop->r & VT_VALMASK;
+        if (v == VT_CMP || (v & ~1) == VT_JMP)
+            gv(RC_INT);
+    }
     tmp = vtop[0];
     vtop[0] = vtop[-1];
     vtop[-1] = tmp;
diff --git a/tests/tcctest.c b/tests/tcctest.c
index 9598fa4..7772f89 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -86,6 +86,7 @@ void asm_test(void);
 void builtin_test(void);
 void weak_test(void);
 void global_data_test(void);
+void cmp_comparison_test(void);
 
 int fib(int n);
 void num(int n);
@@ -592,6 +593,7 @@ int main(int argc, char **argv)
     builtin_test();
     weak_test();
     global_data_test();
+    cmp_comparison_test();
     return 0; 
 }
 
@@ -2562,3 +2564,31 @@ void global_data_test (void)
   global_data_callit (0);
   printf ("%d\n", global_data.a[0]);
 }
+
+struct cmpcmpS
+{
+  unsigned char fill : 3;
+  unsigned char b1 : 1;
+  unsigned char b2 : 1;
+  unsigned char fill2 : 3;
+};
+
+int glob1, glob2, glob3;
+
+void compare_comparisons (struct cmpcmpS *s)
+{
+  if (s->b1 != (glob1 == glob2)
+      || (s->b2 != (glob1 == glob3)))
+    printf ("comparing comparisons broken\n");
+}
+
+void cmp_comparison_test(void)
+{
+  struct cmpcmpS s;
+  s.b1 = 1;
+  glob1 = 42; glob2 = 42;
+  s.b2 = 0;
+  glob3 = 43;
+  compare_comparisons (&s);
+  return 0;
+}
-- 
1.7.3.4

Index: diagnostic.c
===================================================================
--- diagnostic.c	(revision 186473)
+++ diagnostic.c	(working copy)
@@ -714,6 +714,7 @@
 {
   diagnostic_info diagnostic;
   va_list ap;
+  bool ret;
 
   va_start (ap, gmsgid);
   if (kind == DK_PERMERROR)
@@ -727,9 +728,10 @@
       if (kind == DK_WARNING || kind == DK_PEDWARN)
 	diagnostic.option_index = opt;
   }
+
+  ret = report_diagnostic (&diagnostic);
   va_end (ap);
-
-  return report_diagnostic (&diagnostic);
+  return ret;
 }
 
 /* An informative note at LOCATION.  Use this for additional details on an error
@@ -771,13 +773,15 @@
 {
   diagnostic_info diagnostic;
   va_list ap;
+  bool ret;
 
   va_start (ap, gmsgid);
   diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_WARNING);
   diagnostic.option_index = opt;
 
+  ret = report_diagnostic (&diagnostic);
   va_end (ap);
-  return report_diagnostic (&diagnostic);
+  return ret;
 }
 
 /* A warning at LOCATION.  Use this for code which is correct according to the
@@ -789,12 +793,14 @@
 {
   diagnostic_info diagnostic;
   va_list ap;
+  bool ret;
 
   va_start (ap, gmsgid);
   diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_WARNING);
   diagnostic.option_index = opt;
+  ret = report_diagnostic (&diagnostic);
   va_end (ap);
-  return report_diagnostic (&diagnostic);
+  return ret;
 }
 
 /* A "pedantic" warning at LOCATION: issues a warning unless
@@ -815,12 +821,14 @@
 {
   diagnostic_info diagnostic;
   va_list ap;
+  bool ret;
 
   va_start (ap, gmsgid);
   diagnostic_set_info (&diagnostic, gmsgid, &ap, location,  DK_PEDWARN);
   diagnostic.option_index = opt;
+  ret = report_diagnostic (&diagnostic);
   va_end (ap);
-  return report_diagnostic (&diagnostic);
+  return ret;
 }
 
 /* A "permissive" error at LOCATION: issues an error unless
@@ -835,13 +843,15 @@
 {
   diagnostic_info diagnostic;
   va_list ap;
+  bool ret;
 
   va_start (ap, gmsgid);
   diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
                        permissive_error_kind (global_dc));
   diagnostic.option_index = permissive_error_option (global_dc);
+  ret = report_diagnostic (&diagnostic);
   va_end (ap);
-  return report_diagnostic (&diagnostic);
+  return ret;
 }
 
 /* A hard error: the code is definitely ill-formed, and an object file
_______________________________________________
Tinycc-devel mailing list
Tinycc-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to