Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
38ccc34
First try
nordlow Jan 27, 2015
bc850d5
First version that does anything useful
nordlow Jan 27, 2015
cf7f408
Add utility function lowerIsInBounds
nordlow Jan 27, 2015
991c6bd
Set lowerIsLessThanUpper to true for [$ .. $] slicings
nordlow Jan 27, 2015
80f1aaa
Simplify isOpDollar by making use of Id::dollar
nordlow Jan 27, 2015
717d482
Handle case were lower bound is zero
nordlow Jan 27, 2015
2be613e
Simplify
nordlow Jan 27, 2015
4df5e9a
Implement Step 3
nordlow Jan 28, 2015
a811f6e
Keep lowerIsLessThanUpper as true if existing logic cannot prove things
nordlow Jan 28, 2015
ad6bebb
Put lower before upper and remove intermediate
nordlow Jan 28, 2015
a56d4bf
Simplify and use toInteger
nordlow Jan 28, 2015
6079a6d
Implement Step 4
nordlow Jan 28, 2015
e23906c
Simplify
nordlow Jan 28, 2015
4c0afec
Working comparison of rational bounds
nordlow Jan 28, 2015
0cb9ae7
Be more restrictive
nordlow Jan 28, 2015
1074985
Simplify
nordlow Jan 28, 2015
3205b61
Turn lowerIsInBounds to a variable
nordlow Jan 28, 2015
2599088
Break out into SliceExp::Boundness and SliceExp::analyzeRelativeBound…
nordlow Jan 29, 2015
3079e38
Comment Boundness
nordlow Jan 29, 2015
63d2ac5
Various feedback fixes
nordlow Jan 29, 2015
f713a38
Use printf instead of warning
nordlow Jan 29, 2015
86d0907
Disable LOG_BOUNDNESS
nordlow Jan 29, 2015
f37f5b0
Use new function isInteger, move lowerIsInBounds, longer names for Bo…
nordlow Jan 29, 2015
0a70d45
Better dollar offset error checking for (Step 5)
nordlow Jan 29, 2015
d1997cc
Disable LOG_BOUNDNESS again
nordlow Jan 29, 2015
7e0a57f
Bugfix logic that sets lowerIsLessThanUpper
nordlow Jan 29, 2015
9bc7015
Disable LOG_BOUNDNESS again
nordlow Jan 30, 2015
64433ae
Add boundscheck for p*$, p >= 2
nordlow Jan 30, 2015
a571190
Better printing
nordlow Jan 30, 2015
3e7349b
Add lower limit < 0
nordlow Jan 31, 2015
7777ca0
Use isunsigned in analyzeSliceBound
nordlow Feb 1, 2015
9e61acb
Make all negative bounds an error including minus dollar
nordlow Feb 1, 2015
9abf724
Make use of conversion functions isXExp and error checking for - $ * N
nordlow Feb 1, 2015
a371fc2
Make isXExp into virtual members of Expression
nordlow Feb 2, 2015
3b5e9dc
Add debug print
nordlow Feb 3, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 271 additions & 4 deletions src/expression.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <ctype.h>
#include <math.h>
#include <assert.h>
#include <stdio.h>

#include "rmem.h"
#include "port.h"
Expand Down Expand Up @@ -146,7 +147,7 @@ Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad,
if (flag)
return NULL;
e1->error("this for %s needs to be type %s not type %s",
var->toChars(), ad->toChars(), t->toChars());
var->toChars(), ad->toChars(), t->toChars());
return new ErrorExp();
}
}
Expand Down Expand Up @@ -9812,6 +9813,177 @@ Expression *SliceExp::syntaxCopy()
return se;
}

bool isOpDollar(Expression* e, VarDeclaration *lengthVar)
{
return e->op == TOKvar && ((VarExp*)e)->var == lengthVar;
}

bool isInteger(Expression* e, dinteger_t* value)
{
if (e->op == TOKint64)
{
*value = e->toInteger();
return true;
}
else
{
return false;
}
}

