Skip to content

Commit

Permalink
Merge pull request #910 from donc/ctfe7987sliceequals
Browse files Browse the repository at this point in the history
7987 [CTFE] cannot compare arrays of slices, + improve speed
  • Loading branch information
WalterBright committed Apr 27, 2012
2 parents b6f3c3e + 5c2b247 commit 4f85b43
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 29 deletions.
42 changes: 42 additions & 0 deletions src/constfold.c
Expand Up @@ -1541,6 +1541,48 @@ void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int f
memcpy(s + firstIndex * sz, newstr->string, sz * newstr->len);
}

/* Compare a string slice with another string slice.
* Conceptually equivalent to memcmp( se1[lo1..lo1+len], se2[lo2..lo2+len])
*/
int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len)
{
unsigned char *s1 = (unsigned char *)se1->string;
unsigned char *s2 = (unsigned char *)se2->string;
size_t sz = se1->sz;
assert(sz == se2->sz);

return memcmp(s1 + sz * lo1, s2 + sz * lo2, sz * len);
}

/* Compare a string slice with an array literal slice
* Conceptually equivalent to memcmp( se1[lo1..lo1+len], ae2[lo2..lo2+len])
*/
int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len)
{
unsigned char *s = (unsigned char *)se1->string;
size_t sz = se1->sz;

int c = 0;

for (size_t j = 0; j < len; j++)
{
unsigned value = (unsigned)((*ae2->elements)[j + lo2]->toInteger());
unsigned svalue;
switch (sz)
{
case 1: svalue = s[j + lo1]; break;
case 2: svalue = ((unsigned short *)s)[j+lo1]; break;
case 4: svalue = ((unsigned *)s)[j + lo1]; break;
default:
assert(0);
}
int c = svalue - value;
if (c)
return c;
}
return 0;
}

/* Also return EXP_CANT_INTERPRET if this fails
*/
Expression *Cat(Type *type, Expression *e1, Expression *e2)
Expand Down
5 changes: 4 additions & 1 deletion src/expression.h
Expand Up @@ -793,7 +793,7 @@ struct BinExp : Expression
Expression *interpretCommon(InterState *istate, CtfeGoal goal,
Expression *(*fp)(Type *, Expression *, Expression *));
Expression *interpretCommon2(InterState *istate, CtfeGoal goal,
Expression *(*fp)(TOK, Type *, Expression *, Expression *));
Expression *(*fp)(Loc, TOK, Type *, Expression *, Expression *));
Expression *interpretAssignCommon(InterState *istate, CtfeGoal goal,
Expression *(*fp)(Type *, Expression *, Expression *), int post = 0);
Expression *arrayOp(Scope *sc);
Expand Down Expand Up @@ -1710,5 +1710,8 @@ void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *n
void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, int firstIndex);
void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int firstIndex);

int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len);
int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len);


#endif /* DMD_EXPRESSION_H */
253 changes: 225 additions & 28 deletions src/interpret.c
Expand Up @@ -27,6 +27,8 @@
#include "attrib.h" // for AttribDeclaration

#include "template.h"
#include "port.h"
int RealEquals(real_t x1, real_t x2);


#define LOG 0
Expand Down Expand Up @@ -221,6 +223,7 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc,
Expression *scrubReturnValue(Loc loc, Expression *e);
bool isAssocArray(Type *t);
bool isPointer(Type *t);
Expression *ctfeEqual(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2);

// CTFE only expressions
#define TOKclassreference ((TOK)(TOKMAX+1))
Expand Down Expand Up @@ -952,19 +955,6 @@ uinteger_t resolveArrayLength(Expression *e)
return 0;
}

// As Equal, but resolves slices before comparing
Expression *ctfeEqual(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2)
{
if (e1->op == TOKslice)
e1 = resolveSlice(e1);
if (e2->op == TOKslice)
e2 = resolveSlice(e2);
Expression *e = Equal(op, type, e1, e2);
if (e == EXP_CANT_INTERPRET)
error(loc, "cannot evaluate %s==%s at compile time", e1->toChars(), e2->toChars());
return e;
}

Expression *ctfeCat(Type *type, Expression *e1, Expression *e2)
{
Loc loc = e1->loc;
Expand Down Expand Up @@ -2857,7 +2847,7 @@ BIN_INTERPRET(Pow)
#endif


typedef Expression *(*fp2_t)(enum TOK, Type *, Expression *, Expression *);
typedef Expression *(*fp2_t)(Loc loc, enum TOK, Type *, Expression *, Expression *);

// Return EXP_CANT_INTERPRET if they point to independent memory blocks
Expression *comparePointers(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2)
Expand Down Expand Up @@ -2918,21 +2908,227 @@ Expression *comparePointers(Loc loc, enum TOK op, Type *type, Expression *e1, Ex
return new IntegerExp(loc, cmp, type);
}

Expression *ctfeIdentity(enum TOK op, Type *type, Expression *e1, Expression *e2)

int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2);

