Skip to content

Commit

Permalink
Avoid allocating Vector at all. Expose object with value and fill pro…
Browse files Browse the repository at this point in the history
…perties.
  • Loading branch information
JaroslavTulach committed Jan 6, 2023
1 parent 72255fa commit 9484ae0
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 46 deletions.
8 changes: 4 additions & 4 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 @@ -215,12 +215,12 @@ type List
Cons h t ->
v = f h
res = Meta.atom_with_hole (e -> Cons v e)
fill res.first
@Tail_Call go t res.second
fill res.value
@Tail_Call go t res.fill
v = f h
res = Meta.atom_with_hole (e -> Cons v e)
go t res.second
res.first
go t res.fill
res.value

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

Expand Down
13 changes: 5 additions & 8 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso
Original file line number Diff line number Diff line change
Expand Up @@ -276,17 +276,14 @@ new_atom constructor fields = @Builtin_Method "Meta.new_atom"

## PRIVATE

Constructs a new atom with a "hole". Returns a `Pair` of the newly created
atom and a function to "fill the hole" later.
Constructs a new atom with a "hole". Returns an object with `value` and
`fill` properties. Value contains the created atom and `fill` holds a
function to "fill the hole" later.

Arguments:
- factory: a function that takes the "hole" element and returns newly created atom
atom_with_hole : (Any -> Atom) -> Pair Atom (Any -> Nothing)
atom_with_hole factory =
vec = atom_with_hole_builtin factory
Pair.new (vec.at 0) (vec.at 1)

atom_with_hole_builtin factory = @Builtin_Method "Meta.atom_with_hole_builtin"
atom_with_hole : (Any -> Atom) -> Any
atom_with_hole factory = @Builtin_Method "Meta.atom_with_hole_builtin"

## UNSTABLE
ADVANCED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
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 com.oracle.truffle.api.profiles.ValueProfile;
import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.runtime.state.State;

Expand All @@ -35,60 +38,95 @@ static AtomWithAHoleNode build() {
return AtomWithAHoleNodeGen.create();
}


abstract Vector execute(VirtualFrame frame, Object factory);
abstract Object execute(VirtualFrame frame, Object factory);

static InvokeCallableNode callWithHole() {
return InvokeCallableNode.build(
new CallArgumentInfo[] {new CallArgumentInfo()},
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
return InvokeCallableNode.build(
new CallArgumentInfo[] {new CallArgumentInfo()},
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
}

@Specialization
Vector doExecute(
Object doExecute(
VirtualFrame frame,
Object factory,
@Cached("callWithHole()") InvokeCallableNode iop,
@Cached SwapAtomFieldNode swapNode
) {
var ctx = EnsoContext.get(this);
var lazy = new HoleInAtom();
var r = iop.execute(factory, frame, State.create(ctx), new Object[] { 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));
var result = iop.execute(factory, frame, State.create(ctx), new Object[] { lazy });
if (result instanceof Atom atom) {
var index = swapNode.findHoleIndex(atom, lazy);
if (index >= 0) {
var function = swapNode.createFn(lazy);
lazy.init(atom, index, function);
return lazy;
}
}
throw new PanicException(ctx.getBuiltins().error().makeUninitializedStateError(r), this);
throw new PanicException(ctx.getBuiltins().error().makeUninitializedStateError(result), this);
}

@ExportLibrary(InteropLibrary.class)
static final class HoleInAtom implements TruffleObject {
Atom result;
int index;
Function function;

HoleInAtom() {
}

void init(Atom result, int index, Function function) {
this.result = result;
this.index = index;
this.function = function;
}

@ExportMessage boolean hasMembers() {
return true;
}

@ExportMessage boolean isMemberReadable(String member) {
return switch (member) {
case "value", "fill" -> true;
default -> false;
};
}

@ExportMessage Object getMembers(boolean includeInternal) throws UnsupportedMessageException {
return new Array("value", "fill");
}

@ExportMessage
Object readMember(String name) throws UnknownIdentifierException {
if ("value".equals(name)) {
return result;
}
if ("fill".equals(name)) {
return function;
}
throw UnknownIdentifierException.create(name);
}

@ExportMessage
String toDisplayString(boolean pure) {
return "Meta.atom_with_hole";
}
}
static final class SwapAtomFieldNode extends RootNode {
private final FunctionSchema schema;
private final ValueProfile sameAtom = ValueProfile.createClassProfile();
@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 ArgumentDefinition(0, "lazy", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(1, "value", ArgumentDefinition.ExecutionMode.EXECUTE)
}, new boolean[]{
true, true, true, false
true, false
}, new CallArgumentInfo[0]);
}

Expand Down Expand Up @@ -130,8 +168,8 @@ private int findHoleIndexLoop(Object[] arr, HoleInAtom lazy) {
return -1;
}

Function createFn(Atom atom, int index, HoleInAtom lazy) {
var preArgs = new Object[]{atom, index, lazy, null};
Function createFn(HoleInAtom lazy) {
var preArgs = new Object[]{lazy, null};
return new Function(
getCallTarget(),
null,
Expand All @@ -144,13 +182,13 @@ Function createFn(Atom atom, int index, HoleInAtom lazy) {
@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];
}
if (args[0] instanceof HoleInAtom lazy) {
var fields = lazy.result.getFields();
var newValue = args[1];
if (fields[lazy.index] == lazy) {
fields[lazy.index] = newValue;
}
return newValue;
}
return EnsoContext.get(this).getBuiltins().nothing();
}
Expand Down
11 changes: 4 additions & 7 deletions test/Tests/src/Semantic/Meta_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,9 @@ spec =
Test.group "Atom with holes" <|
Test.specify "construct and fill" <|
pair = Meta.atom_with_hole (e -> My_Type.Value 1 e 3)
case pair of
_ : Pair -> Nothing
_ -> Test.fail "Should return a Pair"

atom = pair.first
fill = pair.second
atom = pair.value
fill = pair.fill

Meta.is_atom atom . should_be_true

Expand Down Expand Up @@ -214,8 +211,8 @@ spec =

Test.specify "only one atom_with_hole is used" <|
pair = Meta.atom_with_hole (e -> My_Type.Value e e e)
atom = pair.first
fill = pair.second
atom = pair.value
fill = pair.fill

Meta.is_atom atom . should_be_true

Expand Down

0 comments on commit 9484ae0

Please sign in to comment.