From 94d8d12c26de54d9ab14c34820967861352773ab Mon Sep 17 00:00:00 2001
From: Edmund Grimley Evans <Edmund.Grimley.Evans@gmail.com>
Date: Mon, 10 Oct 2016 20:15:20 +0100
Subject: [PATCH] Fix handling of case_reg in switch statement.

The back end functions gen_op(comparison) and gtst() might allocate
registers so case_reg should be left on the value stack while they
are called and set again afterwards.

This bug fix was first applied as ff3f9aa (20 Feb 2015), but the fix
was reverted by fc0fc6a (21 Sep 2016, "switch: collect case ranges
first, then generate code"). Here the fix is updated for the new code.
---
 tccgen.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/tccgen.c b/tccgen.c
index 6447eb52..ad4cfde9 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -4902,7 +4902,7 @@ static int case_cmp(const void *pa, const void *pb)
     return a < b ? -1 : a > b;
 }
 
-static void gcase(struct case_t **base, int len, int case_reg, int *bsym)
+static int gcase(struct case_t **base, int len, int case_reg, int *bsym)
 {
     struct case_t *p;
     int e;
@@ -4910,17 +4910,25 @@ static void gcase(struct case_t **base, int len, int case_reg, int *bsym)
         while (len--) {
             p = *base++;
             vseti(case_reg, 0);
+            vdup();
             vpushi(p->v2);
             if (p->v1 == p->v2) {
                 gen_op(TOK_EQ);
                 gtst_addr(0, p->sym);
+                case_reg = gv(RC_INT);
+                vpop();
             } else {
                 gen_op(TOK_LE);
                 e = gtst(1, 0);
+                case_reg = gv(RC_INT);
+                vpop();
                 vseti(case_reg, 0);
+                vdup();
                 vpushi(p->v1);
                 gen_op(TOK_GE);
                 gtst_addr(0, p->sym);
+                case_reg = gv(RC_INT);
+                vpop();
                 gsym(e);
             }
         }
@@ -4928,15 +4936,21 @@ static void gcase(struct case_t **base, int len, int case_reg, int *bsym)
         p = base[len/2];
         /* mid */
         vseti(case_reg, 0);
+        vdup();
         vpushi(p->v2);
         gen_op(TOK_LE);
         e = gtst(1, 0);
+        case_reg = gv(RC_INT);
+        vpop();
         vseti(case_reg, 0);
+        vdup();
         vpushi(p->v1);
         gen_op(TOK_GE);
         gtst_addr(0, p->sym);
+        case_reg = gv(RC_INT);
+        vpop();
         /* left */
-        gcase(base, len/2, case_reg, bsym);
+        case_reg = gcase(base, len/2, case_reg, bsym);
         if (cur_switch->def_sym)
             gjmp_addr(cur_switch->def_sym);
         else
@@ -4944,8 +4958,9 @@ static void gcase(struct case_t **base, int len, int case_reg, int *bsym)
         /* right */
         gsym(e);
         e = len/2 + 1;
-        gcase(base + e, len - e, case_reg, bsym);
+        case_reg = gcase(base + e, len - e, case_reg, bsym);
     }
+    return case_reg;
 }
 
 static void block(int *bsym, int *csym, int is_expr)