Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion src/dmd/backend/drtlsym.d
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ Symbol *getRtlsym(RTLSYM i)
case RTLSYM.ARRAYCTOR: symbolz(ps,FLfunc,FREGSAVED,"_d_arrayctor", 0, t); break;
case RTLSYM.ARRAYSETASSIGN: symbolz(ps,FLfunc,FREGSAVED,"_d_arraysetassign", 0, t); break;
case RTLSYM.ARRAYSETCTOR: symbolz(ps,FLfunc,FREGSAVED,"_d_arraysetctor", 0, t); break;
case RTLSYM.ARRAYEQ2: symbolz(ps,FLfunc,FREGSAVED,"_adEq2", 0, t); break;
case RTLSYM.ARRAYCMPCHAR: symbolz(ps,FLfunc,FREGSAVED,"_adCmpChar", 0, t); break;

case RTLSYM.EXCEPT_HANDLER2: symbolz(ps,FLfunc,fregsaved,"_except_handler2", 0, tsclib); break;
Expand Down
2 changes: 1 addition & 1 deletion src/dmd/backend/rtlsym.d
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ enum RTLSYM
ARRAYSETCTOR,
ARRAYCAST, // unused
ARRAYEQ, // unused
ARRAYEQ2,
ARRAYEQ2, // unused
ARRAYCMP, // unused
ARRAYCMP2, // unused
ARRAYCMPCHAR, // unused
Expand Down
132 changes: 57 additions & 75 deletions src/dmd/e2ir.d
Original file line number Diff line number Diff line change
Expand Up @@ -1859,89 +1859,71 @@ extern (C++) class ToElemVisitor : Visitor
else if ((t1.ty == Tarray || t1.ty == Tsarray) &&
(t2.ty == Tarray || t2.ty == Tsarray))
{
Type telement = t1.nextOf().toBasetype();
Type telement2 = t2.nextOf().toBasetype();

if ((telement.isintegral() || telement.ty == Tvoid) && telement.ty == telement2.ty)
{
// Optimize comparisons of arrays of basic types
// For arrays of integers/characters, and void[],
// replace druntime call with:
// For a==b: a.length==b.length && (a.length == 0 || memcmp(a.ptr, b.ptr, size)==0)
// For a!=b: a.length!=b.length || (a.length != 0 || memcmp(a.ptr, b.ptr, size)!=0)
// size is a.length*sizeof(a[0]) for dynamic arrays, or sizeof(a) for static arrays.

elem* earr1 = toElem(ee.e1, irs);
elem* earr2 = toElem(ee.e2, irs);
elem* eptr1, eptr2; // Pointer to data, to pass to memcmp
elem* elen1, elen2; // Length, for comparison
elem* esiz1, esiz2; // Data size, to pass to memcmp
d_uns64 sz = telement.size(); // Size of one element

if (t1.ty == Tarray)
{
elen1 = el_una(target.is64bit ? OP128_64 : OP64_32, TYsize_t, el_same(&earr1));
esiz1 = el_bin(OPmul, TYsize_t, el_same(&elen1), el_long(TYsize_t, sz));
eptr1 = array_toPtr(t1, el_same(&earr1));
}
else
{
elen1 = el_long(TYsize_t, (cast(TypeSArray)t1).dim.toInteger());
esiz1 = el_long(TYsize_t, t1.size());
earr1 = addressElem(earr1, t1);
eptr1 = el_same(&earr1);
}
// Only memcmp-able array comparisons make it to the glue layer
// (others are lowered to object.__equals). Implement via:
// * for a==b: a.length==b.length && (a.length == 0 || memcmp(a.ptr, b.ptr, size)==0)
// * for a!=b: a.length!=b.length || (a.length != 0 && memcmp(a.ptr, b.ptr, size)!=0)
// size is a.length*sizeof(a[0]) for dynamic arrays, or sizeof(a) for static arrays.

if (t2.ty == Tarray)
{
elen2 = el_una(target.is64bit ? OP128_64 : OP64_32, TYsize_t, el_same(&earr2));
esiz2 = el_bin(OPmul, TYsize_t, el_same(&elen2), el_long(TYsize_t, sz));
eptr2 = array_toPtr(t2, el_same(&earr2));
}
else
{
elen2 = el_long(TYsize_t, (cast(TypeSArray)t2).dim.toInteger());
esiz2 = el_long(TYsize_t, t2.size());
earr2 = addressElem(earr2, t2);
eptr2 = el_same(&earr2);
}
elem* earr1 = toElem(ee.e1, irs);
elem* earr2 = toElem(ee.e2, irs);
elem* eptr1, eptr2; // Pointer to data, to pass to memcmp
elem* elen1, elen2; // Length, for comparison
elem* esiz1, esiz2; // Data size, to pass to memcmp
d_uns64 sz = t1.nextOf().toBasetype().size(); // Size of one element

if (t1.ty == Tarray)
{
elen1 = el_una(target.is64bit ? OP128_64 : OP64_32, TYsize_t, el_same(&earr1));
esiz1 = el_bin(OPmul, TYsize_t, el_same(&elen1), el_long(TYsize_t, sz));
eptr1 = array_toPtr(t1, el_same(&earr1));
}
else
{
elen1 = el_long(TYsize_t, (cast(TypeSArray)t1).dim.toInteger());
esiz1 = el_long(TYsize_t, t1.size());
earr1 = addressElem(earr1, t1);
eptr1 = el_same(&earr1);
}

elem *esize = t2.ty == Tsarray ? esiz2 : esiz1;
if (t2.ty == Tarray)
{
elen2 = el_una(target.is64bit ? OP128_64 : OP64_32, TYsize_t, el_same(&earr2));
esiz2 = el_bin(OPmul, TYsize_t, el_same(&elen2), el_long(TYsize_t, sz));
eptr2 = array_toPtr(t2, el_same(&earr2));
}
else
{
elen2 = el_long(TYsize_t, (cast(TypeSArray)t2).dim.toInteger());
esiz2 = el_long(TYsize_t, t2.size());
earr2 = addressElem(earr2, t2);
eptr2 = el_same(&earr2);
}

e = el_param(eptr1, eptr2);
e = el_bin(OPmemcmp, TYint, e, esize);
e = el_bin(eop, TYint, e, el_long(TYint, 0));
elem *esize = t2.ty == Tsarray ? esiz2 : esiz1;

elem *elen = t2.ty == Tsarray ? elen2 : elen1;
elem *esizecheck = el_bin(eop, TYint, el_same(&elen), el_long(TYsize_t, 0));
e = el_bin(ee.op == TOK.equal ? OPoror : OPandand, TYint, esizecheck, e);
e = el_param(eptr1, eptr2);
e = el_bin(OPmemcmp, TYint, e, esize);
e = el_bin(eop, TYint, e, el_long(TYint, 0));

if (t1.ty == Tsarray && t2.ty == Tsarray)
assert(t1.size() == t2.size());
else
{
elem *elencmp = el_bin(eop, TYint, elen1, elen2);
e = el_bin(ee.op == TOK.equal ? OPandand : OPoror, TYint, elencmp, e);
}
elem *elen = t2.ty == Tsarray ? elen2 : elen1;
elem *esizecheck = el_bin(eop, TYint, el_same(&elen), el_long(TYsize_t, 0));
e = el_bin(ee.op == TOK.equal ? OPoror : OPandand, TYint, esizecheck, e);

// Ensure left-to-right order of evaluation
e = el_combine(earr2, e);
e = el_combine(earr1, e);
elem_setLoc(e, ee.loc);
result = e;
return;
if (t1.ty == Tsarray && t2.ty == Tsarray)
assert(t1.size() == t2.size());
else
{
elem *elencmp = el_bin(eop, TYint, elen1, elen2);
e = el_bin(ee.op == TOK.equal ? OPandand : OPoror, TYint, elencmp, e);
}

elem *ea1 = eval_Darray(ee.e1);
elem *ea2 = eval_Darray(ee.e2);

elem *ep = el_params(getTypeInfo(ee.loc, telement.arrayOf(), irs),
ea2, ea1, null);
const rtlfunc = RTLSYM.ARRAYEQ2;
e = el_bin(OPcall, TYint, el_var(getRtlsym(rtlfunc)), ep);
if (ee.op == TOK.notEqual)
e = el_bin(OPxor, TYint, e, el_long(TYint, 1));
elem_setLoc(e,ee.loc);
// Ensure left-to-right order of evaluation
e = el_combine(earr2, e);
e = el_combine(earr1, e);
elem_setLoc(e, ee.loc);
result = e;
return;
}
else if (t1.ty == Taarray && t2.ty == Taarray)
{
Expand Down
87 changes: 74 additions & 13 deletions src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -11472,11 +11472,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
}

Type t1 = exp.e1.type.toBasetype();
Type t2 = exp.e2.type.toBasetype();
if (auto e = exp.op_overload(sc))
{
result = e;
return;
}


// Indicates whether the comparison of the 2 specified array types
// requires an object.__equals() lowering.
// requires an elision of typeCombine.
static bool needsDirectEq(Type t1, Type t2, Scope* sc)
{
Type t1n = t1.nextOf().toBasetype();
Expand Down Expand Up @@ -11504,22 +11508,63 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return false;
}

if (auto e = exp.op_overload(sc))
// Indicates whether the comparison of the 2 specified array types
// can be implemented with a memcmp call in the glue layer instead
// of lowering to object.__equals().
static bool canMemcmpArrays(const ref Loc loc, Type t1, Type t2)
{
result = e;
return;
Type t1n = t1.nextOf().toBasetype();
Type t2n = t2.nextOf().toBasetype();

static Type getBaseElementType(Type t)
{
t = t.baseElemOf();
if (auto tv = t.isTypeVector())
t = tv.elementType();
if (t.ty == Tvoid)
t = Type.tuns8;
return t;
}

Type e1 = getBaseElementType(t1n);
Type e2 = getBaseElementType(t2n);
if (e1.isintegral() && e2.isintegral())
{
// element sizes must match
if (t1n.size(loc) != t2n.size(loc))
return false;

// base integer sizes too
const size = e1.size(loc);
if (size != e2.size(loc))
return false;

// integers < 4 bytes are promoted to int => no memcmp for diverging signed-ness
return size >= 4 || e1.isunsigned() == e2.isunsigned();
}

if ((t1n.ty == Tpointer && t2n.ty == Tpointer) ||
(t1n.ty == Tfunction && t2n.ty == Tfunction) ||
(t1n.ty == Tdelegate && t2n.ty == Tdelegate))
{
return t1n.equivalent(t2n);
}

return false;
}

Type t1 = exp.e1.type.toBasetype();
Type t2 = exp.e2.type.toBasetype();

const isArrayComparison = (t1.ty == Tarray || t1.ty == Tsarray) &&
(t2.ty == Tarray || t2.ty == Tsarray);
const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc);

