aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2022-01-19 11:12:59 -0800
committerMichael Forney <mforney@mforney.org>2022-01-22 11:36:12 -0800
commit51e61fa5fa3de8cabc2191a5c6ac4d18aaaaf049 (patch)
tree8915d405a6f4aeadb28e06ace14d9459aa0b23b4
parentd47ebf4c75615d1667a587d53dcd654e12ca6829 (diff)
Handle unary minus specially instead of 0 - x
This is necessary to fix unary negation of floating-point 0 (also depends on a pending qbe patch).
-rw-r--r--eval.c44
-rw-r--r--expr.c3
-rw-r--r--qbe.c3
-rw-r--r--test/const-expr-neg.c1
-rw-r--r--test/const-expr-neg.qbe1
5 files changed, 41 insertions, 11 deletions
diff --git a/eval.c b/eval.c
index 70febb1..908ce8d 100644
--- a/eval.c
+++ b/eval.c
@@ -27,6 +27,21 @@ cast(struct expr *expr)
}
static void
+unary(struct expr *expr, enum tokenkind op, struct expr *l)
+{
+ expr->kind = EXPRCONST;
+ if (l->type->prop & PROPFLOAT)
+ op |= F;
+ switch (op) {
+ case TSUB: expr->u.constant.u = -l->u.constant.u; break;
+ case TSUB|F: expr->u.constant.f = -l->u.constant.f; break;
+ default:
+ fatal("internal error; unknown unary expression");
+ }
+ cast(expr);
+}
+
+static void
binary(struct expr *expr, enum tokenkind op, struct expr *l, struct expr *r)
{
expr->kind = EXPRCONST;
@@ -111,19 +126,28 @@ eval(struct expr *expr, enum evalkind kind)
break;
case EXPRUNARY:
l = eval(expr->base, kind);
- if (expr->op != TBAND)
+ switch (expr->op) {
+ case TBAND:
+ switch (l->kind) {
+ case EXPRUNARY:
+ if (l->op == TMUL)
+ expr = eval(l->base, kind);
+ break;
+ case EXPRSTRING:
+ if (kind != EVALINIT)
+ break;
+ l->u.ident.decl = stringdecl(l);
+ l->kind = EXPRIDENT;
+ expr->base = l;
+ break;
+ }
break;
- switch (l->kind) {
- case EXPRUNARY:
- if (l->op == TMUL)
- expr = eval(l->base, kind);
+ case TMUL:
break;
- case EXPRSTRING:
- if (kind != EVALINIT)
+ default:
+ if (l->kind != EXPRCONST)
break;
- l->u.ident.decl = stringdecl(l);
- l->kind = EXPRIDENT;
- expr->base = l;
+ unary(expr, expr->op, l);
break;
}
break;
diff --git a/expr.c b/expr.c
index 1e49e4f..903aef8 100644
--- a/expr.c
+++ b/expr.c
@@ -972,7 +972,8 @@ unaryexpr(struct scope *s)
error(&tok.loc, "operand of unary '-' operator must have arithmetic type");
if (e->type->prop & PROPINT)
e = exprpromote(e);
- e = mkbinaryexpr(&tok.loc, TSUB, mkconstexpr(&typeint, 0), e);
+ e = mkexpr(EXPRUNARY, e->type, e);
+ e->op = op;
break;
case TBNOT:
next();
diff --git a/qbe.c b/qbe.c
index e8d9c13..81ed566 100644
--- a/qbe.c
+++ b/qbe.c
@@ -801,6 +801,9 @@ funcexpr(struct func *f, struct expr *e)
case TMUL:
r = funcexpr(f, e->base);
return funcload(f, e->type, (struct lvalue){r});
+ case TSUB:
+ r = funcexpr(f, e->base);
+ return funcinst(f, ISUB, qbetype(e->type).base, mkintconst(0), r);
}
fatal("internal error; unknown unary expression");
break;
diff --git a/test/const-expr-neg.c b/test/const-expr-neg.c
new file mode 100644
index 0000000..0918aa9
--- /dev/null
+++ b/test/const-expr-neg.c
@@ -0,0 +1 @@
+double x = -0.0;
diff --git a/test/const-expr-neg.qbe b/test/const-expr-neg.qbe
new file mode 100644
index 0000000..b380795
--- /dev/null
+++ b/test/const-expr-neg.qbe
@@ -0,0 +1 @@
+export data $x = align 8 { d d_-0, }