/* Conceptually the same as memcmp(e1, e2).
* e1 and e2 may be strings, arrayliterals, or slices.
* For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
* For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
*/
int ctfeCmpArrays(Loc loc, Expression *e1, Expression *e2, uinteger_t len)
{
// Resolve slices, if necessary
uinteger_t lo1 = 0;
uinteger_t lo2 = 0;

Expression *x = e1;
if (x->op == TOKslice)
{ lo1 = ((SliceExp *)x)->lwr->toInteger();
x = ((SliceExp*)x)->e1;
}
StringExp *se1 = (x->op == TOKstring) ? (StringExp *)x : 0;
ArrayLiteralExp *ae1 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0;

x = e2;
if (x->op == TOKslice)
{ lo2 = ((SliceExp *)x)->lwr->toInteger();
x = ((SliceExp*)x)->e1;
}
StringExp *se2 = (x->op == TOKstring) ? (StringExp *)x : 0;
ArrayLiteralExp *ae2 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0;

// Now both must be either TOKarrayliteral or TOKstring
if (se1 && se2)
return sliceCmpStringWithString(se1, se2, lo1, lo2, len);
if (se1 && ae2)
return sliceCmpStringWithArray(se1, ae2, lo1, lo2, len);
if (se2 && ae1)
return -sliceCmpStringWithArray(se2, ae1, lo2, lo1, len);

assert (ae1 && ae2);
// Comparing two array literals. This case is potentially recursive.
// If they aren't strings, we just need an equality check rather than
// a full cmp.
bool needCmp = ae1->type->nextOf()->isintegral();
for (size_t i = 0; i < len; i++)
{ Expression *ee1 = (*ae1->elements)[lo1 + i];
Expression *ee2 = (*ae2->elements)[lo2 + i];
if (needCmp)
{ int c = ee1->toInteger() - ee2->toInteger();
if (c)
return c;
}
else
{ if (ctfeRawCmp(loc, ee1, ee2))
return 1;
}
}
return 0;
}

bool isArray(Expression *e)
{
return e->op == TOKarrayliteral || e->op == TOKstring ||
e->op == TOKslice || e->op == TOKnull;
}

