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:
parent
ece1b28eaf
commit
6a745a01fa
270
expr.c
270
expr.c
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue