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

Removing Unsafe.set_atom_field #4023

Merged
merged 27 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6cc81c6
Removing test that relies on Unsafe
JaroslavTulach Jan 4, 2023
983c37e
Replacing set_atom_field with Meta.lazy_atom
JaroslavTulach Jan 4, 2023
ff7a474
invokeMember instead of execute
JaroslavTulach Jan 4, 2023
f3449b8
With incremental compilation there may not be any input files
JaroslavTulach Jan 5, 2023
e3840fb
Removing Unsafe from micro-distribution
JaroslavTulach Jan 5, 2023
3df23b3
Make fill function really a function that can be called
JaroslavTulach Jan 5, 2023
ba603af
Unit test to check basic functionality
JaroslavTulach Jan 5, 2023
c185161
Generate Uninitialized_State.Error if lazy_atom isn't used
JaroslavTulach Jan 5, 2023
f584555
Fail if non-atom is created
JaroslavTulach Jan 5, 2023
85c8cde
Allow only one lazy_atom in an Atom
JaroslavTulach Jan 5, 2023
c51714b
Speculate the hole index remains the same
JaroslavTulach Jan 5, 2023
28ca763
Specify the exact type for instanceof
JaroslavTulach Jan 5, 2023
eaeadf2
ListTest to verify behavior of operations that need @Tail_Call annota…
JaroslavTulach Jan 6, 2023
9222ff5
Renaming to Meta.atom_with_hole and returning Pair
JaroslavTulach Jan 6, 2023
dc9e60f
Formatting
JaroslavTulach Jan 6, 2023
11d7143
Adding also List.to_text test
JaroslavTulach Jan 6, 2023
73dfc7f
Merge branch 'develop' into wip/jtulach/NoSetAtomField_183578531
JaroslavTulach Jan 6, 2023
695775b
Benchmark to measure performance of List.map
JaroslavTulach Jan 6, 2023
b383ace
Using InvokeCallableNode instead of InteropLibrary
JaroslavTulach Jan 6, 2023
72255fa
Note about Meta.atom_with_hole in the changelog
JaroslavTulach Jan 6, 2023
9484ae0
Avoid allocating Vector at all. Expose object with value and fill pro…
JaroslavTulach Jan 6, 2023
8ccbecb
Make sure all List operation handle "infinite" lists
JaroslavTulach Jan 6, 2023
bfe5338
Pair import is no longer needed
JaroslavTulach Jan 6, 2023
a0906d0
Make sure List.init works for one, two and three elements
JaroslavTulach Jan 6, 2023
a8cd2bf
Using (Cons x _) per James suggestion
JaroslavTulach Jan 6, 2023
b883b2c
Merge branch 'develop' into wip/jtulach/NoSetAtomField_183578531
mergify[bot] Jan 6, 2023
0ee4225
Merging with develop branch
JaroslavTulach Jan 9, 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
35 changes: 13 additions & 22 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/List.enso
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import project.Data.Vector.Vector
import project.Error.Error
import project.Function.Function
import project.Nothing.Nothing
import project.Runtime.Unsafe
import project.Meta

from project.Data.Boolean import Boolean, True, False

Expand Down Expand Up @@ -209,9 +209,18 @@ type List
map self f = case self of
Nil -> Nil
Cons h t ->
res = Cons (f h) Nil
map_helper t res f
res
go : List -> Any -> (Any -> Any) -> Nothing
go list fill = case list of
Nil -> fill Nil
Cons h t ->
v = f h
res = Meta.lazy_atom (e -> Cons v e)
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
fill (res.at 0)
@Tail_Call go t (res.at 1)
v = f h
res = Meta.lazy_atom (e -> Cons v e)
go t (res.at 1)
res.at 0

## Applies a function to each element of the list.

Expand Down Expand Up @@ -379,21 +388,3 @@ type Empty_Error
to_display_text : Text
to_display_text self = "The List is empty."

## PRIVATE
A helper for the `map` function.

Arguments:
- list: The list to map over.
- cons: The current field to set.
- f: The function to apply to the value.

