summaryrefslogtreecommitdiff
path: root/utils/cc
diff options
context:
space:
mode:
Diffstat (limited to 'utils/cc')
-rw-r--r--utils/cc/com.c186
1 files changed, 184 insertions, 2 deletions
diff --git a/utils/cc/com.c b/utils/cc/com.c
index af16e8d9..558077b7 100644
--- a/utils/cc/com.c
+++ b/utils/cc/com.c
@@ -1,5 +1,7 @@
#include "cc.h"
+int compar(Node*, int);
+
void
complex(Node *n)
{
@@ -985,6 +987,8 @@ loop:
case OHI:
ccom(l);
ccom(r);
+ if(compar(n, 0) || compar(n, 1))
+ break;
relcon(l, r);
relcon(r, l);
goto common;
@@ -1082,7 +1086,7 @@ loop:
*n = *l;
break;
}
- goto commun;
+ goto commute;
case OAND:
ccom(l);
@@ -1096,7 +1100,7 @@ loop:
break;
}
- commun:
+ commute:
/* look for commutative constant */
if(r->op == OCONST) {
if(l->op == n->op) {
@@ -1162,3 +1166,181 @@ loop:
evconst(n);
}
}
+
+/* OEQ, ONE, OLE, OLS, OLT, OLO, OGE, OHS, OGT, OHI */
+static char *cmps[12] =
+{
+ "==", "!=", "<=", "<=", "<", "<", ">=", ">=", ">", ">",
+};
+
+/* 128-bit numbers */
+typedef struct Big Big;
+struct Big
+{
+ vlong a;
+ uvlong b;
+};
+static int
+cmp(Big x, Big y)
+{
+ if(x.a != y.a){
+ if(x.a < y.a)
+ return -1;
+ return 1;
+ }
+ if(x.b != y.b){
+ if(x.b < y.b)
+ return -1;
+ return 1;
+ }
+ return 0;
+}
+static Big
+add(Big x, int y)
+{
+ uvlong ob;
+
+ ob = x.b;
+ x.b += y;
+ if(y > 0 && x.b < ob)
+ x.a++;
+ if(y < 0 && x.b > ob)
+ x.a--;
+ return x;
+}
+
+Big
+big(vlong a, uvlong b)
+{
+ Big x;
+
+ x.a = a;
+ x.b = b;
+ return x;
+}
+
+int
+compar(Node *n, int reverse)
+{
+ Big lo, hi, x;
+ int op;
+ char xbuf[40], cmpbuf[50];
+ Node *l, *r;
+ Type *lt, *rt;
+
+ /*
+ * The point of this function is to diagnose comparisons
+ * that can never be true or that look misleading because
+ * of the `usual arithmetic conversions'. As an example
+ * of the latter, if x is a ulong, then if(x <= -1) really means
+ * if(x <= 0xFFFFFFFF), while if(x <= -1LL) really means
+ * what it says (but 8c compiles it wrong anyway).
+ */
+
+ if(reverse){
+ r = n->left;
+ l = n->right;
+ op = comrel[relindex(n->op)];
+ }else{
+ l = n->left;
+ r = n->right;
+ op = n->op;
+ }
+
+ /*
+ * Skip over left casts to find out the original expression range.
+ */
+ while(l->op == OCAST)
+ l = l->left;
+ if(l->op == OCONST)
+ return 0;
+ lt = l->type;
+ if(l->op == ONAME && l->sym->type){
+ lt = l->sym->type;
+ if(lt->etype == TARRAY)
+ lt = lt->link;
+ }
+ if(lt == T)
+ return 0;
+ if(lt->etype == TXXX || lt->etype > TUVLONG)
+ return 0;
+
+ /*
+ * Skip over the right casts to find the on-screen value.
+ */
+ if(r->op != OCONST)
+ return 0;
+ while(r->oldop == OCAST && !r->xcast)
+ r = r->left;
+ rt = r->type;
+ if(rt == T)
+ return 0;
+
+ x.b = r->vconst;
+ x.a = 0;
+ if((rt->etype&1) && r->vconst < 0) /* signed negative */
+ x.a = ~(uvlong)0;
+
+ if((lt->etype&1)==0){
+ /* unsigned */
+ lo = big(0, 0);
+ if(lt->width == 8)
+ hi = big(0, ~(uvlong)0);
+ else
+ hi = big(0, ((uvlong)1<<(l->type->width*8))-1);
+ }else{
+ lo = big(~(uvlong)0, -((uvlong)1<<(l->type->width*8-1)));
+ hi = big(0, ((uvlong)1<<(l->type->width*8-1))-1);
+ }
+
+ switch(op){
+ case OLT:
+ case OLO:
+ case OGE:
+ case OHS:
+ if(cmp(x, lo) <= 0)
+ goto useless;
+ if(cmp(x, add(hi, 1)) >= 0)
+ goto useless;
+ break;
+ case OLE:
+ case OLS:
+ case OGT:
+ case OHI:
+ if(cmp(x, add(lo, -1)) <= 0)
+ goto useless;
+ if(cmp(x, hi) >= 0)
+ goto useless;
+ break;
+ case OEQ:
+ case ONE:
+ /*
+ * Don't warn about comparisons if the expression
+ * is as wide as the value: the compiler-supplied casts
+ * will make both outcomes possible.
+ */
+ if(lt->width >= rt->width && debug['w'] < 2)
+ return 0;
+ if(cmp(x, lo) < 0 || cmp(x, hi) > 0)
+ goto useless;
+ break;
+ }
+ return 0;
+
+useless:
+ if((x.a==0 && x.b<=9) || (x.a==~(uvlong)0 && x.b >= -(uvlong)9))
+ snprint(xbuf, sizeof xbuf, "%lld", x.b);
+ else if(x.a == 0)
+ snprint(xbuf, sizeof xbuf, "%#llux", x.b);
+ else
+ snprint(xbuf, sizeof xbuf, "%#llx", x.b);
+ if(reverse)
+ snprint(cmpbuf, sizeof cmpbuf, "%s %s %T",
+ xbuf, cmps[relindex(n->op)], lt);
+ else
+ snprint(cmpbuf, sizeof cmpbuf, "%T %s %s",
+ lt, cmps[relindex(n->op)], xbuf);
+ warn(n, "useless or misleading comparison: %s", cmpbuf);
+ return 0;
+}
+