/* For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
* For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
*/
int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2)
{
if (e1->op == TOKclassreference || e2->op == TOKclassreference)
{
int cmp = 0;
if (e1->op == TOKclassreference && e2->op == TOKclassreference &&
{ if (e1->op == TOKclassreference && e2->op == TOKclassreference &&
((ClassReferenceExp *)e1)->value == ((ClassReferenceExp *)e2)->value)
cmp = 1;
if (op == TOKnotidentity || op == TOKnotequal)
cmp ^= 1;
return new IntegerExp(e1->loc, cmp, type);
return 0;
return 1;
}
if (e1->op == TOKnull && e2->op == TOKnull)
return 0;

if (e1->type->ty == Tpointer && e2->type->ty == Tpointer)
{ // Can only be an equality test.
if (e1->op == TOKnull && e2->op == TOKnull)
return 0;
dinteger_t ofs1, ofs2;
Expression *agg1 = getAggregateFromPointer(e1, &ofs1);
Expression *agg2 = getAggregateFromPointer(e2, &ofs2);
if ((agg1 == agg2) || (agg1->op == TOKvar && agg2->op == TOKvar &&
((VarExp *)agg1)->var == ((VarExp *)agg2)->var))
{ if (ofs1 == ofs2)
return 0;
}
return 1;
}
if (isArray(e1) && isArray(e2))
{
uinteger_t len1 = resolveArrayLength(e1);
uinteger_t len2 = resolveArrayLength(e2);
if (len1 != len2) // only for equality
return len1 - len2;
if (len1 == 0 || len2 == 0)
return len1 - len2; // Equal - both are empty
return ctfeCmpArrays(loc, e1, e2, len1);
}
if (e1->type->isintegral())
{
return e1->toInteger() - e2->toInteger();
}
real_t r1;
real_t r2;
if (e1->type->isreal())
{
r1 = e1->toReal();
r2 = e2->toReal();
goto L1;
}
else if (e1->type->isimaginary())
{
r1 = e1->toImaginary();
r2 = e2->toImaginary();
L1:
#if __DMC__
return (r1 != r2);
#else
if (Port::isNan(r1) || Port::isNan(r2)) // if unordered
{
return 1;
}
else
{
return (r1 != r2);
}
#endif
}
else if (e1->type->iscomplex())
{
return e1->toComplex() != e2->toComplex();
}

if (e1->op == TOKstructliteral && e2->op == TOKstructliteral)
{ StructLiteralExp *es1 = (StructLiteralExp *)e1;
StructLiteralExp *es2 = (StructLiteralExp *)e2;
// For structs, we only need to return 0 or 1 (< and > aren't legal).

if (es1->sd != es2->sd)
return 1;
else if ((!es1->elements || !es1->elements->dim) &&
(!es2->elements || !es2->elements->dim))
return 0; // both arrays are empty
else if (!es1->elements || !es2->elements)
return 1;
else if (es1->elements->dim != es2->elements->dim)
return 1;
else
{
for (size_t i = 0; i < es1->elements->dim; i++)
{ Expression *ee1 = (*es1->elements)[i];
Expression *ee2 = (*es2->elements)[i];

if (ee1 == ee2)
continue;
if (!ee1 || !ee2)
return 1;
int cmp = ctfeRawCmp(loc, ee1, ee2);
if (cmp)
return 1;
}
return 0; // All elements are equal
}
}
error(loc, "CTFE internal error: bad compare");
assert(0);
}

// As Equal, but resolves slices before comparing
Expression *ctfeEqual(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2)
{
int cmp = !ctfeRawCmp(loc, e1, e2);
if (op == TOKnotequal)
cmp ^= 1;
return new IntegerExp(loc, cmp, type);
}

Expression *ctfeIdentity(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2)
{
int cmp;
if (e1->op == TOKnull)
{
cmp = (e2->op == TOKnull);
}
return Identity(op, type, e1, e2);
else if (e2->op == TOKnull)
{
cmp = 0;
}
else if (e1->op == TOKsymoff && e2->op == TOKsymoff)
{
SymOffExp *es1 = (SymOffExp *)e1;
SymOffExp *es2 = (SymOffExp *)e2;
cmp = (es1->var == es2->var && es1->offset == es2->offset);
}
else if (e1->type->isreal())
cmp = RealEquals(e1->toReal(), e2->toReal());
else if (e1->type->isimaginary())
cmp = RealEquals(e1->toImaginary(), e2->toImaginary());
else if (e1->type->iscomplex())
{ complex_t v1 = e1->toComplex();
complex_t v2 = e2->toComplex();
cmp = RealEquals(creall(v1), creall(v2)) &&
RealEquals(cimagl(v1), cimagl(v1));
}
else
cmp = !ctfeRawCmp(loc, e1, e2);

if (op == TOKnotidentity || op == TOKnotequal)
cmp ^= 1;
return new IntegerExp(loc, cmp, type);
}

Expression *ctfeCmp(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2)
{
return Cmp(op, type, e1, e2);
}

Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp)
{ Expression *e;
Expand Down Expand Up @@ -2991,7 +3187,7 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp
error("cannot compare %s at compile time", e2->toChars());
goto Lcant;
}
e = (*fp)(op, type, e1, e2);
e = (*fp)(loc, op, type, e1, e2);
if (e == EXP_CANT_INTERPRET)
error("%s cannot be interpreted at compile time", toChars());
return e;
Expand All @@ -3006,9 +3202,9 @@ Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \
return interpretCommon2(istate, goal, &opfunc); \
}

BIN_INTERPRET2(Equal, Equal)
BIN_INTERPRET2(Equal, ctfeEqual)
BIN_INTERPRET2(Identity, ctfeIdentity)
BIN_INTERPRET2(Cmp, Cmp)
BIN_INTERPRET2(Cmp, ctfeCmp)

/* Helper functions for BinExp::interpretAssignCommon
*/
Expand Down Expand Up @@ -3518,7 +3714,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
}
bool wantRef = false;
if (!fp && this->e1->type->toBasetype() == this->e2->type->toBasetype() &&
(e1->type->toBasetype()->ty == Tarray || isAssocArray(e1->type))
(e1->type->toBasetype()->ty == Tarray || isAssocArray(e1->type)
|| e1->type->toBasetype()->ty == Tclass)
// e = *x is never a reference, because *x is always a value
&& this->e2->op != TOKstar
)
Expand Down Expand Up @@ -3973,7 +4170,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_
// (in which case, we already have the lvalue).
if (this->e1->op != TOKcall && !(this->e1->op==TOKvar
&& ((VarExp*)this->e1)->var->storage_class & (STCref | STCout)))
e1 = e1->interpret(istate, ctfeNeedLvalue);
e1 = e1->interpret(istate, isPointer(type)? ctfeNeedLvalueRef : ctfeNeedLvalue);
if (exceptionOrCantInterpret(e1))
return e1;
if (e1->op == TOKstructliteral && newval->op == TOKstructliteral)
Expand Down

0 comments on commit 4f85b43

Please sign in to comment.