Skip to content

Commit

Permalink
Use BuiltinRootNode.ArgNode to extract argument for a builtin method (
Browse files Browse the repository at this point in the history
#12201)

- as #12192 (comment) states:
- there is a need to extract values from `EnsoMultiValue` in the _"builtin method prelude code"_
- to enable _specializations based on type of arguments_, we need to wrap such _prelude_ by a `Node`
- hence introducing `ArgNode` & co.
- it kinda replaces the _"static code emitted"_ by annotation processor
- with Truffle `@Specialization`s done inside of `ArgNode`
- making this all more Truffle-like
  • Loading branch information
JaroslavTulach authored Feb 8, 2025
1 parent a829bdb commit 68ccb3d
Show file tree
Hide file tree
Showing 40 changed files with 479 additions and 327 deletions.
1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,6 @@ lazy val enso = (project in file("."))
`text-buffer`,
`version-output`,
`ydoc-polyfill`,
`ydoc-server`,
`zio-wrapper`
)
.settings(Global / concurrentRestrictions += Tags.exclusive(Exclusive))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ public class AtomBenchmarks {
import Standard.Base.Data.Numbers
main = length ->
generator = acc -> i -> if i == 0 then acc else @Tail_Call generator (List.Cons i acc) (i - 1)
generator = acc -> i ->
if i == 0 then acc else
list = List.Cons i acc
less = i - 1
@Tail_Call generator list less
res = generator List.Nil length
res
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public void initializeBenchmark(BenchmarkParams params) throws Exception {
"""
from Standard.Base import all
all_length v = v.fold 0 (sum -> str -> sum + str.length)
all_length v = v.fold 0 sum-> str->
sum + str.length
create rep len =
s = "Long string".repeat rep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public void warningsInCondAndThenOrElse() throws Exception {
var no = check.execute(f, y, n);

assertEquals("Yes", yes.asString());
assertWarning("GoodMaybe", yes);
assertWarning("MaybeGood", yes);
assertEquals("No", no.asString());
assertWarning("BadMaybe not", no);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,17 @@ public void toTextIsCalledForListings() throws Exception {
assertEquals("Half", 2, r.getArrayElement(1).asInt());
assertEquals("Two", 2, r.getArrayElement(2).asInt());
assertEquals("No output printed", "", out.toString());
assertThat(
"Stderr contains some warnings about one\n",
err.toString(),
containsString("one = 1\n ! Beware of ONE"));
assertThat(
"Stderr contains some warnings about half\n",
err.toString(),
containsString("half = 2\n ! Beware of HALF"));
assertThat(
"Stderr contains some warnings",
err.toString(),
AllOf.allOf(
containsString("half = 2\n ! Beware of HALF"),
containsString("one = 1\n ! Beware of ONE"),
containsString("two = 2\n ! Beware of TWO\n ! Beware of HALF"),
containsString("two = 2")));
containsString("two = 2\n ! Beware of HALF\n ! Beware of TWO"));
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package org.enso.interpreter.node.expression.builtin;

import com.oracle.truffle.api.interop.TruffleObject;
import org.enso.interpreter.dsl.BuiltinType;

@BuiltinType(name = "Standard.Base.Any.Any")
public class Any extends Builtin {
public final class Any extends Builtin {
public Any() {
super(TruffleObject.class);
}

@Override
public Class<? extends Builtin> getSuperType() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
// Before moving this definition to the `bool` package, as we should, one would have to address that
// problem first.
@BuiltinType(name = "Standard.Base.Data.Boolean.Boolean")
public class Boolean extends Builtin {
public final class Boolean extends Builtin {
public Boolean() {
super(java.lang.Boolean.class);
}

@Override
protected List<Cons> getDeclaredConstructors() {
return List.of(new Cons("False"), new Cons("True"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,33 @@ private AtomConstructor build(EnsoLanguage language, ModuleScope.Builder scope,
}
}

private final Class<?> representationType;
private final String name;

public Builtin() {
name = this.getClass().getSimpleName().replaceAll("([^_A-Z])([A-Z])", "$1_$2");
protected Builtin(String representationType) {
this(findType(representationType));
}

protected Builtin(Class<?> representationType) {
this.representationType = representationType;
this.name = this.getClass().getSimpleName().replaceAll("([^_A-Z])([A-Z])", "$1_$2");
}

private static Class<?> findType(String fqn) {
try {
return Class.forName(fqn);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(ex);
}
}

private @CompilerDirectives.CompilationFinal Type type;
private @CompilerDirectives.CompilationFinal(dimensions = 1) AtomConstructor[] constructors;

public final boolean isRepresentedBy(Class<?> clazz) {
return representationType == clazz;
}

protected Class<? extends Builtin> getSuperType() {
return Any.class;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
package org.enso.interpreter.node.expression.builtin;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.EnsoLanguage;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.hash.EnsoHashMap;
import org.enso.interpreter.runtime.data.hash.HashMapInsertAllNode;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.warning.AppendWarningNode;
import org.enso.interpreter.runtime.warning.WarningsLibrary;
import org.enso.pkg.QualifiedName;

/** Root node for use by all the builtin functions. */
@NodeInfo(shortName = "BuiltinRoot", description = "Root node for builtin functions.")
public abstract class BuiltinRootNode extends RootNode {
private QualifiedName moduleName;
private QualifiedName typeName;

protected BuiltinRootNode(EnsoLanguage language) {
super(language);
}

protected QualifiedName moduleName = null;
protected QualifiedName typeName = null;

/** Get the module name where the builtin is defined. */
public QualifiedName getModuleName() {
return moduleName;
Expand Down Expand Up @@ -52,4 +70,159 @@ public void setTypeName(QualifiedName typeName) {
*/
@Override
public abstract String getName();

protected static final class ArgContext {
private TruffleObject returnValue;
private EnsoHashMap warnings;

public ArgContext() {}

public TruffleObject getReturnValue() {
return returnValue;
}

public boolean hasWarnings() {
return this.warnings != null;
}

private void addWarnings(
VirtualFrame frame, HashMapInsertAllNode insertNode, EnsoHashMap newWarnings) {
if (this.warnings == null) {
this.warnings = newWarnings;
} else {
int maxWarnings = EnsoContext.get(insertNode).getWarningsLimit();
this.warnings = insertNode.executeInsertAll(frame, this.warnings, newWarnings, maxWarnings);
}
}
}

protected abstract static class ArgNode extends Node {
private static final byte IS_SELF = 0x01;
private static final byte REQUIRES_CAST = 0x04;
private static final byte CHECK_ERRORS = 0x08;
private static final byte CHECK_PANIC_SENTINEL = 0x10;
private static final byte CHECK_WARNINGS = 0x20;
private final byte flags;
@CompilerDirectives.CompilationFinal private Type ensoType;

@Child private WarningsLibrary warnings;
@Child private AppendWarningNode appendWarningNode;
@Child private HashMapInsertAllNode mapInsertAllNode;

private final BranchProfile errorsTaken = BranchProfile.create();
private final BranchProfile sentinelTaken = BranchProfile.create();

ArgNode(byte flags) {
this.flags = flags;
if (is(CHECK_WARNINGS)) {
this.warnings = WarningsLibrary.getFactory().createDispatched(5);
}
}

private boolean is(byte what) {
return (flags & what) != 0;
}

@SuppressWarnings("unchecked")
public final <T> T processArgument(
VirtualFrame frame, Class<T> type, Object value, ArgContext context) {
assert value != null;
if (is(CHECK_ERRORS) && value instanceof DataflowError err) {
errorsTaken.enter();
context.returnValue = err;
return null;
}
if (is(CHECK_PANIC_SENTINEL) && value instanceof PanicSentinel sentinel) {
sentinelTaken.enter();
throw sentinel;
}
if (warnings != null) {
if (warnings.hasWarnings(value)) {
if (mapInsertAllNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.mapInsertAllNode = insert(HashMapInsertAllNode.build());
}
try {
context.addWarnings(frame, mapInsertAllNode, warnings.getWarnings(value, false));
value = warnings.removeWarnings(value);
} catch (UnsupportedMessageException ex) {
throw raise(RuntimeException.class, ex);
}
}
}
if (is(REQUIRES_CAST) && type != Object.class) {
var ctx = EnsoContext.get(this);
if (this.ensoType == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
var builtin = ctx.getBuiltins().getByRepresentationType(type);
if (builtin == null) {
this.ensoType = ctx.getBuiltins().any();
} else {
this.ensoType = builtin.getType();
}
}
var conv = executeConversion(value);
if (conv == null) {
CompilerDirectives.transferToInterpreter();
var err = ctx.getBuiltins().error().makeTypeError(this.ensoType, value, type.getName());
throw new PanicException(err, this);
}
return type.cast(conv);
} else {
return type.cast(value);
}
}

public final Object processWarnings(VirtualFrame frame, Object result, ArgContext context) {
assert context.warnings != null;
if (this.appendWarningNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.appendWarningNode = insert(AppendWarningNode.build());
}
return appendWarningNode.executeAppend(frame, result, context.warnings);
}

abstract Object executeConversion(Object obj);

@Specialization
final Object extractMultiValue(EnsoMultiValue emv, @Cached EnsoMultiValue.CastToNode castTo) {
var extracted = castTo.findTypeOrNull(ensoType, emv, false, false);
return extracted;
}

@Fallback
final Object justReturnIt(Object obj) {
return obj;
}

public static ArgNode create(
boolean isSelf,
boolean requiresCast,
boolean checkErrors,
boolean checkPanicSentinel,
boolean checkWarnings) {
byte flags = 0x00;
if (isSelf) {
flags |= IS_SELF;
}
if (requiresCast) {
flags |= REQUIRES_CAST;
}
if (checkErrors) {
flags |= CHECK_ERRORS;
}
if (checkPanicSentinel) {
flags |= CHECK_PANIC_SENTINEL;
}
if (checkWarnings) {
flags |= CHECK_WARNINGS;
}
return BuiltinRootNodeFactory.ArgNodeGen.create(flags);
}
}

@SuppressWarnings("unchecked")
private static <E extends Exception> E raise(Class<E> clazz, Throwable t) throws E {
throw (E) t;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package org.enso.interpreter.node.expression.builtin;

import org.enso.interpreter.dsl.BuiltinType;
import org.enso.interpreter.runtime.error.DataflowError;

@BuiltinType(name = "Standard.Base.Error.Error")
public class Error extends Builtin {
public final class Error extends Builtin {
public Error() {
super(DataflowError.class);
}

@Override
protected Class<? extends Builtin> getSuperType() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import org.enso.interpreter.dsl.BuiltinType;

@BuiltinType(name = "Standard.Base.Nothing.Nothing")
public class Nothing extends Builtin {
public final class Nothing extends Builtin {
public Nothing() {
super(Void.class);
}

@Override
public boolean containsValues() {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
import org.enso.interpreter.dsl.BuiltinType;

@BuiltinType
public class Polyglot extends Builtin {}
public final class Polyglot extends Builtin {
public Polyglot() {
super(Object.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
public abstract class UniquelyConstructibleBuiltin extends Builtin {
private @CompilerDirectives.CompilationFinal AtomConstructor uniqueConstructor;

protected UniquelyConstructibleBuiltin() {
super(Object.class);
}

public final AtomConstructor getUniqueConstructor() {
return uniqueConstructor;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@
import org.enso.interpreter.node.expression.builtin.Builtin;

@BuiltinType
public class Debug extends Builtin {}
public class Debug extends Builtin {
public Debug() {
super(Object.class);
}
}
Loading

0 comments on commit 68ccb3d

Please sign in to comment.