aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdmund Grimley Evans <Edmund.Grimley.Evans@gmail.com>2016-10-10 20:15:20 +0100
committerEdmund Grimley Evans <Edmund.Grimley.Evans@gmail.com>2016-10-10 20:15:20 +0100
commit94d8d12c26de54d9ab14c34820967861352773ab (patch)
tree6debfa9fb0ff14e0e9cc9c642cfa4d56c6021124
parent5a0ca53a4a87252ad4fdb442ec3daaa6b00ec2e2 (diff)
downloadtinycc-94d8d12c26de54d9ab14c34820967861352773ab.tar.gz
tinycc-94d8d12c26de54d9ab14c34820967861352773ab.tar.bz2
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.
-rw-r--r--tccgen.c21
1 files changed, 18 insertions, 3 deletions
diff --git a/tccgen.c b/tccgen.c
index 6447eb5..ad4cfde 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)