if (!needsArrayLowering)
// bring both sides to common type
if (!isArrayComparison || !needsDirectEq(t1, t2, sc))
{
if (auto e = typeCombine(exp, sc))
{
result = e;
result = e; // error
return;
}
}
Expand All @@ -11529,6 +11574,20 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (f1 || f2)
return setError();

version (none)
{
// check for mismatching lengths when comparing 2 static arrays
if (isArrayComparison)
if (auto ts1 = exp.e1.type.toBasetype().isTypeSArray())
if (auto ts2 = exp.e2.type.toBasetype().isTypeSArray())
if (ts1.dim.toUInteger() != ts2.dim.toUInteger())
{
exp.error("incompatible types for array comparison: `%s` and `%s`",
exp.e1.type.toChars(), exp.e2.type.toChars());
return setError();
}
}

exp.type = Type.tbool;

if (!isArrayComparison)
Expand All @@ -11541,8 +11600,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
}

// lower some array comparisons to object.__equals(e1, e2)
if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray))
// lower non-memcmp-able array comparisons to object.__equals(e1, e2)
if (isArrayComparison && !canMemcmpArrays(exp.loc, exp.e1.type.toBasetype(), exp.e2.type.toBasetype()))
{
//printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars());

Expand Down Expand Up @@ -11575,9 +11634,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}

