aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2021-09-29 14:51:49 -0700
committerMichael Forney <mforney@mforney.org>2021-09-29 15:04:44 -0700
commitcb697da1d5c36ac4e75e554cbc279674dd7075f4 (patch)
tree9008074de873285abb083cfc66f5c324caa26e08
parentdbdab3ac59af6469e715f8ce9dfe3fdfc523cddf (diff)
eval: Avoid implementation-defined unsigned to signed conversions
Instead, use an additional int64_t member in the union. Since exact-width integer types have no padding bits or trap representations, and use two's-complement representation, we can portably access an int64_t union member stored as uint64_t and vice-versa. This allows us to reinterpret the value without invoking potentially implementation-defined behavior of casting an unsigned integer to a signed integer type which may not be able to represent its value.
-rw-r--r--cc.h1
-rw-r--r--eval.c74
2 files changed, 38 insertions, 37 deletions
diff --git a/cc.h b/cc.h
index 9597d6e..51b8f86 100644
--- a/cc.h
+++ b/cc.h
@@ -328,6 +328,7 @@ struct expr {
} ident;
union {
uint64_t u;
+ int64_t i;
double f;
} constant;
struct {
diff --git a/eval.c b/eval.c
index 2590687..958f517 100644
--- a/eval.c
+++ b/eval.c
@@ -17,13 +17,13 @@ cast(struct expr *expr)
else if (expr->type->prop & PROPINT && expr->type->basic.issigned)
size |= S;
switch (size) {
- case 1: expr->constant.u = (uint8_t)expr->constant.u; break;
- case 1|S: expr->constant.u = (int8_t)expr->constant.u; break;
+ case 1: expr->constant.u = (uint8_t)expr->constant.u; break;
+ case 1|S: expr->constant.u = (expr->constant.u & UINT8_MAX ^ INT8_MIN) - INT8_MIN; break;
case 2: expr->constant.u = (uint16_t)expr->constant.u; break;
- case 2|S: expr->constant.u = (int16_t)expr->constant.u; break;
+ case 2|S: expr->constant.u = (expr->constant.u & UINT16_MAX ^ INT16_MIN) - INT16_MIN; break;
case 4: expr->constant.u = (uint32_t)expr->constant.u; break;
- case 4|S: expr->constant.u = (int32_t)expr->constant.u; break;
- case 4|F: expr->constant.f = (float)expr->constant.f; break;
+ case 4|S: expr->constant.u = (expr->constant.u & UINT32_MAX ^ INT32_MIN) - INT32_MIN; break;
+ case 4|F: expr->constant.f = (float)expr->constant.f; break;
}
}
@@ -37,47 +37,47 @@ binary(struct expr *expr, enum tokenkind op, struct expr *l, struct expr *r)
op |= S;
switch (op) {
case TMUL:
- case TMUL|S: expr->constant.u = l->constant.u * r->constant.u; break;
- case TMUL|F: expr->constant.f = l->constant.f * r->constant.f; break;
- case TDIV: expr->constant.u = l->constant.u / r->constant.u; break;
- case TDIV|S: expr->constant.u = (int64_t)l->constant.u / (int64_t)r->constant.u; break;
- case TDIV|F: expr->constant.f = l->constant.f / r->constant.f; break;
- case TMOD: expr->constant.u = l->constant.u % r->constant.u; break;
- case TMOD|S: expr->constant.u = (int64_t)l->constant.u % (int64_t)r->constant.u; break;
+ case TMUL|S: expr->constant.u = l->constant.u * r->constant.u; break;
+ case TMUL|F: expr->constant.f = l->constant.f * r->constant.f; break;
+ case TDIV: expr->constant.u = l->constant.u / r->constant.u; break;
+ case TDIV|S: expr->constant.i = l->constant.i / r->constant.i; break;
+ case TDIV|F: expr->constant.f = l->constant.f / r->constant.f; break;
+ case TMOD: expr->constant.u = l->constant.u % r->constant.u; break;
+ case TMOD|S: expr->constant.i = l->constant.i % r->constant.i; break;
case TADD:
- case TADD|S: expr->constant.u = l->constant.u + r->constant.u; break;
- case TADD|F: expr->constant.f = l->constant.f + r->constant.f; break;
+ case TADD|S: expr->constant.u = l->constant.u + r->constant.u; break;
+ case TADD|F: expr->constant.f = l->constant.f + r->constant.f; break;
case TSUB:
- case TSUB|S: expr->constant.u = l->constant.u - r->constant.u; break;
- case TSUB|F: expr->constant.f = l->constant.f - r->constant.f; break;
+ case TSUB|S: expr->constant.u = l->constant.u - r->constant.u; break;
+ case TSUB|F: expr->constant.f = l->constant.f - r->constant.f; break;
case TSHL:
- case TSHL|S: expr->constant.u = l->constant.u << r->constant.u; break;
- case TSHR: expr->constant.u = l->constant.u >> r->constant.u; break;
- case TSHR|S: expr->constant.u = (int64_t)l->constant.u >> r->constant.u; break;
+ case TSHL|S: expr->constant.u = l->constant.u << (r->constant.u & 63); break;
+ case TSHR: expr->constant.u = l->constant.u >> (r->constant.u & 63); break;
+ case TSHR|S: expr->constant.i = l->constant.i >> (r->constant.u & 63); break;
case TBAND:
- case TBAND|S: expr->constant.u = l->constant.u & r->constant.u; break;
+ case TBAND|S: expr->constant.u = l->constant.u & r->constant.u; break;
case TBOR:
- case TBOR|S: expr->constant.u = l->constant.u | r->constant.u; break;
+ case TBOR|S: expr->constant.u = l->constant.u | r->constant.u; break;
case TXOR:
- case TXOR|S: expr->constant.u = l->constant.u ^ r->constant.u; break;
- case TLESS: expr->constant.u = l->constant.u < r->constant.u; break;
- case TLESS|S: expr->constant.u = (int64_t)l->constant.u < (int64_t)r->constant.u; break;
- case TLESS|F: expr->constant.u = l->constant.f < r->constant.f; break;
- case TGREATER: expr->constant.u = l->constant.u > r->constant.u; break;
- case TGREATER|S: expr->constant.u = (int64_t)l->constant.u > (int64_t)r->constant.u; break;
+ case TXOR|S: expr->constant.u = l->constant.u ^ r->constant.u; break;
+ case TLESS: expr->constant.u = l->constant.u < r->constant.u; break;
+ case TLESS|S: expr->constant.u = l->constant.i < r->constant.i; break;
+ case TLESS|F: expr->constant.u = l->constant.f < r->constant.f; break;
+ case TGREATER: expr->constant.u = l->constant.u > r->constant.u; break;
+ case TGREATER|S: expr->constant.u = l->constant.i > r->constant.i; break;
case TGREATER|F: expr->constant.u = l->constant.f > r->constant.f; break;
- case TLEQ: expr->constant.u = l->constant.u <= r->constant.u; break;
- case TLEQ|S: expr->constant.u = (int64_t)l->constant.u <= (int64_t)r->constant.u; break;
- case TLEQ|F: expr->constant.u = l->constant.f <= r->constant.f; break;
- case TGEQ: expr->constant.u = l->constant.u >= r->constant.u; break;
- case TGEQ|S: expr->constant.u = (int64_t)l->constant.u >= (int64_t)r->constant.u; break;
- case TGEQ|F: expr->constant.u = l->constant.f >= r->constant.f; break;
+ case TLEQ: expr->constant.u = l->constant.u <= r->constant.u; break;
+ case TLEQ|S: expr->constant.u = l->constant.i <= r->constant.i; break;
+ case TLEQ|F: expr->constant.u = l->constant.f <= r->constant.f; break;
+ case TGEQ: expr->constant.u = l->constant.u >= r->constant.u; break;
+ case TGEQ|S: expr->constant.u = l->constant.i >= r->constant.i; break;
+ case TGEQ|F: expr->constant.u = l->constant.f >= r->constant.f; break;
case TEQL:
- case TEQL|S: expr->constant.u = l->constant.u == r->constant.u; break;
- case TEQL|F: expr->constant.u = l->constant.f == r->constant.f; break;
+ case TEQL|S: expr->constant.u = l->constant.u == r->constant.u; break;
+ case TEQL|F: expr->constant.u = l->constant.f == r->constant.f; break;
case TNEQ:
- case TNEQ|S: expr->constant.u = l->constant.u != r->constant.u; break;
- case TNEQ|F: expr->constant.u = l->constant.f != r->constant.f; break;
+ case TNEQ|S: expr->constant.u = l->constant.u != r->constant.u; break;
+ case TNEQ|F: expr->constant.u = l->constant.f != r->constant.f; break;
default:
fatal("internal error; unknown binary expression");
}