// Decribes Boundness of a Slice Beginning or End Index.
enum Boundness
{
unknownBounds, // unknown whether inside or outside

insideBounds, // inclusive inside of [0 .. $]
atLowBound, // specialization of inside
atHighBound, // specialization of inside

outsideBounds, // outside of [0 .. $]
aboveHighBound, // specialization of outside
belowLowBound, // specialization of outside

belowHighBound,
aboveLowBound,
};

bool isInside(Boundness boundness)
{
return (boundness == insideBounds ||
boundness == atLowBound ||
boundness == atHighBound);
}

bool isOutside(Boundness boundness)
{
return (boundness == outsideBounds ||
boundness == aboveHighBound ||
boundness == belowLowBound);
}

bool LOG_BOUNDNESS = false;

/** Analyze Expression $(D e) as Slice Bound with Length Variable (Dollar) in $(D lengthVar).
*/
Boundness analyzeSliceBound(Expression* e,
VarDeclaration *lengthVar,
dinteger_t* p, uinteger_t* q, uinteger_t* off) // out arguments
{
if (e->op == TOKint64)
{
const dinteger_t value = e->toInteger();
if (value == 0)
{
if (LOG_BOUNDNESS) e->warning("avoiding boundscheck for 0");
*off = 0;
return atLowBound;
}
const sinteger_t svalue = (sinteger_t)value;
if (svalue < 0) // limit max slice bound index to 2^n-1, n=32 on 32-bit and n=64 on 64-bit
{
*off = value;
printf("%s is below low bound, e->toInteger() returns %ld, e->toUInteger() returns %ld\n",
e->toChars(),
e->toInteger(),
e->toUInteger());
return belowLowBound; // TODO aboveHighBound instead?
}
}
else if (isOpDollar(e, lengthVar))
{
if (LOG_BOUNDNESS) e->warning("avoiding boundscheck for $");
return atHighBound;
}
else if (NegExp* neg = e->isNegExp())
{
if (isOpDollar(neg->e1, lengthVar))
{
return outsideBounds;
}
}
else if (DivExp* div = e->isDivExp())
{
if (isOpDollar(div->e1, lengthVar) && isInteger(div->e2, q))
{
if (*q >= 1)
{
if (LOG_BOUNDNESS) e->warning("avoiding boundscheck for $/%ld", *q);
return insideBounds;
}
}
else if (div->e1->op == TOKmul && isInteger(div->e2, q))
{
MulExp* mul = (MulExp*)div->e1;
if (isOpDollar(mul->e1, lengthVar) && isInteger(mul->e2, p))
{
if (*p <= *q)
{
if (LOG_BOUNDNESS) e->warning("avoiding boundscheck for $*%ld/%ld", *p, *q);
return insideBounds;
}
else if (*p > *q)
{
return outsideBounds;
}
}
else if (isInteger(mul->e1, p) &&
isOpDollar(mul->e2, lengthVar))
{
if (*p <= *q)
{
if (LOG_BOUNDNESS) e->warning("avoiding boundscheck for $*%ld/%ld", *p, *q);
return insideBounds;
}
else if (*p > *q)
{
return outsideBounds;
}
}
}
}
else if (MulExp* mul = e->isMulExp())
{
if (NegExp* neg = mul->e1->isNegExp())
{
if (isOpDollar(neg->e1, lengthVar) && isInteger(mul->e2, p) &&
*p >= 2)
{
return belowLowBound;
}
}
else
{
if (((isOpDollar(mul->e1, lengthVar) && isInteger(mul->e2, p)) ||
(isInteger(mul->e1, p) && isOpDollar(mul->e2, lengthVar))) &&
*p >= 2)
{
return aboveHighBound; // iff $ != 0
}
}
}
else if (AddExp* add = e->isAddExp())
{
if (((isOpDollar(add->e1, lengthVar) && isInteger(add->e2, off)) ||
(isInteger(add->e1, off) && isOpDollar(add->e2, lengthVar))) &&
*off >= 1)
{
return aboveHighBound;
}
}
else if (MinExp* min = e->isMinExp())
{
if ((isOpDollar(min->e1, lengthVar) && isInteger(min->e2, off)))
{
return belowHighBound;
}
}

return unknownBounds;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like

return decl && decl->ident->len == 8 && memcmp(decl->ident->string, "__dollar", decl->ident->len) == 0;

to me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I'll wait and see if any more logic has to be added :) If not I'll change it.


#define DINTEGER_UNDEFINED (18446744073709551615ULL)

Expression *SliceExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
Expand Down Expand Up @@ -9938,7 +10110,7 @@ Expression *SliceExp::semantic(Scope *sc)
if (e1->op == TOKerror)
return e1;
error("%s cannot be sliced with []",
t1b->ty == Tvoid ? e1->toChars() : t1b->toChars());
t1b->ty == Tvoid ? e1->toChars() : t1b->toChars());
return new ErrorExp();
}

