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"
__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 !defined(MKSH_LEGACY_MODE) || HAVE_LONG_32BIT
@ -67,7 +67,6 @@ enum token {
/* things that don't appear in the opinfo[] table */
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)
/* precisions; used to be enum prec but we do arithmetics on it */
@ -158,6 +157,7 @@ typedef struct expr_state {
bool natural;
} Expr_state;
/* to be replaced (later) */
#define bivui(x, op, y) (es->natural ? \
(mksh_uari_t)((x)->val.u op (y)->val.u) : \
(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);
}
/* 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 *
evalexpr(Expr_state *es, int prec)
{
@ -315,44 +341,67 @@ evalexpr(Expr_state *es, int prec)
mksh_uari_t res = 0;
if (prec == P_PRIMARY) {
op = es->tok;
if (op == O_BNOT || op == O_LNOT || op == O_MINUS ||
op == O_PLUS) {
switch ((int)(op = es->tok)) {
case O_BNOT:
case O_LNOT:
case O_MINUS:
case O_PLUS:
exprtoken(es);
vl = intvar(es, evalexpr(es, P_PRIMARY));
if (op == O_BNOT)
vl->val.i = ~vl->val.i;
else if (op == O_LNOT)
vl->val.i = !vl->val.i;
else if (op == O_MINUS)
vl->val.i = -vl->val.i;
/* op == O_PLUS is a no-op */
} else if (op == OPEN_PAREN) {
switch ((int)op) {
case O_BNOT:
vl->val.u = ~vl->val.u;
break;
case O_LNOT:
vl->val.u = !vl->val.u;
break;
case O_MINUS:
vl->val.u = -vl->val.u;
break;
case O_PLUS:
/* nop */
break;
}
break;
case OPEN_PAREN:
exprtoken(es);
vl = evalexpr(es, MAX_PREC);
if (es->tok != CLOSE_PAREN)
evalerr(es, ET_STR, "missing )");
exprtoken(es);
} else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
break;
case O_PLUSPLUS:
case O_MINUSMINUS:
exprtoken(es);
vl = do_ppmm(es, op, es->val, true);
exprtoken(es);
} else if (op == VAR || op == LIT) {
break;
case VAR:
case LIT:
vl = es->val;
exprtoken(es);
} else {
break;
default:
evalerr(es, ET_UNEXPECTED, NULL);
/* NOTREACHED */
}
if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
vl = do_ppmm(es, es->tok, vl, false);
exprtoken(es);
}
return (vl);
/* prec == P_PRIMARY */
}
vl = evalexpr(es, prec - 1);
for (op = es->tok; IS_BINOP(op) && opinfo[(int)op].prec == prec;
op = es->tok) {
while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA &&
opinfo[(int)op].prec == prec) {
exprtoken(es);
vasn = vl;
if (op != O_ASN)
@ -362,69 +411,92 @@ evalexpr(Expr_state *es, int prec)
if (!es->noassign)
assign_check(es, op, vasn);
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));
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) {
case O_TIMES:
case O_TIMESASN:
res = bivui(vl, *, vr);
break;
case O_DIV:
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_MODASN:
/*
* actually, the entire division routine needs
* a more high-level implementation using only
* unsigned arithmetics
*/
switch (vr->val.u) {
#if !HAVE_SILENT_IDIVWRAPV
/* see O_DIV / O_DIVASN for the reason behind this */
if (!es->natural &&
vl->val.u == IDIVWRAPV_VL &&
vr->val.u == IDIVWRAPV_VR) {
/* -2147483648 % -1 = 0 */
res = 0;
} else
case IDIVWRAPV_VR:
if (vl->val.u == IDIVWRAPV_VL && !es->natural) {
/*
* these are the correct precalculated
* values for signed division of the
* most negative number (which has no
* positive representation) by -1:
* result INTMIN, modulo 0
*/
res = op == O_DIV || op == O_DIVASN ?
IDIVWRAPV_VL : 0;
break;
}
if (0)
/* FALLTHROUGH */
#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;
case O_PLUS:
case O_PLUSASN:
res = bivui(vl, +, vr);
res = vl->val.u + vr->val.u;
break;
case O_MINUS:
case O_MINUSASN:
res = bivui(vl, -, vr);
res = vl->val.u - vr->val.u;
break;
case O_LSHIFT:
case O_LSHIFTASN:
res = bivui(vl, <<, vr);
/* how about ANDing with 31 (except lksh)? */
res = vl->val.u << vr->val.u;
break;
case O_RSHIFT:
case O_RSHIFTASN:
res = bivui(vl, >>, vr);
break;
/* how about rotation? */
case O_LT:
res = bivui(vl, <, vr);
break;
@ -438,66 +510,45 @@ evalexpr(Expr_state *es, int prec)
res = bivui(vl, >=, vr);
break;
case O_EQ:
res = bivui(vl, ==, vr);
res = vl->val.u == vr->val.u;
break;
case O_NE:
res = bivui(vl, !=, vr);
res = vl->val.u != vr->val.u;
break;
case O_BAND:
case O_BANDASN:
res = bivui(vl, &, vr);
res = vl->val.u & vr->val.u;
break;
case O_BXOR:
case O_BXORASN:
res = bivui(vl, ^, vr);
res = vl->val.u ^ vr->val.u;
break;
case O_BOR:
case O_BORASN:
res = bivui(vl, |, vr);
res = vl->val.u | vr->val.u;
break;
case O_LAND:
if (!vl->val.i)
if (!vl->val.u)
es->noassign++;
vr = intvar(es, evalexpr(es, prec - 1));
res = bivui(vl, &&, vr);
if (!vl->val.i)
res = vl->val.u && vr->val.u;
if (!vl->val.u)
es->noassign--;
break;
case O_LOR:
if (vl->val.i)
if (vl->val.u)
es->noassign++;
vr = intvar(es, evalexpr(es, prec - 1));
res = bivui(vl, ||, vr);
if (vl->val.i)
res = vl->val.u || vr->val.u;
if (vl->val.u)
es->noassign--;
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:
res = vr->val.u;
break;
case O_COMMA:
res = vr->val.u;
break;
}
if (IS_ASSIGNOP(op)) {
vr->val.u = res;
if (!es->noassign) {
@ -507,7 +558,7 @@ evalexpr(Expr_state *es, int prec)
setint(vasn, (mksh_ari_t)res);
}
vl = vr;
} else if (op != O_TERN)
} else
vl->val.u = res;
}
return (vl);
@ -610,39 +661,6 @@ exprtoken(Expr_state *es)
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
assign_check(Expr_state *es, enum token op, struct tbl *vasn)
{