Skip to content

Commit

Permalink
[WIP] Fixing specializations in EqualsNode
Browse files Browse the repository at this point in the history
  • Loading branch information
Akirathan committed Feb 17, 2023
1 parent 0f23952 commit 30a1ad8
Showing 1 changed file with 156 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.Arrays;
Expand Down Expand Up @@ -93,7 +94,11 @@ boolean equalsLong(long self, long other) {

@Specialization
boolean equalsDouble(double self, double other) {
return self == other;
if (Double.isNaN(self) || Double.isNaN(other)) {
return false;
} else {
return self == other;
}
}

@Specialization
Expand All @@ -116,6 +121,11 @@ boolean equalsLongInt(long self, int other) {
return self == (long) other;
}

@Specialization
boolean equalsLongBigInt(long self, EnsoBigInteger bigInt) {
return BigInteger.valueOf(self).equals(bigInt.getValue());
}

@Specialization
boolean equalsIntDouble(int self, double other) {
return (double) self == other;
Expand Down Expand Up @@ -267,12 +277,8 @@ boolean equalsBooleanInterop(
}

@Specialization(guards = {
"!selfInterop.isDate(selfTimeZone)",
"!selfInterop.isTime(selfTimeZone)",
"selfInterop.isTimeZone(selfTimeZone)",
"!otherInterop.isDate(otherTimeZone)",
"!otherInterop.isTime(otherTimeZone)",
"otherInterop.isTimeZone(otherTimeZone)"
"isTimeZone(selfTimeZone, selfInterop)",
"isTimeZone(otherTimeZone, otherInterop)",
}, limit = "3")
boolean equalsTimeZones(Object selfTimeZone, Object otherTimeZone,
@CachedLibrary("selfTimeZone") InteropLibrary selfInterop,
Expand All @@ -288,12 +294,8 @@ boolean equalsTimeZones(Object selfTimeZone, Object otherTimeZone,

@TruffleBoundary
@Specialization(guards = {
"selfInterop.isDate(selfZonedDateTime)",
"selfInterop.isTime(selfZonedDateTime)",
"selfInterop.isTimeZone(selfZonedDateTime)",
"otherInterop.isDate(otherZonedDateTime)",
"otherInterop.isTime(otherZonedDateTime)",
"otherInterop.isTimeZone(otherZonedDateTime)"
"isZonedDateTime(selfZonedDateTime, selfInterop)",
"isZonedDateTime(otherZonedDateTime, otherInterop)",
}, limit = "3")
boolean equalsZonedDateTimes(Object selfZonedDateTime, Object otherZonedDateTime,
@CachedLibrary("selfZonedDateTime") InteropLibrary selfInterop,
Expand All @@ -317,12 +319,8 @@ boolean equalsZonedDateTimes(Object selfZonedDateTime, Object otherZonedDateTime
}

@Specialization(guards = {
"selfInterop.isDate(selfDateTime)",
"selfInterop.isTime(selfDateTime)",
"!selfInterop.isTimeZone(selfDateTime)",
"otherInterop.isDate(otherDateTime)",
"otherInterop.isTime(otherDateTime)",
"!otherInterop.isTimeZone(otherDateTime)"
"isDateTime(selfDateTime, selfInterop)",
"isDateTime(otherDateTime, otherInterop)",
}, limit = "3")
boolean equalsDateTimes(Object selfDateTime, Object otherDateTime,
@CachedLibrary("selfDateTime") InteropLibrary selfInterop,
Expand All @@ -343,12 +341,8 @@ boolean equalsDateTimes(Object selfDateTime, Object otherDateTime,
}

@Specialization(guards = {
"selfInterop.isDate(selfDate)",
"!selfInterop.isTime(selfDate)",
"!selfInterop.isTimeZone(selfDate)",
"otherInterop.isDate(otherDate)",
"!otherInterop.isTime(otherDate)",
"!otherInterop.isTimeZone(otherDate)"
"isDate(selfDate, selfInterop)",
"isDate(otherDate, otherInterop)",
}, limit = "3")
boolean equalsDates(Object selfDate, Object otherDate,
@CachedLibrary("selfDate") InteropLibrary selfInterop,
Expand All @@ -363,12 +357,8 @@ boolean equalsDates(Object selfDate, Object otherDate,
}

@Specialization(guards = {
"!selfInterop.isDate(selfTime)",
"selfInterop.isTime(selfTime)",
"!selfInterop.isTimeZone(selfTime)",
"!otherInterop.isDate(otherTime)",
"otherInterop.isTime(otherTime)",
"!otherInterop.isTimeZone(otherTime)"
"isTime(selfTime, selfInterop)",
"isTime(otherTime, otherInterop)",
}, limit = "3")
boolean equalsTimes(Object selfTime, Object otherTime,
@CachedLibrary("selfTime") InteropLibrary selfInterop,
Expand Down Expand Up @@ -504,21 +494,8 @@ boolean equalsHashMaps(Object selfHashMap, Object otherHashMap,
}

@Specialization(guards = {
"!isAtom(selfObject)",
"!isAtom(otherObject)",
"!isHostObject(selfObject)",
"!isHostObject(otherObject)",
"interop.hasMembers(selfObject)",
"interop.hasMembers(otherObject)",
"!interop.isDate(selfObject)",
"!interop.isDate(otherObject)",
"!interop.isTime(selfObject)",
"!interop.isTime(otherObject)",
// Objects with types are handled in `equalsTypes` specialization, so we have to
// negate the guards of that specialization here - to make the specializations
// disjunctive.
"!typesLib.hasType(selfObject)",
"!typesLib.hasType(otherObject)",
"isObjectWithMembers(selfObject, interop)",
"isObjectWithMembers(otherObject, interop)",
})
boolean equalsInteropObjectWithMembers(Object selfObject, Object otherObject,
@CachedLibrary(limit = "10") InteropLibrary interop,
Expand Down Expand Up @@ -584,18 +561,19 @@ static EqualsNode[] createEqualsNodes(int size) {
}

@Specialization(guards = {
"fieldsLenCached == structsLib.getFields(self).length"
})
"selfCtorCached == self.getConstructor()"
}, limit = "10")
@ExplodeLoop
boolean equalsAtoms(
Atom self,
Atom other,
@CachedLibrary(limit = "5") StructsLibrary structsLib,
@Cached(value = "structsLib.getFields(self).length", allowUncached = true) int fieldsLenCached,
@Cached("self.getConstructor()") AtomConstructor selfCtorCached,
@Cached(value = "selfCtorCached.getFields().length", allowUncached = true) int fieldsLenCached,
@Cached(value = "createEqualsNodes(fieldsLenCached)", allowUncached = true) EqualsNode[] fieldEqualsNodes,
@Cached ConditionProfile constructorsNotEqualProfile,
@Cached HasCustomComparatorNode hasCustomComparatorNode,
@Cached InvokeAnyEqualsNode invokeAnyEqualsNode
@Cached InvokeAnyEqualsNode invokeAnyEqualsNode,
@CachedLibrary(limit = "5") StructsLibrary structsLib
) {
if (constructorsNotEqualProfile.profile(
self.getConstructor() != other.getConstructor()
Expand Down Expand Up @@ -633,9 +611,14 @@ boolean equalsAtoms(
@TruffleBoundary
@Specialization(replaces = "equalsAtoms")
boolean equalsAtomsUncached(Atom self, Atom other) {
if (!equalsAtomConstructors(self.getConstructor(), other.getConstructor())) {
return false;
}
Object[] selfFields = StructsLibrary.getUncached().getFields(self);
Object[] otherFields = StructsLibrary.getUncached().getFields(other);
assert selfFields.length == otherFields.length;
if (selfFields.length != otherFields.length) {
return false;
}
for (int i = 0; i < selfFields.length; i++) {
boolean areFieldsSame;
if (selfFields[i] instanceof Atom selfFieldAtom
Expand Down Expand Up @@ -684,29 +667,134 @@ boolean equalsHostFunctions(Object selfHostFunc, Object otherHostFunc,
return equalsNode.execute(selfFuncStrRepr, otherFuncStrRepr);
}

// TODO: Add more specs
@Specialization(guards = {
"!isHostObject(left)",
"!isHostFunction(left)",
"!isHostObject(right)",
"!isHostFunction(right)",
"!isAtom(left)",
"!isAtom(right)",
"!typesLib.hasType(left)",
"!typesLib.hasType(right)",
"!interop.isString(left)",
"!interop.isString(right)",
})
@Specialization(guards = "fallbackGuard(left, right, interop)")
@TruffleBoundary
boolean equalsGeneric(Object left, Object right,
@CachedLibrary(limit = "5") InteropLibrary interop,
@CachedLibrary(limit = "5") TypesLibrary typesLib) {
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedLibrary(limit = "10") TypesLibrary typesLib) {
return left == right
|| interop.isIdentical(left, right, interop)
|| left.equals(right)
|| (isNullOrNothing(left, typesLib, interop) && isNullOrNothing(right, typesLib, interop));
}

// We have to manually specify negation of guards of other specializations, because
// we cannot use @Fallback here. Note that this guard is not precisely the negation of
// all the other guards on purpose.
boolean fallbackGuard(Object left, Object right, InteropLibrary interop) {
if (isPrimitive(left) && isPrimitive(right)) {
return false;
}
if (isHostObject(left) && isHostObject(right)) {
return false;
}
if (isHostFunction(left) && isHostFunction(right)) {
return false;
}
if (left instanceof Atom && right instanceof Atom) {
return false;
}
if (interop.isNull(left) && interop.isNull(right)) {
return false;
}
if (interop.isString(left) && interop.isString(right)) {
return false;
}
if (interop.hasArrayElements(left) && interop.hasArrayElements(right)) {
return false;
}
if (interop.hasHashEntries(left) && interop.hasHashEntries(right)) {
return false;
}
if (isObjectWithMembers(left, interop) && isObjectWithMembers(right, interop)) {
return false;
}
if (isTimeZone(left, interop) && isTimeZone(right, interop)) {
return false;
}
if (isZonedDateTime(left, interop) && isZonedDateTime(right, interop)) {
return false;
}
if (isDateTime(left, interop) && isDateTime(right, interop)) {
return false;
}
if (isDate(left, interop) && isDate(right, interop)) {
return false;
}
if (isTime(left, interop) && isTime(right, interop)) {
return false;
}
if (interop.isDuration(left) && interop.isDuration(right)) {
return false;
}
// For all other cases, fall through to the generic specialization
return true;
}

/**
* Return true iff object is a primitive value used in some of the specializations
* guard.
*/
private static boolean isPrimitive(Object object) {
return object instanceof Boolean ||
object instanceof Byte ||
object instanceof Long ||
object instanceof Double ||
object instanceof Integer ||
object instanceof EnsoBigInteger;
}

boolean isTimeZone(Object object, InteropLibrary interop) {
return
!interop.isTime(object) &&
!interop.isDate(object) &&
interop.isTimeZone(object);
}

boolean isZonedDateTime(Object object, InteropLibrary interop) {
return
interop.isTime(object) &&
interop.isDate(object) &&
interop.isTimeZone(object);
}

boolean isDateTime(Object object, InteropLibrary interop) {
return
interop.isTime(object) &&
interop.isDate(object) &&
!interop.isTimeZone(object);
}

boolean isDate(Object object, InteropLibrary interop) {
return
!interop.isTime(object) &&
interop.isDate(object) &&
!interop.isTimeZone(object);
}

boolean isTime(Object object, InteropLibrary interop) {
return
interop.isTime(object) &&
!interop.isDate(object) &&
!interop.isTimeZone(object);
}

boolean isObjectWithMembers(Object object, InteropLibrary interop) {
if (object instanceof Atom) {
return false;
}
if (isHostObject(object)) {
return false;
}
if (interop.isDate(object)) {
return false;
}
if (interop.isTime(object)) {
return false;
}
return interop.hasMembers(object);
}

private boolean isNullOrNothing(Object object, TypesLibrary typesLib, InteropLibrary interop) {
if (typesLib.hasType(object)) {
return typesLib.getType(object) == EnsoContext.get(this).getNothing();
Expand Down Expand Up @@ -747,11 +835,11 @@ boolean invokeEqualsCachedAtomCtor(Atom selfAtom, Atom thatAtom,
@Cached(value = "getAnyEqualsMethod()", allowUncached = true) Function anyEqualsFunc,
@Cached(value = "buildInvokeFuncNodeForAnyEquals()", allowUncached = true) InvokeFunctionNode invokeAnyEqualsNode,
@CachedLibrary(limit = "3") InteropLibrary interop) {
// TODO: Shouldn't Comparable type be the very first argument? (synthetic self)?
Object ret = invokeAnyEqualsNode.execute(
anyEqualsFunc,
null,
State.create(EnsoContext.get(this)),
// TODO: Shouldn't Any type be the very first argument? (synthetic self)?
new Object[]{selfAtom, thatAtom}
);
try {
Expand Down

0 comments on commit 30a1ad8

Please sign in to comment.