if (exp.e1.type.toBasetype().ty == Taarray)
semanticTypeInfo(sc, exp.e1.type.toBasetype());
t1 = exp.e1.type.toBasetype();
t2 = exp.e2.type.toBasetype();

if (t1.ty == Taarray)
semanticTypeInfo(sc, t1);

if (!target.isVectorOpSupported(t1, exp.op, t2))
{
Expand Down
10 changes: 1 addition & 9 deletions src/dmd/inline.d
Original file line number Diff line number Diff line change
Expand Up @@ -793,15 +793,7 @@ public:
visit(cast(BinExp)e);

Type t1 = e.e1.type.toBasetype();
if (t1.ty == Tarray || t1.ty == Tsarray)
{
Type t = t1.nextOf().toBasetype();
while (t.toBasetype().nextOf())
t = t.nextOf().toBasetype();
if (t.ty == Tstruct)
semanticTypeInfo(null, t);
}
else if (t1.ty == Taarray)
if (t1.ty == Taarray)
{
semanticTypeInfo(null, t1);
}
Expand Down
2 changes: 1 addition & 1 deletion test/fail_compilation/diag7420.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ TEST_OUTPUT:
fail_compilation/diag7420.d(21): Error: static variable `x` cannot be read at compile time
fail_compilation/diag7420.d(21): while evaluating: `static assert(x < 4)`
fail_compilation/diag7420.d(22): Error: static variable `y` cannot be read at compile time
fail_compilation/diag7420.d(22): called from here: `__equals(y, "abc")`
fail_compilation/diag7420.d(22): while evaluating: `static assert(y == "abc")`
fail_compilation/diag7420.d(23): Error: static variable `y` cannot be read at compile time
fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(ubyte[])y != null)`
Expand All @@ -16,6 +15,7 @@ fail_compilation/diag7420.d(25): while evaluating: `static assert(y[0..1]
---
*/


int x = 2;
char[] y = "abc".dup;
static assert(x < 4);
Expand Down
2 changes: 1 addition & 1 deletion test/fail_compilation/verifyhookexist.d
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ MyStruct[] castToMyStruct(int[] arr) {
}

void test() {
int[] arrA, arrB;
float[] arrA, arrB;

bool a = arrA[] == arrB[];
bool b = arrA < arrB;
Expand Down
10 changes: 5 additions & 5 deletions test/runnable/sdtor.d
Original file line number Diff line number Diff line change
Expand Up @@ -3935,9 +3935,9 @@ bool test14022()
t.sb[1].x = 'y';
assert(sa == [S('a'), S('b'), S('c')]);
assert(t.sb == [S('x'), S('y')]);
assert(op == "BC");
assert(op == "BCcbcbayx");
}
assert(op == "BCyxcba");
assert(op == "BCcbcbayxyxcba");

op = null;
{
Expand All @@ -3948,7 +3948,7 @@ bool test14022()
assert(op == "BxCy");
assert(t.sb == [S('b'), S('c')]);
}
assert(op == "BxCycbcba");
assert(op == "BxCycbcbcba");

return true;
}
Expand Down Expand Up @@ -4017,9 +4017,9 @@ bool test14023()
{
S[3] sa = [S('a'), S('b'), S('c')];
test(sa[1..3]);
assert(op == "BxCx");
assert(op == "BxCxcb");
}
assert(op == "BxCxcba");
assert(op == "BxCxcbcba");

return true;
}
Expand Down