Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Splitting Gigantic EqualsNode into few Smaller Ones #6280

Merged
merged 23 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
80fd070
Always try to resolve conversion for Any type
JaroslavTulach Apr 3, 2023
40cc46b
Make sure conversions fallback to Any for foreign objects and types
JaroslavTulach Apr 4, 2023
65e933e
Splitting the EqualsNode to two speeds the sieve benchmark
JaroslavTulach Apr 4, 2023
2e847e6
EqualsComplexNode shall in general delegate to simple EqualsNode
JaroslavTulach Apr 14, 2023
ca4d7b6
Comparing interop strings needs to be handled in EqualsNode
JaroslavTulach Apr 14, 2023
f6b43f7
Only when isPrimitive-ness differs we can conclude they aren't the same
JaroslavTulach Apr 14, 2023
e8a0c97
Dedicated node to compare atoms
JaroslavTulach Apr 14, 2023
6a9358a
Let HasCustomComparatorNode return the comparator to use
JaroslavTulach Apr 14, 2023
e45fc47
Merging with current develop branch
JaroslavTulach Apr 14, 2023
adb78d7
Delegate to custom comparators when they are provided
JaroslavTulach Apr 14, 2023
0195e6c
Treat InteropLibrary.isString as a primitive object
JaroslavTulach Apr 14, 2023
0d21a46
No need for InvokeAnyEqualsNode, EqualsNode handles all the cases
JaroslavTulach Apr 14, 2023
a30ab04
There should be no need for int, byte & co. specializations - such ty…
JaroslavTulach Apr 14, 2023
904055d
Implementing from conversion via Truffle nodes
JaroslavTulach Apr 14, 2023
1c69c34
Update engine/runtime/src/main/java/org/enso/interpreter/node/express…
JaroslavTulach Apr 14, 2023
dcd484b
Renaming to isNotPrimitive
JaroslavTulach Apr 14, 2023
a9787c6
Dropping Has and making CustomComparatorNode
JaroslavTulach Apr 15, 2023
1c7ac56
Define Any.== as a builtin
JaroslavTulach Apr 15, 2023
38ce191
test micro distribution has to use Any.== builtin
JaroslavTulach Apr 15, 2023
6a78062
Merging with changes on the develop branch
JaroslavTulach Apr 16, 2023
7c8730b
Convert interop values on the Enso boundary
JaroslavTulach Apr 16, 2023
04bdbff
Reverting no longer needed 2bf5256 as is builtin again
JaroslavTulach Apr 16, 2023
80eb323
Resolving conflicts with develop branch
JaroslavTulach Apr 17, 2023
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
17 changes: 1 addition & 16 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso
Original file line number Diff line number Diff line change
Expand Up @@ -108,22 +108,7 @@ type Any
a = 7 * 21
a == 147
== : Any -> Boolean
== self that =
# If there is No_Such_Conversion, then `self` and `that` are probably
# host or polyglot values, so we just compare them with the default comparator.
eq_self = Comparable.from self
eq_that = Comparable.from that
similar_type = Meta.is_same_object eq_self eq_that
case similar_type of
False -> False
True ->
case Meta.is_same_object eq_self Default_Comparator of
# Shortcut for objects with Default_Comparator, because of the performance.
True -> Comparable.equals_builtin self that
False ->
case eq_self.compare self that of
Ordering.Equal -> True
_ -> False
== self that = @Builtin_Method "Any.=="

## ALIAS Inequality

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,22 +124,14 @@ type Comparable
ordering = Ordering.compare atom that
if ordering.is_error then Nothing else ordering.to_sign

## PRIVATE
A custom comparator is any comparator that is different than the
default ones.
has_custom_comparator : Atom -> Boolean
has_custom_comparator atom =
comp = Comparable.from atom
(comp.is_a Default_Comparator).not