Expand Down Expand Up @@ -10046,6 +10218,98 @@ Expression *SliceExp::semantic(Scope *sc)
lwr = lwr->optimize(WANTvalue);
upr = upr->optimize(WANTvalue);

// avoid slice bounds-checking
{
// lower
dinteger_t lwrM = 1; // non-one means [ $*lwrM .. _ ]
uinteger_t lwrD = 1; // non-one means [ $/lwrD .. _ ]
uinteger_t lwrO = DINTEGER_UNDEFINED; // offset
const Boundness lwrBoundness = analyzeSliceBound(lwr, this->lengthVar, &lwrM, &lwrD, &lwrO);
bool lowerIsInBounds = false;
if (isInside(lwrBoundness))
{
lowerIsInBounds = true;
}
else if (isOutside(lwrBoundness))
{
char buf[1024];
int tot = sizeof(buf);
int off = 0;
off += snprintf(&buf[off], tot-off, "lower slice limit ");
if (lwrM != 1 && lwrD != 1 && off < tot)
{
off += snprintf(&buf[off], tot-off, "$");
}
if (lwrM != 1 && off < tot)
{
off += snprintf(&buf[off], tot-off, "*%lld", (unsigned long long)lwrM);
}
if (lwrD != 1 && off < tot)
{
off += snprintf(&buf[off], tot-off, "/%lld", (unsigned long long)lwrD);
}
if (lwrO != 0 && lwrO != DINTEGER_UNDEFINED && off < tot)
{
off += snprintf(&buf[off], tot-off, "+%lld", (unsigned long long)lwrO);
}
off += snprintf(&buf[off], tot-off, " is out of bounds");
lwr->error(buf);
}

// upper
dinteger_t uprM = 1; // non-one means [ $*uprM .. _ ]
uinteger_t uprD = 1; // non-one means [ $/uprD .. _ ]
uinteger_t uprO = DINTEGER_UNDEFINED;
const Boundness uprBoundness = analyzeSliceBound(upr, this->lengthVar, &uprM, &uprD, &uprO);
if (isInside(uprBoundness))
{
this->upperIsInBounds = true;
}
else if (isOutside(uprBoundness))
{
char buf[1024];
int tot = sizeof(buf);
int off = 0;
off += snprintf(&buf[off], tot-off, "upper slice limit ");
off += snprintf(&buf[off], tot-off, "$");
if (uprM != 1 && uprD != 1 && off < tot)
{
off += snprintf(&buf[off], tot-off, "$");
}
if (uprM != 1 && off < tot)
{
off += snprintf(&buf[off], tot-off, "*%lld", (unsigned long long)uprM);
}
if (uprD != 1 && off < tot)
{
off += snprintf(&buf[off], tot-off, "/%lld", (unsigned long long)uprD);
}
if (uprO != 0 && uprO != DINTEGER_UNDEFINED && off < tot)
{
off += snprintf(&buf[off], tot-off, "+%lld", (unsigned long long)uprO);
}
off += snprintf(&buf[off], tot-off, " is out of bounds");
upr->error(buf);
}

// lower and upper
// [$*p/q .. $*r/s], p/q+o <= r/s+t => p*s + o*q*s <= r*q + t*q*s.
// TODO do we need to handle mul overflows for (lwrM*uprD <= uprM*lwrD)
if (((lwrBoundness == atLowBound && isInside(uprBoundness)) ||
(isInside(lwrBoundness) && uprBoundness == atHighBound) ||
(lwrBoundness == belowHighBound && uprBoundness == atHighBound) ||
(lwrBoundness == atLowBound && uprBoundness == aboveLowBound) ||
(lwrM*uprD <= uprM*lwrD && lwrO == 0 && uprO == 0) ||
(lwrM == 1 && lwrD == 1 &&
uprM == 1 && uprD == 1 &&
lwrO != DINTEGER_UNDEFINED && uprO != DINTEGER_UNDEFINED &&
lwrO <= uprO)))
{
lowerIsLessThanUpper = true;
if (LOG_BOUNDNESS) this->warning("Lower <= upper bound");
}
}

