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

Suspended atom fields are evaluated only once #6151

Merged
merged 20 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
aa497f2
Suspended atom fields are evaluated only once
JaroslavTulach Mar 30, 2023
3ab8de5
More detailed analysis of the log
JaroslavTulach Mar 31, 2023
4d8dbec
Merge branch 'develop' into wip/jtulach/LazyAtomFields_6134
JaroslavTulach Apr 4, 2023
1fc191f
Change note: One can define lazy atom fields
JaroslavTulach Apr 4, 2023
4ad5130
Using MethodNames constant
JaroslavTulach Apr 4, 2023
cb89ab6
Suspended field getter
JaroslavTulach Apr 4, 2023
d76cb6e
Testing infinite list generator
JaroslavTulach Apr 4, 2023
160068a
Testing lazy atom fields by the Lazy_Spec
JaroslavTulach Apr 5, 2023
cad4e8e
Keep and rethrow suspended exceptions
JaroslavTulach Apr 5, 2023
39819fa
Replacing Lazy.Lazy with ~lazy atom field
JaroslavTulach Apr 5, 2023
bcec688
Comparing standard List with lazy list implementation
JaroslavTulach Apr 5, 2023
03262ce
Make Table_Tests pass
JaroslavTulach Apr 5, 2023
08a9408
Merge branch 'develop' into wip/jtulach/LazyAtomFields_6134
mergify[bot] Apr 5, 2023
473911d
Include SQL_Type in the signature
JaroslavTulach Apr 5, 2023
3619f8a
Removing empty line
JaroslavTulach Apr 5, 2023
fbaff7a
Merge branch 'wip/jtulach/LazyAtomFields_6134' of enso:enso-org/enso …
JaroslavTulach Apr 5, 2023
f5ceec0
Merge branch 'develop' into wip/jtulach/LazyAtomFields_6134
mergify[bot] Apr 5, 2023
938b49b
Merging with origin/develop
JaroslavTulach Apr 5, 2023
87954c0
Merge branch 'wip/jtulach/LazyAtomFields_6134' of enso:enso-org/enso …
JaroslavTulach Apr 5, 2023
0d196fd
Merge branch 'develop' into wip/jtulach/LazyAtomFields_6134
mergify[bot] Apr 5, 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@
- [Don't install Python component on Windows][5900]
- [Detect potential name conflicts between exported types and FQNs][5966]
- [Ensure calls involving warnings remain instrumented][6067]
- [One can define lazy atom fields][6151]

[3227]: https://github.com/enso-org/enso/pull/3227
[3248]: https://github.com/enso-org/enso/pull/3248
Expand Down Expand Up @@ -773,6 +774,7 @@
[5900]: https://github.com/enso-org/enso/pull/5900
[5966]: https://github.com/enso-org/enso/pull/5966
[6067]: https://github.com/enso-org/enso/pull/6067
[6151]: https://github.com/enso-org/enso/pull/6151

# Enso 2.0.0-alpha.18 (2021-10-12)

Expand Down
75 changes: 0 additions & 75 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Lazy.enso

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.Runtime.Lazy.Lazy

import project.Connection.Connection.Connection
import project.Data.SQL_Type.SQL_Type
Expand All @@ -14,7 +13,7 @@ type SQL_Type_Reference

Since fetching this type requires querying the database, it is computed
lazily and cached.
Computed_By_Database (lazy_ref : Lazy)
Computed_By_Database (~lazy_ref : SQL_Type)

## Refers to an SQL type that is overridden by the dialect's type system.
Overridden (value : SQL_Type)
Expand All @@ -25,7 +24,7 @@ type SQL_Type_Reference
This may perform a database query on first access.
get : SQL_Type
get self = case self of
SQL_Type_Reference.Computed_By_Database lazy_ref -> lazy_ref.get
SQL_Type_Reference.Computed_By_Database lazy_ref -> lazy_ref
SQL_Type_Reference.Overridden value -> value

## PRIVATE
Expand All @@ -51,7 +50,7 @@ type SQL_Type_Reference
columns = connection.jdbc_connection.fetch_columns statement statement_setter
only_column = columns.first
only_column.second
SQL_Type_Reference.Computed_By_Database (Lazy.new do_fetch)
SQL_Type_Reference.Computed_By_Database do_fetch

## PRIVATE
Creates a new `SQL_Type_Reference` that should never be used.
Expand All @@ -61,7 +60,7 @@ type SQL_Type_Reference
null =
getter =
Error.throw (Illegal_State.Error "Getting the SQL_Type from SQL_Type_Reference.null is not allowed. This indicates a bug in the Database library.")
SQL_Type_Reference.Computed_By_Database (Lazy.new getter)
SQL_Type_Reference.Computed_By_Database getter

## PRIVATE
Turns this reference into a type override.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,25 @@ public void initializeBenchmark(BenchmarkParams params) throws Exception {
from Standard.Base.Data.List.List import Cons, Nil
import Standard.Base.IO

type Lenivy
Nic
Hlava ~x ~xs

map self fn = case self of
Lenivy.Nic -> Lenivy.Nic
Lenivy.Hlava x xs -> Lenivy.Hlava (fn x) (xs.map fn)

plus_one list = list.map (x -> x + 1)

leniva_suma list acc = case list of
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
Lenivy.Nic -> acc
Lenivy.Hlava x xs -> @Tail_Call leniva_suma xs acc+x

lenivy_generator n =
go x v l = if x > n then l else
@Tail_Call go x+1 v+1 (Lenivy.Hlava v l)
go 1 1 Lenivy.Nic

sum list acc =
case list of
Nil -> acc
Expand All @@ -69,15 +86,22 @@ public void initializeBenchmark(BenchmarkParams params) throws Exception {
this.self = module.invokeMember("get_associated_type");
Function<String,Value> getMethod = (name) -> module.invokeMember("get_method", self, name);

Value longList = getMethod.apply("generator").execute(self, LENGTH_OF_EXPERIMENT);

this.plusOne = getMethod.apply("plus_one");
this.sum = getMethod.apply("sum");

switch (benchmarkName) {
case "mapOverList": {
this.list = longList;
this.oldSum = sum.execute(self, longList, 0);
this.list = getMethod.apply("generator").execute(self, LENGTH_OF_EXPERIMENT);
this.sum = getMethod.apply("sum");
this.oldSum = sum.execute(self, this.list, 0);
if (!this.oldSum.fitsInLong()) {
throw new AssertionError("Expecting a number " + this.oldSum);
}
break;
}
case "mapOverLazyList": {
this.list = getMethod.apply("lenivy_generator").execute(self, LENGTH_OF_EXPERIMENT);
this.sum = getMethod.apply("leniva_suma");
this.oldSum = sum.execute(self, this.list, 0);
if (!this.oldSum.fitsInLong()) {
throw new AssertionError("Expecting a number " + this.oldSum);
}
Expand All @@ -93,6 +117,11 @@ public void mapOverList(Blackhole matter) {
performBenchmark(matter);
}

@Benchmark
public void mapOverLazyList(Blackhole matter) {
performBenchmark(matter);
}

private void performBenchmark(Blackhole hole) throws AssertionError {
var newList = plusOne.execute(self, list);
var newSum = sum.execute(self, newList, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public AtomConstructor initializeFields(
cachedInstance = null;
}
if (Layout.isAritySupported(args.length)) {
boxedLayout = Layout.create(args.length, 0);
boxedLayout = Layout.create(args.length, 0, args);
}
this.constructorFunction =
buildConstructorFunction(language, localScope, assignments, varReads, annotations, args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.enso.interpreter.dsl.atom.LayoutSpec;
import org.enso.interpreter.node.expression.atom.InstantiateNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;

/**
Expand Down Expand Up @@ -54,6 +55,7 @@ public static int countLongs(long flags) {
private final @CompilerDirectives.CompilationFinal(dimensions = 1) UnboxingAtom.FieldGetterNode[]
uncachedFieldGetters;

private final @CompilerDirectives.CompilationFinal(dimensions = 1) ArgumentDefinition[] args;
private final @CompilerDirectives.CompilationFinal(dimensions = 1) NodeFactory<
? extends UnboxingAtom.FieldSetterNode>[]
fieldSetterFactories;
Expand All @@ -69,23 +71,30 @@ public Layout(
int[] fieldToStorage,
NodeFactory<? extends UnboxingAtom.FieldGetterNode>[] fieldGetterFactories,
NodeFactory<? extends UnboxingAtom.FieldSetterNode>[] fieldSetterFactories,
NodeFactory<? extends UnboxingAtom.InstantiatorNode> instantiatorFactory) {
NodeFactory<? extends UnboxingAtom.InstantiatorNode> instantiatorFactory,
ArgumentDefinition[] args) {
this.args = args;
this.inputFlags = inputFlags;
this.fieldToStorage = fieldToStorage;
this.instantiatorFactory = instantiatorFactory;
this.fieldGetterFactories = fieldGetterFactories;
this.uncachedFieldGetters = new UnboxingAtom.FieldGetterNode[fieldGetterFactories.length];
for (int i = 0; i < fieldGetterFactories.length; i++) {
this.uncachedFieldGetters[i] = fieldGetterFactories[i].getUncachedInstance();
assert this.uncachedFieldGetters[i] != null;
}
this.fieldSetterFactories = fieldSetterFactories;
this.uncachedFieldSetters = new UnboxingAtom.FieldSetterNode[fieldSetterFactories.length];
for (int i = 0; i < fieldSetterFactories.length; i++) {
if (fieldSetterFactories[i] != null) {
this.uncachedFieldSetters[i] = fieldSetterFactories[i].getUncachedInstance();
}
}
for (int i = 0; i < fieldGetterFactories.length; i++) {
this.uncachedFieldGetters[i] = fieldGetterFactories[i].getUncachedInstance();
assert this.uncachedFieldGetters[i] != null;
if (args[i].isSuspended()) {
this.uncachedFieldGetters[i] =
SuspendedFieldGetterNode.build(
this.uncachedFieldGetters[i], this.uncachedFieldSetters[i]);
}
}
}

public static boolean isAritySupported(int arity) {
Expand All @@ -98,7 +107,7 @@ public static boolean isAritySupported(int arity) {
* factories for getters, setters and instantiators.
*/
@SuppressWarnings("unchecked")
public static Layout create(int arity, long typeFlags) {
public static Layout create(int arity, long typeFlags, ArgumentDefinition[] args) {
if (arity > 32) {
throw new IllegalArgumentException("Too many fields in unboxed atom");
}
Expand Down Expand Up @@ -137,7 +146,7 @@ public static Layout create(int arity, long typeFlags) {
var instantiatorFactory = LayoutFactory.getInstantiatorNodeFactory(numUnboxed, numBoxed);

return new Layout(
typeFlags, fieldToStorage, getterFactories, setterFactories, instantiatorFactory);
typeFlags, fieldToStorage, getterFactories, setterFactories, instantiatorFactory, args);
}

public UnboxingAtom.FieldGetterNode[] getUncachedFieldGetters() {
Expand All @@ -148,6 +157,10 @@ public UnboxingAtom.FieldGetterNode[] buildGetters() {
var getters = new UnboxingAtom.FieldGetterNode[fieldGetterFactories.length];
for (int i = 0; i < fieldGetterFactories.length; i++) {
getters[i] = fieldGetterFactories[i].createNode();
if (args[i].isSuspended()) {
var setterOrNull = buildSetter(i);
getters[i] = SuspendedFieldGetterNode.build(getters[i], setterOrNull);
}
}
return getters;
}
Expand All @@ -157,15 +170,20 @@ public UnboxingAtom.FieldGetterNode getUncachedFieldGetter(int index) {
}

public UnboxingAtom.FieldGetterNode buildGetter(int index) {
return fieldGetterFactories[index].createNode();
var node = fieldGetterFactories[index].createNode();
if (args[index].isSuspended()) {
node = SuspendedFieldGetterNode.build(node, buildSetter(index));
}
return node;
}

public UnboxingAtom.FieldSetterNode getUncachedFieldSetter(int index) {
return uncachedFieldSetters[index];
}

public UnboxingAtom.FieldSetterNode buildSetter(int index) {
return fieldSetterFactories[index].createNode();
var fieldSetterFactory = fieldSetterFactories[index];
return fieldSetterFactory == null ? null : fieldSetterFactory.createNode();
}

public boolean isDoubleAt(int fieldIndex) {
Expand Down Expand Up @@ -233,7 +251,7 @@ public Object execute(Object[] arguments) {
if (layouts.length == this.unboxedLayouts.length) {
// Layouts stored in this node are probably up-to-date; create a new one and try to
// register it.
var newLayout = Layout.create(arity, flags);
var newLayout = Layout.create(arity, flags, boxedLayout.layout.args);
constructor.atomicallyAddLayout(newLayout, this.unboxedLayouts.length);
}
updateFromConstructor();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package org.enso.interpreter.runtime.callable.atom.unboxing;

import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
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.function.Function;
import org.enso.interpreter.runtime.state.State;

/**
* Getter node that reads a field value. If the value is a thunk the node
* evaluates it and replaces the original lazy value with the new value.
*/
final class SuspendedFieldGetterNode extends UnboxingAtom.FieldGetterNode {
@Node.Child
private UnboxingAtom.FieldSetterNode set;
@Node.Child
private UnboxingAtom.FieldGetterNode get;
@Node.Child
private InvokeFunctionNode invoke = InvokeFunctionNode.build(
new CallArgumentInfo[0], InvokeCallableNode.DefaultsExecutionMode.EXECUTE, InvokeCallableNode.ArgumentsExecutionMode.EXECUTE
);

private SuspendedFieldGetterNode(UnboxingAtom.FieldGetterNode get, UnboxingAtom.FieldSetterNode set) {
this.get = get;
this.set = set;
}

static UnboxingAtom.FieldGetterNode build(UnboxingAtom.FieldGetterNode get, UnboxingAtom.FieldSetterNode set) {
return new SuspendedFieldGetterNode(get, set);
}

@Override
public Object execute(Atom atom) {
java.lang.Object value = get.execute(atom);
if (value instanceof Function fn && fn.isThunk()) {
try {
org.enso.interpreter.runtime.EnsoContext ctx = EnsoContext.get(this);
java.lang.Object newValue = invoke.execute(fn, null, State.create(ctx), new Object[0]);
set.execute(atom, newValue);
return newValue;
} catch (AbstractTruffleException ex) {
var rethrow = new SuspendedException(ex);
set.execute(atom, rethrow);
throw ex;
}
} else if (value instanceof SuspendedException suspended) {
throw suspended.ex;
} else {
return value;
}
}

private static final class SuspendedException implements TruffleObject {
final AbstractTruffleException ex;

SuspendedException(AbstractTruffleException ex) {
this.ex = ex;
}
}
}
Loading