## PRIVATE
Default implementation of a _comparator_.
@Builtin_Type
type Default_Comparator
## PRIVATE
compare : Any -> Any -> (Ordering|Nothing)
compare x y =
case Comparable.equals_builtin x y of
case Any.== x y of
True -> Ordering.Equal
False ->
case Comparable.less_than_builtin x y of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ public void initializeBenchmark(BenchmarkParams params) throws Exception {

var benchmarkName = params.getBenchmark().replaceFirst(".*\\.", "");
var code = """
import Standard.Base.Any.Any

avg fn len =
sum acc i = if i == len then acc else
sum (acc + fn i) i+1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ public void initializeBenchmark(BenchmarkParams params) throws Exception {
var benchmarkName = params.getBenchmark().replaceFirst(".*\\.", "");
var codeBuilder = new StringBuilder("""
import Standard.Base.Data.Range.Extensions
import Standard.Base.Any.Any

type Node
C1 f1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ public void initializeBenchmark(BenchmarkParams params) throws Exception {
var code = """
import Standard.Base.Data.Vector.Vector
import Standard.Base.Data.Array_Proxy.Array_Proxy
import Standard.Base.Any.Any

avg arr =
sum acc i = if i == arr.length then acc else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class AtomFixtures extends DefaultInterpreterRunner {

val millionElementList = eval(
s"""import Standard.Base.Data.List.List
|import Standard.Base.Any.Any
|
|main =
| generator fn acc i end = if i == end then acc else @Tail_Call generator fn (fn acc i) i+1 end
Expand All @@ -17,7 +16,6 @@ class AtomFixtures extends DefaultInterpreterRunner {

val generateListCode =
"""import Standard.Base.Data.List.List
|import Standard.Base.Any.Any
|
|main = length ->
| generator = acc -> i -> if i == 0 then acc else @Tail_Call generator (List.Cons i acc) (i - 1)
Expand All @@ -29,7 +27,6 @@ class AtomFixtures extends DefaultInterpreterRunner {

val generateListQualifiedCode =
"""import Standard.Base.Data.List.List
|import Standard.Base.Any.Any
|
|main = length ->
| generator = acc -> i -> if i == 0 then acc else @Tail_Call generator (List.Cons i acc) (i - 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ class CallableFixtures extends DefaultInterpreterRunner {
val sumTCOfromCallCode =
"""
|from Standard.Base.Data.Numbers import all
|import Standard.Base.Any.Any
|
|type Foo
|
Expand All @@ -23,8 +22,7 @@ class CallableFixtures extends DefaultInterpreterRunner {


val sumTCOmethodCallCode =
"""import Standard.Base.Any.Any
|
"""
|summator = acc -> current ->
| if current == 0 then acc else @Tail_Call summator (acc + current) (current - 1)
|
Expand All @@ -35,8 +33,7 @@ class CallableFixtures extends DefaultInterpreterRunner {
val sumTCOmethodCall = getMain(sumTCOmethodCallCode)

val sumTCOmethodCallWithNamedArgumentsCode =
"""import Standard.Base.Any.Any
|
"""
|summator = acc -> current ->
| if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
|
Expand All @@ -48,8 +45,7 @@ class CallableFixtures extends DefaultInterpreterRunner {
getMain(sumTCOmethodCallWithNamedArgumentsCode)

val sumTCOmethodCallWithDefaultedArgumentsCode =
"""import Standard.Base.Any.Any
|
"""
|summator = (acc = 0) -> current ->
| if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ class NamedDefaultedArgumentFixtures extends DefaultInterpreterRunner {
val hundredMillion: Long = 100000000

val sumTCOWithNamedArgumentsCode =
"""import Standard.Base.Any.Any
|
"""
|main = sumTo ->
| summator = acc -> current ->
| if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
Expand All @@ -18,8 +17,7 @@ class NamedDefaultedArgumentFixtures extends DefaultInterpreterRunner {
val sumTCOWithNamedArguments = getMain(sumTCOWithNamedArgumentsCode)

val sumTCOWithDefaultedArgumentsCode =
"""import Standard.Base.Any.Any
|
"""
|main = sumTo ->
| summator = (acc = 0) -> current ->
| if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ class RecursionFixtures extends DefaultInterpreterRunner {
val hundred: Long = 100

val sumTCOCode =
"""import Standard.Base.Any.Any
|
"""
|main = sumTo ->
| summator = acc -> current ->
| if current == 0 then acc else @Tail_Call summator acc+current current-1
Expand All @@ -21,8 +20,7 @@ class RecursionFixtures extends DefaultInterpreterRunner {
val sumTCO = getMain(sumTCOCode)

val sumTCOFoldLikeCode =
"""import Standard.Base.Any.Any
|
"""
|main = sumTo ->
| summator = acc -> i -> f ->
| if i == 0 then acc else @Tail_Call summator (f acc i) (i - 1) f
Expand All @@ -32,8 +30,7 @@ class RecursionFixtures extends DefaultInterpreterRunner {
val sumTCOFoldLike = getMain(sumTCOFoldLikeCode)

val sumRecursiveCode =
"""import Standard.Base.Any.Any
|
"""
|main = sumTo ->
| summator = i -> if i == 0 then 0 else i + summator (i - 1)
| res = summator sumTo
Expand All @@ -42,8 +39,7 @@ class RecursionFixtures extends DefaultInterpreterRunner {
val sumRecursive = getMain(sumRecursiveCode)

val oversaturatedRecursiveCallTCOCode =
"""import Standard.Base.Any.Any
|
"""
|main = sumTo ->
| summator = acc -> i -> f ->
| if i == 0 then acc else @Tail_Call summator (f acc i) (i - 1) f
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import java.util.Arrays;
import org.enso.interpreter.node.callable.InvokeCallableNode.ArgumentsExecutionMode;
import org.enso.interpreter.node.callable.InvokeCallableNode.DefaultsExecutionMode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.expression.builtin.ordering.CustomComparatorNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.atom.StructsLibrary;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.state.State;

@GenerateUncached
public abstract class EqualsAtomNode extends Node {

public static EqualsAtomNode build() {
return EqualsAtomNodeGen.create();
}

public abstract boolean execute(Atom left, Atom right);

static EqualsNode[] createEqualsNodes(int size) {
EqualsNode[] nodes = new EqualsNode[size];
Arrays.fill(nodes, EqualsNode.build());
return nodes;
}

@Specialization(
guards = {
"selfCtorCached == self.getConstructor()",
"customComparatorNode.execute(self) == null"
},
limit = "10")
@ExplodeLoop
boolean equalsAtoms(
Atom self,
Atom other,
@Cached("self.getConstructor()") AtomConstructor selfCtorCached,
@Cached(value = "selfCtorCached.getFields().length", allowUncached = true)
int fieldsLenCached,
@Cached(value = "createEqualsNodes(fieldsLenCached)", allowUncached = true)
EqualsNode[] fieldEqualsNodes,
@Cached CustomComparatorNode customComparatorNode,
@Cached ConditionProfile constructorsNotEqualProfile,
@CachedLibrary(limit = "5") StructsLibrary structsLib) {
if (constructorsNotEqualProfile.profile(self.getConstructor() != other.getConstructor())) {
return false;
}
var selfFields = structsLib.getFields(self);
var otherFields = structsLib.getFields(other);
assert selfFields.length == otherFields.length
: "Constructors are same, atoms should have the same number of fields";

CompilerAsserts.partialEvaluationConstant(fieldsLenCached);
for (int i = 0; i < fieldsLenCached; i++) {
boolean fieldsAreEqual = fieldEqualsNodes[i].execute(selfFields[i], otherFields[i]);
if (!fieldsAreEqual) {
return false;
}
}
return true;
}

@Specialization(
guards = {
"selfCtorCached == self.getConstructor()",
"cachedComparator != null",
},
limit = "10")
boolean equalsAtoms(
Atom self,
Atom other,
@Cached("self.getConstructor()") AtomConstructor selfCtorCached,
@Cached CustomComparatorNode customComparatorNode,
@Cached(value = "customComparatorNode.execute(self)") Type cachedComparator,
@Cached(value = "findCompareMethod(cachedComparator)", allowUncached = true)
Function compareFn,
@Cached(value = "invokeCompareNode(compareFn)") InvokeFunctionNode invokeNode) {
var otherComparator = customComparatorNode.execute(other);
if (cachedComparator != otherComparator) {
return false;
}
var ctx = EnsoContext.get(this);
var args = new Object[] {cachedComparator, self, other};
var result = invokeNode.execute(compareFn, null, State.create(ctx), args);
return ctx.getBuiltins().ordering().newEqual() == result;
}

@CompilerDirectives.TruffleBoundary
@Specialization(replaces = "equalsAtoms")
boolean equalsAtomsUncached(Atom self, Atom other) {
if (self.getConstructor() != other.getConstructor()) {
return false;
}
Object[] selfFields = StructsLibrary.getUncached().getFields(self);
Object[] otherFields = StructsLibrary.getUncached().getFields(other);
if (selfFields.length != otherFields.length) {
return false;
}
for (int i = 0; i < selfFields.length; i++) {
boolean areFieldsSame = EqualsNodeGen.getUncached().execute(selfFields[i], otherFields[i]);
if (!areFieldsSame) {
return false;
}
}
return true;
}

@TruffleBoundary
static Function findCompareMethod(Type comparator) {
var fn = comparator.getDefinitionScope().getMethods().get(comparator).get("compare");
if (fn == null) {
throw new AssertionError("No compare function for " + comparator);
}
return fn;
}

static InvokeFunctionNode invokeCompareNode(Function compareFn) {
CallArgumentInfo[] argsInfo = new CallArgumentInfo[compareFn.getSchema().getArgumentsCount()];
for (int i = 0; i < argsInfo.length; i++) {
var argDef = compareFn.getSchema().getArgumentInfos()[i];
argsInfo[i] = new CallArgumentInfo(argDef.getName());
}
return InvokeFunctionNode.build(
argsInfo, DefaultsExecutionMode.EXECUTE, ArgumentsExecutionMode.EXECUTE);
}
}
Loading