Bump GraalVM to 20.1 (#775)

This commit is contained in:
Ara Adkins 2020-05-21 11:46:06 +01:00 committed by GitHub
parent b4e24c3c97
commit 5420ca9482
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 200 additions and 183 deletions

View File

@ -8,7 +8,7 @@ on:
env:
# Please ensure that this is in sync with graalAPIVersion in build.sbt
graalVersion: 20.0.0
graalVersion: 20.1.0
javaVersion: java8
# Please ensure that this is in sync with project/build.properties
sbtVersion: 1.3.10

View File

@ -14,7 +14,7 @@ import scala.sys.process._
// ============================================================================
val scalacVersion = "2.13.2"
val graalVersion = "20.0.0"
val graalVersion = "20.1.0"
val ensoVersion = "0.0.1"
organization in ThisBuild := "org.enso"
scalaVersion in ThisBuild := scalacVersion

View File

@ -0,0 +1,46 @@
package org.enso.interpreter.node.controlflow;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.function.Function;
/** An abstract representation of a case branch. */
@NodeInfo(shortName = "case_branch", description = "Represents a case branch at runtime.")
public abstract class BranchNode extends BaseNode {
/**
* Executes the case expression with an atom scrutinee.
*
* @param frame the stack frame in which to execute
* @param target the atom to match and destructure
* @throws UnexpectedResultException when the result of desctructuring {@code target} can't be
* represented as a value of the expected return type
*/
public abstract void executeAtom(VirtualFrame frame, Atom target)
throws UnexpectedResultException;
/**
* Executes the case expression with a function scrutinee.
*
* @param frame the stack frame in which to execute
* @param target the function to match
* @throws UnexpectedResultException when the result of desctructuring {@code target} can't be
* represented as a value of the expected return type
*/
public abstract void executeFunction(VirtualFrame frame, Function target)
throws UnexpectedResultException;
/**
* * Executes the case expression with a number scrutinee.
*
* @param frame the stack frame in which to execute
* @param target the number to match
* @throws UnexpectedResultException when the result of desctructuring {@code target} can't be
* represented as a value of the expected return type
*/
public abstract void executeNumber(VirtualFrame frame, long target)
throws UnexpectedResultException;
}

View File

@ -1,46 +1,134 @@
package org.enso.interpreter.node.controlflow;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.enso.interpreter.node.BaseNode;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.RuntimeError;
import org.enso.interpreter.runtime.error.TypeError;
/** An abstract representation of a case expression. */
@NodeInfo(shortName = "CaseOf", description = "Represents a case expression at runtime")
public abstract class CaseNode extends BaseNode {
/**
* A node representing a pattern match on an arbitrary runtime value.
*
* <p>Has a scrutinee node and a collection of {@link BranchNode}s. The case nodes get executed one by
* one, until one throws an {@link BranchSelectedException}, the value of which becomes the result
* of this pattern match.
*/
@NodeChild(value = "scrutinee", type = ExpressionNode.class)
@NodeInfo(shortName = "case_of", description = "The runtime representation of a case expression.")
public abstract class CaseNode extends ExpressionNode {
@Children private final BranchNode[] cases;
@Child private BranchNode fallback;
private final BranchProfile typeErrorProfile = BranchProfile.create();
CaseNode(BranchNode[] cases, BranchNode fallback) {
this.cases = cases;
this.fallback = fallback;
}
/**
* Executes the case expression with an atom scrutinee.
* Creates an instance of this node.
*
* @param frame the stack frame in which to execute
* @param target the atom to match and destructure
* @throws UnexpectedResultException when the result of desctructuring {@code target} can't be
* represented as a value of the expected return type
* @param cases the case branches
* @param fallback the fallback branch
* @param scrutinee the value being scrutinised
* @return a node representing a pattern match
*/
public abstract void executeAtom(VirtualFrame frame, Atom target)
throws UnexpectedResultException;
public static CaseNode build(BranchNode[] cases, BranchNode fallback, ExpressionNode scrutinee) {
return CaseNodeGen.create(cases, fallback, scrutinee);
}
/**
* Executes the case expression with a function scrutinee.
*
* @param frame the stack frame in which to execute
* @param target the function to match
* @throws UnexpectedResultException when the result of desctructuring {@code target} can't be
* represented as a value of the expected return type
*/
public abstract void executeFunction(VirtualFrame frame, Function target)
throws UnexpectedResultException;
@Specialization
Object doError(VirtualFrame frame, RuntimeError error) {
return error;
}
/**
* * Executes the case expression with a number scrutinee.
// TODO[MK]: The atom, number and function cases are very repetitive and should be refactored.
// It poses some engineering challenge the approaches tried so far included passing the only
// changing line as a lambda and introducing a separate node between this and the CaseNodes.
// Both attempts resulted in a performance drop.
@ExplodeLoop
@Specialization
Object doAtom(VirtualFrame frame, Atom atom) {
try {
for (BranchNode branchNode : cases) {
branchNode.executeAtom(frame, atom);
}
fallback.executeAtom(frame, atom);
CompilerDirectives.transferToInterpreter();
throw new RuntimeException("Impossible behavior.");
} catch (BranchSelectedException e) {
// Note [Branch Selection Control Flow]
frame.setObject(getStateFrameSlot(), e.getResult().getState());
return e.getResult().getValue();
} catch (UnexpectedResultException e) {
typeErrorProfile.enter();
throw new TypeError("Expected an Atom, got " + e.getResult(), this);
}
}
@ExplodeLoop
@Specialization
Object doFunction(VirtualFrame frame, Function function) {
try {
for (BranchNode branchNode : cases) {
branchNode.executeFunction(frame, function);
}
fallback.executeFunction(frame, function);
CompilerDirectives.transferToInterpreter();
throw new RuntimeException("Impossible behavior.");
} catch (BranchSelectedException e) {
// Note [Branch Selection Control Flow]
frame.setObject(getStateFrameSlot(), e.getResult().getState());
return e.getResult().getValue();
} catch (UnexpectedResultException e) {
typeErrorProfile.enter();
throw new TypeError("Expected an Atom, got " + e.getResult(), this);
}
}
@ExplodeLoop
@Specialization
Object doNumber(VirtualFrame frame, long number) {
try {
for (BranchNode branchNode : cases) {
branchNode.executeNumber(frame, number);
}
fallback.executeNumber(frame, number);
CompilerDirectives.transferToInterpreter();
throw new RuntimeException("Impossible behavior.");
} catch (BranchSelectedException e) {
// Note [Branch Selection Control Flow]
frame.setObject(getStateFrameSlot(), e.getResult().getState());
return e.getResult().getValue();
} catch (UnexpectedResultException e) {
typeErrorProfile.enter();
throw new TypeError("Expected an Atom: " + e.getResult(), this);
}
}
/* Note [Branch Selection Control Flow]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Truffle provides no easy way to return control flow from multiple paths. This is entirely due
* to Java's (current) lack of support for case-expressions.
*
* @param frame the stack frame in which to execute
* @param target the number to match
* @throws UnexpectedResultException when the result of desctructuring {@code target} can't be
* represented as a value of the expected return type
* As a result, this implementation resorts to using an exception to short-circuit evaluation on
* a successful match. This short-circuiting also allows us to hand the result of evaluation back
* out to the caller (here) wrapped in the exception.
*
* The main alternative to this was desugaring to a nested-if, which would've been significantly
* harder to maintain, and also resulted in significantly higher code complexity.
*/
public abstract void executeNumber(VirtualFrame frame, long target)
throws UnexpectedResultException;
}

View File

@ -15,14 +15,14 @@ import org.enso.interpreter.runtime.type.TypesGen;
/** An implementation of the case expression specialised to working on constructors. */
@NodeInfo(shortName = "ConsCaseNode")
public class ConstructorCaseNode extends CaseNode {
public class ConstructorBranchNode extends BranchNode {
@Child private ExpressionNode matcher;
@Child private ExpressionNode branch;
@Child private ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create();
private final ConditionProfile profile = ConditionProfile.createCountingProfile();
private final ConditionProfile atomTypeProfile = ConditionProfile.createCountingProfile();
ConstructorCaseNode(ExpressionNode matcher, ExpressionNode branch) {
ConstructorBranchNode(ExpressionNode matcher, ExpressionNode branch) {
this.matcher = matcher;
this.branch = branch;
}
@ -34,8 +34,8 @@ public class ConstructorCaseNode extends CaseNode {
* @param branch the expression to be executed if (@code matcher} matches
* @return a node for matching in a case expression
*/
public static ConstructorCaseNode build(ExpressionNode matcher, ExpressionNode branch) {
return new ConstructorCaseNode(matcher, branch);
public static ConstructorBranchNode build(ExpressionNode matcher, ExpressionNode branch) {
return new ConstructorBranchNode(matcher, branch);
}
/**
@ -46,7 +46,7 @@ public class ConstructorCaseNode extends CaseNode {
*
* @param frame the stack frame in which to execute
* @param target the atom to destructure
* @throws UnexpectedResultException
* @throws UnexpectedResultException when evaluation fails
*/
@Override
public void executeAtom(VirtualFrame frame, Atom target) throws UnexpectedResultException {

View File

@ -13,17 +13,17 @@ import org.enso.interpreter.runtime.error.InexhaustivePatternMatchException;
@NodeInfo(
shortName = "DefaultFallback",
description = "A fallback branch for a case expression when none is explicitly provided")
public class DefaultFallbackNode extends CaseNode {
public class DefaultFallbackBranchNode extends BranchNode {
DefaultFallbackNode() {}
DefaultFallbackBranchNode() {}
/**
* Creates an instance of this node.
*
* @return a default fallback node
*/
public static DefaultFallbackNode build() {
return new DefaultFallbackNode();
public static DefaultFallbackBranchNode build() {
return new DefaultFallbackBranchNode();
}
/**

View File

@ -15,11 +15,11 @@ import org.enso.interpreter.runtime.callable.function.Function;
* executes the catch-all case code.
*/
@NodeInfo(shortName = "Fallback", description = "An explicit fallback branch in a case expression")
public class FallbackNode extends CaseNode {
public class FallbackBranchNode extends BranchNode {
@Child private ExpressionNode functionNode;
@Child private ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create();
FallbackNode(ExpressionNode functionNode) {
FallbackBranchNode(ExpressionNode functionNode) {
this.functionNode = functionNode;
}
@ -29,8 +29,8 @@ public class FallbackNode extends CaseNode {
* @param functionNode the function to execute in this case
* @return a fallback node
*/
public static FallbackNode build(ExpressionNode functionNode) {
return new FallbackNode(functionNode);
public static FallbackBranchNode build(ExpressionNode functionNode) {
return new FallbackBranchNode(functionNode);
}
private void execute(VirtualFrame frame, Object target) throws UnexpectedResultException {

View File

@ -1,132 +0,0 @@
package org.enso.interpreter.node.controlflow;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.RuntimeError;
import org.enso.interpreter.runtime.error.TypeError;
/**
* A node representing a pattern match on an arbitrary runtime value.
*
* <p>Has a scrutinee node and a collection of {@link CaseNode}s. The case nodes get executed one by
* one, until one throws an {@link BranchSelectedException}, the value of which becomes the result
* of this pattern match.
*/
@NodeChild(value = "scrutinee", type = ExpressionNode.class)
public abstract class MatchNode extends ExpressionNode {
@Children private final CaseNode[] cases;
@Child private CaseNode fallback;
private final BranchProfile typeErrorProfile = BranchProfile.create();
MatchNode(CaseNode[] cases, CaseNode fallback) {
this.cases = cases;
this.fallback = fallback;
}
/**
* Creates an instance of this node.
*
* @param cases the case branches
* @param fallback the fallback branch
* @param scrutinee the value being scrutinised
* @return a node representing a pattern match
*/
public static MatchNode build(CaseNode[] cases, CaseNode fallback, ExpressionNode scrutinee) {
return MatchNodeGen.create(cases, fallback, scrutinee);
}
@Specialization
Object doError(VirtualFrame frame, RuntimeError error) {
return error;
}
// TODO[MK]: The atom, number and function cases are very repetitive and should be refactored.
// It poses some engineering challenge the approaches tried so far included passing the only
// changing line as a lambda and introducing a separate node between this and the CaseNodes.
// Both attempts resulted in a performance drop.
@ExplodeLoop
@Specialization
Object doAtom(VirtualFrame frame, Atom atom) {
try {
for (CaseNode caseNode : cases) {
caseNode.executeAtom(frame, atom);
}
fallback.executeAtom(frame, atom);
CompilerDirectives.transferToInterpreter();
throw new RuntimeException("Impossible behavior.");
} catch (BranchSelectedException e) {
// Note [Branch Selection Control Flow]
frame.setObject(getStateFrameSlot(), e.getResult().getState());
return e.getResult().getValue();
} catch (UnexpectedResultException e) {
typeErrorProfile.enter();
throw new TypeError("Expected an Atom, got " + e.getResult(), this);
}
}
@ExplodeLoop
@Specialization
Object doFunction(VirtualFrame frame, Function function) {
try {
for (CaseNode caseNode : cases) {
caseNode.executeFunction(frame, function);
}
fallback.executeFunction(frame, function);
CompilerDirectives.transferToInterpreter();
throw new RuntimeException("Impossible behavior.");
} catch (BranchSelectedException e) {
// Note [Branch Selection Control Flow]
frame.setObject(getStateFrameSlot(), e.getResult().getState());
return e.getResult().getValue();
} catch (UnexpectedResultException e) {
typeErrorProfile.enter();
throw new TypeError("Expected an Atom, got " + e.getResult(), this);
}
}
@ExplodeLoop
@Specialization
Object doNumber(VirtualFrame frame, long number) {
try {
for (CaseNode caseNode : cases) {
caseNode.executeNumber(frame, number);
}
fallback.executeNumber(frame, number);
CompilerDirectives.transferToInterpreter();
throw new RuntimeException("Impossible behavior.");
} catch (BranchSelectedException e) {
// Note [Branch Selection Control Flow]
frame.setObject(getStateFrameSlot(), e.getResult().getState());
return e.getResult().getValue();
} catch (UnexpectedResultException e) {
typeErrorProfile.enter();
throw new TypeError("Expected an Atom: " + e.getResult(), this);
}
}
/* Note [Branch Selection Control Flow]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Truffle provides no easy way to return control flow from multiple paths. This is entirely due
* to Java's (current) lack of support for case-expressions.
*
* As a result, this implementation resorts to using an exception to short-circuit evaluation on
* a successful match. This short-circuiting also allows us to hand the result of evaluation back
* out to the caller (here) wrapped in the exception.
*
* The main alternative to this was desugaring to a nested-if, which would've been significantly
* harder to maintain, and also resulted in significantly higher code complexity.
*/
}

View File

@ -2,12 +2,16 @@ package org.enso.interpreter.runtime.callable.atom;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/** A runtime representation of an Atom in Enso. */
@ExportLibrary(InteropLibrary.class)
public class Atom implements TruffleObject {
private final AtomConstructor constructor;
private @CompilationFinal(dimensions = 1) Object[] fields;
@ -79,4 +83,15 @@ public class Atom implements TruffleObject {
public String toString() {
return toString(false);
}
/**
* Displays a human-readable string representation of this atom.
*
* @param allowSideEffects whether or not to allow side effects in displaying the string
* @return a string representation of this atom
*/
@ExportMessage
public Object toDisplayString(boolean allowSideEffects) {
return this.toString();
}
}

View File

@ -445,20 +445,20 @@ class IRToTruffle(
"Case branch missing tail position information."
)
val caseNode = ConstructorCaseNode
val caseNode = ConstructorBranchNode
.build(this.run(branch.pattern), this.run(branch.expression))
caseNode.setTail(caseIsTail)
caseNode
})
.toArray[CaseNode]
.toArray[BranchNode]
// Note [Pattern Match Fallbacks]
val fallbackNode = fallback
.map(fb => FallbackNode.build(this.run(fb)))
.getOrElse(DefaultFallbackNode.build())
.map(fb => FallbackBranchNode.build(this.run(fb)))
.getOrElse(DefaultFallbackBranchNode.build())
val matchExpr = MatchNode.build(cases, fallbackNode, targetNode)
val matchExpr = CaseNode.build(cases, fallbackNode, targetNode)
setLocation(matchExpr, location)
case IR.Case.Branch(_, _, _, _, _) =>
throw new CompilerError("A CaseBranch should never occur here.")

View File

@ -2,7 +2,7 @@ package org.enso.interpreter.test.semantic
import org.enso.interpreter.node.callable.function.CreateFunctionNode
import org.enso.interpreter.node.callable.thunk.ForceNode
import org.enso.interpreter.node.callable.{ApplicationNode, SequenceLiteralNode}
import org.enso.interpreter.node.controlflow.MatchNode
import org.enso.interpreter.node.controlflow.CaseNode
import org.enso.interpreter.node.expression.literal.IntegerLiteralNode
import org.enso.interpreter.node.scope.{AssignmentNode, ReadLocalVariableNode}
import org.enso.interpreter.test.InterpreterTest
@ -113,7 +113,7 @@ class CodeLocationsTest extends InterpreterTest {
|
| foo x + foo y
|""".stripMargin
instrumenter.assertNodeExists(80, 109, classOf[MatchNode])
instrumenter.assertNodeExists(80, 109, classOf[CaseNode])
instrumenter.assertNodeExists(126, 7, classOf[ApplicationNode])
instrumenter.assertNodeExists(146, 9, classOf[AssignmentNode])
instrumenter.assertNodeExists(183, 5, classOf[ApplicationNode])