IntRange lwrRange = getIntRange(lwr);
IntRange uprRange = getIntRange(upr);

Expand All @@ -10063,12 +10327,15 @@ Expression *SliceExp::semantic(Scope *sc)
}
else if (t1b->ty == Tpointer)
{
this->upperIsInBounds = true;
this->upperIsInBounds = true; // no boundscheck for x.ptr[_ .. _]
}
else
assert(0);

this->lowerIsLessThanUpper = (lwrRange.imax <= uprRange.imin);
if (!this->lowerIsLessThanUpper)
{
this->lowerIsLessThanUpper = (lwrRange.imax <= uprRange.imin);
}

//printf("upperIsInBounds = %d lowerIsLessThanUpper = %d\n", upperIsInBounds, lowerIsLessThanUpper);
}
Expand Down
12 changes: 12 additions & 0 deletions src/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ class Expression : public RootObject

int isConst() { return ::isConst(this); }
virtual int isBool(int result);

virtual AddExp* isAddExp() { return NULL; }
virtual MinExp* isMinExp() { return NULL; }
virtual MulExp* isMulExp() { return NULL; }
virtual DivExp* isDivExp() { return NULL; }
virtual NegExp* isNegExp() { return NULL; }

Expression *op_overload(Scope *sc)
{
return ::op_overload(this, sc);
Expand Down Expand Up @@ -929,6 +936,7 @@ class NegExp : public UnaExp
Expression *semantic(Scope *sc);

void accept(Visitor *v) { v->visit(this); }
NegExp* isNegExp() { return this; }
};

class UAddExp : public UnaExp
Expand Down Expand Up @@ -1274,6 +1282,7 @@ class AddExp : public BinExp
Expression *semantic(Scope *sc);

void accept(Visitor *v) { v->visit(this); }
AddExp* isAddExp() { return this; }
};

class MinExp : public BinExp
Expand All @@ -1283,6 +1292,7 @@ class MinExp : public BinExp
Expression *semantic(Scope *sc);

void accept(Visitor *v) { v->visit(this); }
MinExp* isMinExp() { return this; }
};

class CatExp : public BinExp
Expand All @@ -1301,6 +1311,7 @@ class MulExp : public BinExp
Expression *semantic(Scope *sc);

void accept(Visitor *v) { v->visit(this); }
MulExp* isMulExp() { return this; }
};

class DivExp : public BinExp
Expand All @@ -1310,6 +1321,7 @@ class DivExp : public BinExp
Expression *semantic(Scope *sc);

void accept(Visitor *v) { v->visit(this); }
DivExp* isDivExp() { return this; }
};

class ModExp : public BinExp
Expand Down