begin using unsigned arithmetics internally as much as possible

(i.e. where signedness doesn’t matter, given -fwrapv) and note
where there’s work to do; note future improvements and additions;
optimise a little

mostly untested
This commit is contained in:
tg 2013-03-31 18:33:13 +00:00
parent ece1b28eaf
commit 6a745a01fa
1 changed files with 144 additions and 126 deletions

270
expr.c
View File

@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.62 2013/03/29 16:54:05 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/expr.c,v 1.63 2013/03/31 18:33:13 tg Exp $");
#if !HAVE_SILENT_IDIVWRAPV #if !HAVE_SILENT_IDIVWRAPV
#if !defined(MKSH_LEGACY_MODE) || HAVE_LONG_32BIT #if !defined(MKSH_LEGACY_MODE) || HAVE_LONG_32BIT
@ -67,7 +67,6 @@ enum token {
/* things that don't appear in the opinfo[] table */ /* things that don't appear in the opinfo[] table */
VAR, LIT, END, BAD VAR, LIT, END, BAD
}; };
#define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN) #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
/* precisions; used to be enum prec but we do arithmetics on it */ /* precisions; used to be enum prec but we do arithmetics on it */
@ -158,6 +157,7 @@ typedef struct expr_state {
bool natural; bool natural;
} Expr_state; } Expr_state;
/* to be replaced (later) */
#define bivui(x, op, y) (es->natural ? \ #define bivui(x, op, y) (es->natural ? \
(mksh_uari_t)((x)->val.u op (y)->val.u) : \ (mksh_uari_t)((x)->val.u op (y)->val.u) : \
(mksh_uari_t)((x)->val.i op (y)->val.i) \ (mksh_uari_t)((x)->val.i op (y)->val.i) \
@ -307,6 +307,32 @@ evalerr(Expr_state *es, enum error_type type, const char *str)
unwind(LAEXPR); unwind(LAEXPR);
} }
/* do a ++ or -- operation */
static struct tbl *
do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
{
struct tbl *vl;
mksh_uari_t oval;
assign_check(es, op, vasn);
vl = intvar(es, vasn);
oval = vl->val.u;
if (op == O_PLUSPLUS)
++vl->val.u;
else
--vl->val.u;
if (vasn->flag & INTEGER)
setint_v(vasn, vl, es->arith);
else
setint(vasn, vl->val.i);
if (!is_prefix)
/* undo the increment/decrement */
vl->val.u = oval;
return (vl);
}
static struct tbl * static struct tbl *
evalexpr(Expr_state *es, int prec) evalexpr(Expr_state *es, int prec)
{ {
@ -315,44 +341,67 @@ evalexpr(Expr_state *es, int prec)
mksh_uari_t res = 0; mksh_uari_t res = 0;
if (prec == P_PRIMARY) { if (prec == P_PRIMARY) {
op = es->tok; switch ((int)(op = es->tok)) {
if (op == O_BNOT || op == O_LNOT || op == O_MINUS || case O_BNOT:
op == O_PLUS) { case O_LNOT:
case O_MINUS:
case O_PLUS:
exprtoken(es); exprtoken(es);
vl = intvar(es, evalexpr(es, P_PRIMARY)); vl = intvar(es, evalexpr(es, P_PRIMARY));
if (op == O_BNOT) switch ((int)op) {
vl->val.i = ~vl->val.i; case O_BNOT:
else if (op == O_LNOT) vl->val.u = ~vl->val.u;
vl->val.i = !vl->val.i; break;
else if (op == O_MINUS) case O_LNOT:
vl->val.i = -vl->val.i; vl->val.u = !vl->val.u;
/* op == O_PLUS is a no-op */ break;
} else if (op == OPEN_PAREN) { case O_MINUS:
vl->val.u = -vl->val.u;
break;
case O_PLUS:
/* nop */
break;
}
break;
case OPEN_PAREN:
exprtoken(es); exprtoken(es);
vl = evalexpr(es, MAX_PREC); vl = evalexpr(es, MAX_PREC);
if (es->tok != CLOSE_PAREN) if (es->tok != CLOSE_PAREN)
evalerr(es, ET_STR, "missing )"); evalerr(es, ET_STR, "missing )");
exprtoken(es); exprtoken(es);
} else if (op == O_PLUSPLUS || op == O_MINUSMINUS) { break;
case O_PLUSPLUS:
case O_MINUSMINUS:
exprtoken(es); exprtoken(es);
vl = do_ppmm(es, op, es->val, true); vl = do_ppmm(es, op, es->val, true);
exprtoken(es); exprtoken(es);
} else if (op == VAR || op == LIT) { break;
case VAR:
case LIT:
vl = es->val; vl = es->val;
exprtoken(es); exprtoken(es);
} else { break;
default:
evalerr(es, ET_UNEXPECTED, NULL); evalerr(es, ET_UNEXPECTED, NULL);
/* NOTREACHED */ /* NOTREACHED */
} }
if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) { if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
vl = do_ppmm(es, es->tok, vl, false); vl = do_ppmm(es, es->tok, vl, false);
exprtoken(es); exprtoken(es);
} }
return (vl); return (vl);
/* prec == P_PRIMARY */
} }
vl = evalexpr(es, prec - 1); vl = evalexpr(es, prec - 1);
for (op = es->tok; IS_BINOP(op) && opinfo[(int)op].prec == prec; while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA &&
op = es->tok) { opinfo[(int)op].prec == prec) {
exprtoken(es); exprtoken(es);
vasn = vl; vasn = vl;
if (op != O_ASN) if (op != O_ASN)
@ -362,69 +411,92 @@ evalexpr(Expr_state *es, int prec)
if (!es->noassign) if (!es->noassign)
assign_check(es, op, vasn); assign_check(es, op, vasn);
vr = intvar(es, evalexpr(es, P_ASSIGN)); vr = intvar(es, evalexpr(es, P_ASSIGN));
} else if (op != O_TERN && op != O_LAND && op != O_LOR) } else if (op == O_TERN) {
bool ev = vl->val.u != 0;
if (!ev)
es->noassign++;
vl = evalexpr(es, MAX_PREC);
if (!ev)
es->noassign--;
if (es->tok != CTERN)
evalerr(es, ET_STR, "missing :");
exprtoken(es);
if (ev)
es->noassign++;
vr = evalexpr(es, P_TERN);
if (ev)
es->noassign--;
vl = ev ? vl : vr;
continue;
} else if (op != O_LAND && op != O_LOR)
vr = intvar(es, evalexpr(es, prec - 1)); vr = intvar(es, evalexpr(es, prec - 1));
if ((op == O_DIV || op == O_MOD || op == O_DIVASN ||
op == O_MODASN) && vr->val.i == 0) {
if (es->noassign)
vr->val.i = 1;
else
evalerr(es, ET_STR, "zero divisor");
}
switch ((int)op) { switch ((int)op) {
case O_TIMES:
case O_TIMESASN:
res = bivui(vl, *, vr);
break;
case O_DIV: case O_DIV:
case O_DIVASN: case O_DIVASN:
#if !HAVE_SILENT_IDIVWRAPV
/*
* we are doing the comparisons here for the
* signed arithmetics (!es->natural) case,
* but the exact value checks and the bypass
* case assignments are done unsignedly as
* several compilers bitch around otherwise
*/
if (!es->natural &&
vl->val.u == IDIVWRAPV_VL &&
vr->val.u == IDIVWRAPV_VR) {
/* -2147483648 / -1 = 2147483648 */
/* this ^ is really (1 << 31) though */
res = IDIVWRAPV_VL;
} else
#endif
res = bivui(vl, /, vr);
break;
case O_MOD: case O_MOD:
case O_MODASN: case O_MODASN:
/*
* actually, the entire division routine needs
* a more high-level implementation using only
* unsigned arithmetics
*/
switch (vr->val.u) {
#if !HAVE_SILENT_IDIVWRAPV #if !HAVE_SILENT_IDIVWRAPV
/* see O_DIV / O_DIVASN for the reason behind this */ case IDIVWRAPV_VR:
if (!es->natural && if (vl->val.u == IDIVWRAPV_VL && !es->natural) {
vl->val.u == IDIVWRAPV_VL && /*
vr->val.u == IDIVWRAPV_VR) { * these are the correct precalculated
/* -2147483648 % -1 = 0 */ * values for signed division of the
res = 0; * most negative number (which has no
} else * positive representation) by -1:
* result INTMIN, modulo 0
*/
res = op == O_DIV || op == O_DIVASN ?
IDIVWRAPV_VL : 0;
break;
}
if (0)
/* FALLTHROUGH */
#endif #endif
res = bivui(vl, %, vr); case 0:
{
if (!es->noassign) {
evalerr(es, ET_STR,
"zero divisor");
}
vr->val.u = 1;
}
/* FALLTHROUGH */
default:
res = op == O_DIV || op == O_DIVASN ?
bivui(vl, /, vr) : bivui(vl, %, vr);
}
break;
case O_TIMES:
case O_TIMESASN:
/* all bivui users need special handling */
res = bivui(vl, *, vr);
break; break;
case O_PLUS: case O_PLUS:
case O_PLUSASN: case O_PLUSASN:
res = bivui(vl, +, vr); res = vl->val.u + vr->val.u;
break; break;
case O_MINUS: case O_MINUS:
case O_MINUSASN: case O_MINUSASN:
res = bivui(vl, -, vr); res = vl->val.u - vr->val.u;
break; break;
case O_LSHIFT: case O_LSHIFT:
case O_LSHIFTASN: case O_LSHIFTASN:
res = bivui(vl, <<, vr); /* how about ANDing with 31 (except lksh)? */
res = vl->val.u << vr->val.u;
break; break;
case O_RSHIFT: case O_RSHIFT:
case O_RSHIFTASN: case O_RSHIFTASN:
res = bivui(vl, >>, vr); res = bivui(vl, >>, vr);
break; break;
/* how about rotation? */
case O_LT: case O_LT:
res = bivui(vl, <, vr); res = bivui(vl, <, vr);
break; break;
@ -438,66 +510,45 @@ evalexpr(Expr_state *es, int prec)
res = bivui(vl, >=, vr); res = bivui(vl, >=, vr);
break; break;
case O_EQ: case O_EQ:
res = bivui(vl, ==, vr); res = vl->val.u == vr->val.u;
break; break;
case O_NE: case O_NE:
res = bivui(vl, !=, vr); res = vl->val.u != vr->val.u;
break; break;
case O_BAND: case O_BAND:
case O_BANDASN: case O_BANDASN:
res = bivui(vl, &, vr); res = vl->val.u & vr->val.u;
break; break;
case O_BXOR: case O_BXOR:
case O_BXORASN: case O_BXORASN:
res = bivui(vl, ^, vr); res = vl->val.u ^ vr->val.u;
break; break;
case O_BOR: case O_BOR:
case O_BORASN: case O_BORASN:
res = bivui(vl, |, vr); res = vl->val.u | vr->val.u;
break; break;
case O_LAND: case O_LAND:
if (!vl->val.i) if (!vl->val.u)
es->noassign++; es->noassign++;
vr = intvar(es, evalexpr(es, prec - 1)); vr = intvar(es, evalexpr(es, prec - 1));
res = bivui(vl, &&, vr); res = vl->val.u && vr->val.u;
if (!vl->val.i) if (!vl->val.u)
es->noassign--; es->noassign--;
break; break;
case O_LOR: case O_LOR:
if (vl->val.i) if (vl->val.u)
es->noassign++; es->noassign++;
vr = intvar(es, evalexpr(es, prec - 1)); vr = intvar(es, evalexpr(es, prec - 1));
res = bivui(vl, ||, vr); res = vl->val.u || vr->val.u;
if (vl->val.i) if (vl->val.u)
es->noassign--; es->noassign--;
break; break;
case O_TERN:
{
bool ev = vl->val.i != 0;
if (!ev)
es->noassign++;
vl = evalexpr(es, MAX_PREC);
if (!ev)
es->noassign--;
if (es->tok != CTERN)
evalerr(es, ET_STR, "missing :");
exprtoken(es);
if (ev)
es->noassign++;
vr = evalexpr(es, P_TERN);
if (ev)
es->noassign--;
vl = ev ? vl : vr;
}
break;
case O_ASN: case O_ASN:
res = vr->val.u;
break;
case O_COMMA: case O_COMMA:
res = vr->val.u; res = vr->val.u;
break; break;
} }
if (IS_ASSIGNOP(op)) { if (IS_ASSIGNOP(op)) {
vr->val.u = res; vr->val.u = res;
if (!es->noassign) { if (!es->noassign) {
@ -507,7 +558,7 @@ evalexpr(Expr_state *es, int prec)
setint(vasn, (mksh_ari_t)res); setint(vasn, (mksh_ari_t)res);
} }
vl = vr; vl = vr;
} else if (op != O_TERN) } else
vl->val.u = res; vl->val.u = res;
} }
return (vl); return (vl);
@ -610,39 +661,6 @@ exprtoken(Expr_state *es)
es->tokp = cp; es->tokp = cp;
} }
/* Do a ++ or -- operation */
static struct tbl *
do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
{
struct tbl *vl;
mksh_ari_t oval;
assign_check(es, op, vasn);
vl = intvar(es, vasn);
oval = vl->val.i;
if (op == O_PLUSPLUS) {
if (es->natural)
++vl->val.u;
else
++vl->val.i;
} else {
if (es->natural)
--vl->val.u;
else
--vl->val.i;
}
if (vasn->flag & INTEGER)
setint_v(vasn, vl, es->arith);
else
setint(vasn, vl->val.i);
if (!is_prefix)
/* undo the increment/decrement */
vl->val.i = oval;
return (vl);
}
static void static void
assign_check(Expr_state *es, enum token op, struct tbl *vasn) assign_check(Expr_state *es, enum token op, struct tbl *vasn)
{ {