Uses unsafe field mutation under the hood, to rewrite `map` in
a tail-recursive manner. The mutation is purely internal and does not leak
to the user-facing API.
map_helper : List -> Any -> (Any -> Any) -> Nothing
map_helper list cons f = case list of
Nil -> Unsafe.set_atom_field cons 1 Nil
Cons h t ->
res = Cons (f h) Nil
Unsafe.set_atom_field cons 1 res
@Tail_Call map_helper t res f
3 changes: 3 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ get_constructor_name atom_constructor = @Builtin_Method "Meta.get_constructor_na
new_atom : Any -> Array -> Atom
new_atom constructor fields = @Builtin_Method "Meta.new_atom"

JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
lazy_atom : (Any -> Atom) -> Atom
lazy_atom constructor fields = @Builtin_Method "Meta.lazy_atom"
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved

## UNSTABLE
ADVANCED

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
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.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.Vector;
import org.enso.interpreter.runtime.error.PanicException;

@BuiltinMethod(
type = "Meta",
name = "lazy_atom",
description = "Creates a new atom with given constructor and fields.",
autoRegister = false)
public abstract class LazyAtomInstanceNode extends Node {

static LazyAtomInstanceNode build() {
return LazyAtomInstanceNodeGen.create();
}

abstract Vector execute(Object factory);

@Specialization(guards = "iop.isExecutable(factory)")
Vector doExecute(
Object factory,
@CachedLibrary(limit="3") InteropLibrary iop,
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
@Cached SwapAtomFieldNode swapNode
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
) {
var ctx = EnsoContext.get(this);
var lazy = new HoleInAtom();
try {
var r = iop.execute(factory, lazy);
if (r instanceof Atom a) {
var i = swapNode.findHoleIndex(a, lazy);
if (i >= 0) {
var function = swapNode.createFn(a, i, lazy);
return Vector.fromArray(new Array(a, function));
}
}
throw new PanicException(ctx.getBuiltins().error().makeUninitializedStateError(r), this);
} catch (UnsupportedTypeException ex) {
throw raise(RuntimeException.class, ex);
} catch (ArityException ex) {
throw raise(RuntimeException.class, ex);
} catch (UnsupportedMessageException ex) {
throw raise(RuntimeException.class, ex);
}
}

@SuppressWarnings("unchecked")
private static <E extends Exception> E raise(Class<E> type, Throwable t) throws E {
throw (E)t;
}

@ExportLibrary(InteropLibrary.class)
static final class HoleInAtom implements TruffleObject {
HoleInAtom() {
}

@ExportMessage
String toDisplayString(boolean pure) {
return "Meta.lazy_atom";
}
}
static final class SwapAtomFieldNode extends RootNode {
private final FunctionSchema schema;
@CompilerDirectives.CompilationFinal
private int lastIndex = -1;

private SwapAtomFieldNode() {
super(null);
this.schema = new FunctionSchema(FunctionSchema.CallerFrameAccess.NONE, new ArgumentDefinition[]{
new ArgumentDefinition(0, "instance", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(1, "lazy_index", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(2, "lazy", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(3, "value", ArgumentDefinition.ExecutionMode.EXECUTE)
}, new boolean[]{
true, true, true, false
}, new CallArgumentInfo[0]);
}

static SwapAtomFieldNode create() {
return new SwapAtomFieldNode();
}

int findHoleIndex(Atom atom, HoleInAtom lazy) {
var arr = atom.getFields();
if (lastIndex >= 0 && lastIndex < arr.length) {
if (arr[lastIndex] == lazy) {
return lastIndex;
}
}
int index = findHoleIndexLoop(arr, lazy);
if (index == -1) {
return -1;
}
if (lastIndex == -1) {
lastIndex = index;
CompilerDirectives.transferToInterpreterAndInvalidate();
return index;
} else {
if (lastIndex != -2) {
CompilerDirectives.transferToInterpreterAndInvalidate();
lastIndex = -2;
}
}
return index;
}

@CompilerDirectives.TruffleBoundary
private int findHoleIndexLoop(Object[] arr, HoleInAtom lazy) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == lazy) {
return i;
}
}
return -1;
}

Function createFn(Atom atom, int index, HoleInAtom lazy) {
var preArgs = new Object[]{atom, index, lazy, null};
return new Function(
getCallTarget(),
null,
schema,
preArgs,
new Object[]{}
);
}

@Override
public Object execute(VirtualFrame frame) {
var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());
if (args[0] instanceof Atom replace) {
if (args[1] instanceof Integer n) {
var fields = replace.getFields();
if (fields[n.intValue()] == args[2]) {
fields[n.intValue()] = args[3];
}
}
}
return EnsoContext.get(this).getBuiltins().nothing();
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -613,69 +613,6 @@ public String toString() {
}
}

// TODO[PM]: Re-enable (https://www.pivotaltracker.com/story/show/183854585)
@Test
@Ignore
public void unsafeRecursiveAtom() throws Exception {
Engine eng = Engine.newBuilder()
.allowExperimentalOptions(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../test/micro-distribution/component").toFile().getAbsolutePath()
).build();
Context ctx = Context.newBuilder()
.engine(eng)
.allowIO(true)
.allowHostClassLoading(true)
.allowHostClassLookup((c) -> true)
.build();
final Map<String, Language> langs = ctx.getEngine().getLanguages();
org.junit.Assert.assertNotNull("Enso found: " + langs, langs.get("enso"));

final URI onceUri = new URI("memory://once.enso");
final Source onesSrc = Source.newBuilder("enso", """
import Standard.Base.Runtime.Unsafe

type Gen
Empty
Generator a:Int tail:Gen

ones : Gen
ones =
g = Gen.Generator 1 Gen.Empty
Unsafe.set_atom_field g 1 g
g

next g = case g of
Gen.Generator a tail -> a
Gen.Empty -> -1
""", "ones.enso")
.uri(onceUri)
.buildLiteral();

var module = ctx.eval(onesSrc);
var ones = module.invokeMember("eval_expression", "ones");
var next = module.invokeMember("eval_expression", "next");


final var dbg = Debugger.find(eng);
final var values = new HashSet<String>();
try (var session = dbg.startSession((event) -> {
final DebugValue gVariable = findDebugValue(event, "g");
if (gVariable != null) {
final String str = gVariable.toDisplayString(false);
assertNotNull("The string shall always be computed for " + gVariable, str);
values.add(str);
}
event.getSession().suspendNextExecution();
})) {
session.suspendNextExecution();
var one = next.execute(ones);
Assert.assertEquals("First element from list of ones", 1, one.asInt());
}
Assert.assertEquals("Some values of g variable found: " + values, 1, values.size());
}

private static DebugValue findDebugValue(SuspendedEvent event, final String n) throws DebugException {
for (var v : event.getTopStackFrame().getScope().getDeclaredValues()) {
if (v.getName().contains(n)) {
Expand Down
11 changes: 8 additions & 3 deletions project/FrgaalJavaCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ object FrgaalJavaCompiler {
}

val (withTarget, noTarget) = sources0.partition(checkTarget)
val in = findUnder(3, noTarget.tail.fold(asPath(noTarget.head))(asCommon).asInstanceOf[Path])
val in = if (noTarget.isEmpty) {
None
} else {
Some(findUnder(3, noTarget.tail.fold(asPath(noTarget.head))(asCommon).asInstanceOf[Path]))
}
val generated = if (withTarget.isEmpty) {
None
} else {
Expand All @@ -133,8 +137,9 @@ object FrgaalJavaCompiler {
def storeArray(name: String, values : Seq[String]) = {
values.zipWithIndex.foreach { case (value, idx) => ensoProperties.setProperty(s"$name.$idx", value) }
}

ensoProperties.setProperty("input", in.toString())
if (in.isDefined) {
ensoProperties.setProperty("input", in.get.toString())
}
Comment on lines +140 to +142
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

      in.foreach { prop => 
        ensoProperties.setProperty("input", prop.toString())
      }

if (generated.isDefined) {
ensoProperties.setProperty("generated", generated.get.toString())
}
Expand Down
Loading