cproc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/cproc
Log | Files | Refs | Submodules | README | LICENSE

commit a98385a607aafe373c14d2a6fb2778d21a600c30
parent e0b772b836265b77686e9a7aa8ad01bb1fca53e7
Author: Michael Forney <mforney@mforney.org>
Date:   Tue, 23 Apr 2019 21:22:25 -0700

Fix integer promotion on bit-fields

Fixes #47.

Diffstat:
Mcc.h | 6+++---
Mexpr.c | 33++++++++++++++++++++++++++-------
Mqbe.c | 2+-
Mstmt.c | 2+-
Atest/bitfield-integer-promotion-long.c | 6++++++
Atest/bitfield-integer-promotion-long.qbe | 6++++++
Atest/bitfield-integer-promotion.c | 7+++++++
Atest/bitfield-integer-promotion.qbe | 17+++++++++++++++++
Mtype.c | 27++++++++++++---------------
9 files changed, 79 insertions(+), 27 deletions(-)

diff --git a/cc.h b/cc.h @@ -400,9 +400,8 @@ _Bool typecompatible(struct type *, struct type *); _Bool typesame(struct type *, struct type *); struct type *typecomposite(struct type *, struct type *); struct type *typeunqual(struct type *, enum typequal *); -struct type *typecommonreal(struct type *, struct type *); -struct type *typeargpromote(struct type *); -struct type *typeintpromote(struct type *); +struct type *typecommonreal(struct type *, unsigned, struct type *, unsigned); +struct type *typepromote(struct type *, unsigned); enum typeprop typeprop(struct type *); struct member *typemember(struct type *, const char *, uint64_t *); @@ -451,6 +450,7 @@ uint64_t intconstexpr(struct scope *, _Bool); void delexpr(struct expr *); struct expr *exprconvert(struct expr *, struct type *); +struct expr *exprpromote(struct expr *); /* eval */ diff --git a/expr.c b/expr.c @@ -107,12 +107,29 @@ mkunaryexpr(enum tokenkind op, struct expr *base) fatal("internal error: unknown unary operator %d", op); } +static unsigned +bitfieldwidth(struct expr *e) +{ + if (e->kind != EXPRBITFIELD) + return -1; + return e->type->size * 8 - e->bitfield.bits.before - e->bitfield.bits.after; +} + +struct expr * +exprpromote(struct expr *e) +{ + struct type *t; + + t = typepromote(e->type, bitfieldwidth(e)); + return exprconvert(e, t); +} + static struct type * commonreal(struct expr **e1, struct expr **e2) { struct type *t; - t = typecommonreal((*e1)->type, (*e2)->type); + t = typecommonreal((*e1)->type, bitfieldwidth(*e1), (*e2)->type, bitfieldwidth(*e2)); *e1 = exprconvert(*e1, t); *e2 = exprconvert(*e2, t); @@ -208,8 +225,8 @@ mkbinaryexpr(struct location *loc, enum tokenkind op, struct expr *l, struct exp case TSHR: if (!(lp & PROPINT) || !(rp & PROPINT)) error(loc, "operands to '%s' operator must be integer", tokstr[op]); - l = exprconvert(l, typeintpromote(l->type)); - r = exprconvert(r, typeintpromote(r->type)); + l = exprpromote(l); + r = exprpromote(r); t = l->type; break; default: @@ -597,7 +614,7 @@ postfixexpr(struct scope *s, struct expr *r) error(&tok.loc, "too many arguments for function call"); *end = assignexpr(s); if (!t->func.isprototype || (t->func.isvararg && !p)) - *end = exprconvert(*end, typeargpromote((*end)->type)); + *end = exprpromote(*end); else *end = exprconvert(*end, p->type); end = &(*end)->next; @@ -682,14 +699,16 @@ unaryexpr(struct scope *s) e = castexpr(s); if (!(e->type->prop & PROPARITH)) error(&tok.loc, "operand of unary '+' operator must have arithmetic type"); - e = exprconvert(e, typeintpromote(e->type)); + if (e->type->prop & PROPINT) + e = exprpromote(e); break; case TSUB: next(); e = castexpr(s); if (!(e->type->prop & PROPARITH)) error(&tok.loc, "operand of unary '-' operator must have arithmetic type"); - e = exprconvert(e, typeintpromote(e->type)); + if (e->type->prop & PROPINT) + e = exprpromote(e); e = mkbinaryexpr(&tok.loc, TSUB, mkconstexpr(&typeint, 0), e); break; case TBNOT: @@ -697,7 +716,7 @@ unaryexpr(struct scope *s) e = castexpr(s); if (!(e->type->prop & PROPINT)) error(&tok.loc, "operand of '~' operator must have integer type"); - e = exprconvert(e, typeintpromote(e->type)); + e = exprpromote(e); e = mkbinaryexpr(&tok.loc, TXOR, e, mkconstexpr(e->type, -1)); break; case TLNOT: diff --git a/qbe.c b/qbe.c @@ -382,7 +382,7 @@ mkfunc(char *name, struct type *t, struct scope *s) for (p = t->func.params; p; p = p->next) { if (!p->name) error(&tok.loc, "parameter name omitted in function definition"); - if (!t->func.isprototype && !typecompatible(p->type, typeargpromote(p->type))) + if (!t->func.isprototype && !typecompatible(p->type, typepromote(p->type, -1))) error(&tok.loc, "old-style function definition with parameter type incompatible with promoted type is not yet supported"); emittype(p->type); d = mkdecl(DECLOBJECT, p->type, p->qual, LINKNONE); diff --git a/stmt.c b/stmt.c @@ -127,7 +127,7 @@ stmt(struct func *f, struct scope *s) if (!(e->type->prop & PROPINT)) error(&tok.loc, "controlling expression of switch statement must have integer type"); - e = exprconvert(e, typeintpromote(e->type)); + e = exprpromote(e); label[0] = mkblock("switch_cond"); label[1] = mkblock("switch_join"); diff --git a/test/bitfield-integer-promotion-long.c b/test/bitfield-integer-promotion-long.c @@ -0,0 +1,6 @@ +struct {unsigned long x:31;} s1; +struct {unsigned long x:32;} s2; +struct {unsigned long x:33;} s3; +int c1 = __builtin_types_compatible_p(__typeof__(+s1.x), int); +int c2 = __builtin_types_compatible_p(__typeof__(+s2.x), unsigned); +int c3 = __builtin_types_compatible_p(__typeof__(+s3.x), unsigned long); diff --git a/test/bitfield-integer-promotion-long.qbe b/test/bitfield-integer-promotion-long.qbe @@ -0,0 +1,6 @@ +export data $c1 = align 4 { w 1, } +export data $c2 = align 4 { w 1, } +export data $c3 = align 4 { w 1, } +export data $s1 = align 8 { z 8 } +export data $s2 = align 8 { z 8 } +export data $s3 = align 8 { z 8 } diff --git a/test/bitfield-integer-promotion.c b/test/bitfield-integer-promotion.c @@ -0,0 +1,7 @@ +struct { + unsigned a : 2; +} s; + +int main(void) { + return -1 > s.a; +} diff --git a/test/bitfield-integer-promotion.qbe b/test/bitfield-integer-promotion.qbe @@ -0,0 +1,17 @@ +export +function w $main() { +@start.1 +@body.2 + %.1 =w sub 0, 1 + %.2 =l copy $s + %.3 =l mul 0, 1 + %.4 =l add %.2, %.3 + %.5 =l copy %.4 + %.6 =w loaduw %.5 + %.7 =w shl %.6, 30 + %.8 =w shr %.7, 30 + %.9 =w copy %.8 + %.10 =w csgtw %.1, %.9 + ret %.10 +} +export data $s = align 4 { z 4 } diff --git a/type.c b/type.c @@ -149,7 +149,7 @@ typecompatible(struct type *t1, struct type *t2) return false; if (!t2->func.paraminfo) { for (p1 = t1->func.params; p1; p1 = p1->next) { - if (!typecompatible(p1->type, typeargpromote(p1->type))) + if (!typecompatible(p1->type, typepromote(p1->type, -1))) return false; } return true; @@ -157,7 +157,7 @@ typecompatible(struct type *t1, struct type *t2) for (p1 = t1->func.params, p2 = t2->func.params; p1 && p2; p1 = p1->next, p2 = p2->next) { if (p1->qual != p2->qual) return false; - tmp = t2->func.isprototype ? p2->type : typeargpromote(p2->type); + tmp = t2->func.isprototype ? p2->type : typepromote(p2->type, -1); if (!typecompatible(p1->type, tmp)) return false; } @@ -186,23 +186,20 @@ typecomposite(struct type *t1, struct type *t2) } struct type * -typeintpromote(struct type *t) -{ - if (t->prop & PROPINT && typerank(t) <= typerank(&typeint)) - return t->size < typeint.size || t->basic.issigned ? &typeint : &typeuint; - return t; -} - -struct type * -typeargpromote(struct type *t) +typepromote(struct type *t, unsigned width) { if (t == &typefloat) return &typedouble; - return typeintpromote(t); + if (t->prop & PROPINT && (typerank(t) <= typerank(&typeint) || width <= typeint.size * 8)) { + if (width == -1) + width = t->size * 8; + return width - t->basic.issigned < typeint.size * 8 ? &typeint : &typeuint; + } + return t; } struct type * -typecommonreal(struct type *t1, struct type *t2) +typecommonreal(struct type *t1, unsigned w1, struct type *t2, unsigned w2) { struct type *tmp; @@ -215,8 +212,8 @@ typecommonreal(struct type *t1, struct type *t2) return &typedouble; if (t1 == &typefloat || t2 == &typefloat) return &typefloat; - t1 = typeintpromote(t1); - t2 = typeintpromote(t2); + t1 = typepromote(t1, w1); + t2 = typepromote(t2, w2); if (t1 == t2) return t1; if (t1->basic.issigned == t2->basic.issigned)