Truffle Tutorial Slides (30-60p) This is my own translation memo.
We do not guarantee mistranslations or omissions.
If there is a problem, delete it.
--Any type of key and value can be used
--The key is usually of type String
SL type | value | Implementation in Java?(Java Type in Implementation) |
---|---|---|
Number | Arbitrary precision integer | Long within 64 bits If it overflows java.lang.BigInteger |
Boolean | true or false | boolean |
String | Unicode string | java.lang.String |
Function | Reference to function | SLFunction |
Object | Key and value | DynamicObject |
Null | null | SLNull.SINGLETON |
Use primitive types as much as possible for performance
Don't use Java null in your Guest language
Abbreviation
Abbreviation
Getting Started
Abbreviation
I'm sorry I omit
Class Node
Base class of Truffle node? (base class of all Truffle tree nodes)
NodeUtil provides convenient utility methods.
public abstract class Node implements Cloneable {
public final Node getParent() { ... }
public final Iterable<Node> getChildren() { ... }
public final <T extends Node> T replace(T newNode) { ... }
public Node copy() { ... }
public SourceSection getSourceSection();
}
public final class SLIfNode extends SLStatementNode {
@Child private SLExpressionNode conditionNode;
@Child private SLStatementNode thenPartNode;
@Child private SLStatementNode elsePartNode;
public SLIfNode(SLExpressionNode conditionNode, SLStatementNode thenPartNode, SLStatementNode elsePartNode) {
this.conditionNode = conditionNode;
this.thenPartNode = thenPartNode;
this.elsePartNode = elsePartNode;
}
public void executeVoid(VirtualFrame frame) {
if (conditionNode.executeBoolean(frame)) {
thenPartNode.executeVoid(frame);
}else{
elsePartNode.executeVoid(frame);
}
}
}
Annotate the field of the child node with @Child and do not make it final.
public final class SLIfNode extends SLStatementNode {
@Child private SLExpressionNode conditionNode;
@Child private SLStatementNode thenPartNode;
@Child private SLStatementNode elsePartNode;
private final ConditionProfile condition = ConditionProfile.createCountingProfile();
public SLIfNode(SLExpressionNode conditionNode, SLStatementNode thenPartNode, SLStatementNode elsePartNode) {
this.conditionNode = conditionNode;
this.thenPartNode = thenPartNode;
this.elsePartNode = elsePartNode;
}
public void executeVoid(VirtualFrame frame) {
if (condition.profile(conditionNode.executeBoolean(frame))) {
thenPartNode.executeVoid(frame);
}else{
elsePartNode.executeVoid(frame);
}
}
}
Profiling with an interpreter allows the compiler to generate better code.
public final class SLBlockNode extends SLStatementNode {
@Children private final SLStatementNode[] bodyNodes;
public SLBlockNode(SLStatementNode[] bodyNodes) {
this.bodyNodes = bodyNodes;
}
@ExplodeLoop public void executeVoid(VirtualFrame frame) {
for (SLStatementNode statement : bodyNodes) {
statement.executeVoid(frame);
}
}
}
Fields that represent multiple nodes are represented by a final array with @Children.
Do I need to add @ExplodeLoop when repeating child elements? (The iteration of the children must be annotated with @ExplodeLoop)
public final class SLReturnNode extends SLStatementNode {
@Child private SLExpressionNode valueNode;
...
public void executeVoid(VirtualFrame frame) {
throw new SLReturnException(valueNode.executeGeneric(frame));
}
}
public final class SLReturnException extends ControlFlowException {
private final Object result;
...
}
public final class SLFunctionBodyNode extends SLExpressionNode {
@Child private SLStatementNode bodyNode;
...
public Object executeGeneric(VirtualFrame frame) {
try {
bodyNode.executeVoid(frame);
}catch (SLReturnException ex){
return ex.getResult();
}
return SLNull.SINGLETON;
}
}
Does the exception rewind all stack frames? (Exception unwinds all the interpreter stack frames of the method (loops, conditions, blocks, ...))
@NodeChildren({@NodeChild("leftNode"), @NodeChild("rightNode")})
public abstract class SLBinaryNode extends SLExpressionNode {
}
public abstract class SLAddNode extends SLBinaryNode {
@Specialization(rewriteOn = ArithmeticException.class)
protected final long add(long left, long right) {
return ExactMath.addExact(left, right);
}
@Specialization
protected final BigInteger add(BigInteger left, BigInteger right) {
return left.add(right);
}
@Specialization(guards = "isString(left, right)")
protected final String add(Object left, Object right) {
return left.toString() + right.toString();
}
protected final boolean isString(Object a, Object b) {
return a instanceof String || b instanceof String;
}
}
The order of the @Specialization methods is important. The first matching element is selected.
In all other specializations, guarding is implicit based on the method signature.
Code generated by factory methods
@GeneratedBy(SLAddNode.class)
public final class SLAddNodeGen extends SLAddNode {
public static SLAddNode create(SLExpressionNode leftNode, SLExpressionNode rightNode) {
...
}
...
}
@GeneratedBy(methodName = "add(long, long)", value = SLAddNode.class)
private static final class Add0Node_ extends BaseNode_ {
@Override public long executeLong(VirtualFrame frameValue) throws UnexpectedResultException {
long leftNodeValue_;
try {
leftNodeValue_ = root.leftNode_.executeLong(frameValue);
}catch (UnexpectedResultException ex){
Object rightNodeValue = executeRightNode_(frameValue);
return SLTypesGen.expectLong(getNext().execute_(frameValue, ex.getResult(), rightNodeValue));
}
long rightNodeValue_;
try {
rightNodeValue_ = root.rightNode_.executeLong(frameValue);
}catch (UnexpectedResultException ex){
return SLTypesGen.expectLong(getNext().execute_(frameValue, leftNodeValue_, ex.getResult()));
}
try {
return root.add(leftNodeValue_, rightNodeValue_);
} catch (ArithmeticException ex) {
root.excludeAdd0_ = true;
return SLTypesGen.expectLong(remove("threw rewrite exception", frameValue, leftNodeValue_, rightNodeValue_));
}
}
@Override public Object execute(VirtualFrame frameValue) {
try {
return executeLong(frameValue);
} catch (UnexpectedResultException ex) {
return ex.getResult();
}
} //The original article does not have this closing brace
}
@TypeSystem({long.class, BigInteger.class, boolean.class, String.class, SLFunction.class, SLNull.class})
public abstract class SLTypes {
@ImplicitCast
public BigInteger castBigInteger(long value) {
return BigInteger.valueOf(value);
}
}
@TypeSystemReference(SLTypes.class)
public abstract class SLExpressionNode extends SLStatementNode {
public abstract Object executeGeneric(VirtualFrame frame);
public long executeLong(VirtualFrame frame) throws UnexpectedResultException {
return SLTypesGen.SLTYPES.expectLong(executeGeneric(frame));
}
public boolean executeBoolean(VirtualFrame frame) ...
}
UnexpectedResultException
But what to do when speculation was too optimistic? --You need to return a value with a more general type than the return value. --Returns the value "boxed" with UnexpectedResultException.
Exception handler performs node rewriting --There is no performance bottleneck as exceptions are thrown only once.
Abbreviation
--Is the function assigned to the prologue? (Allocated in the function prologue)
--Passed as a parameter to the execute () method? (Passed around as parameter to execute () methods)
--Mapping from identifiers (usually variable names) to typed slots? (A mapping from identifiers (usually variable names) to typed slots.) -Does every slot have a unique index to the frame object? (Every slot has a unique index into the frame object.) --Created and filled during parsing.
--Created for each function of your own (Guest) language called.
The Truffle API only exposes the frame interface. --Implementation class depends on the optimization system.
Virtual frame
--Automatically optimized by the compiler. --Do not assign to fields or leave the interpreted function.
--A frame that can be stored without restrictions? (A frame that can be stored without restrictions) --Example: Closure frame that needs to be passed to another function? (frame of a closure that needs to be passed to other function)
--Factory method of TruffleRuntime class
public interface Frame {
FrameDescriptor getFrameDescriptor();
Object[] getArguments();
boolean isType(FrameSlot slot);
Type getType(FrameSlot slot) throws FrameSlotTypeException;
void setType(FrameSlot slot, Type value);
Object getValue(FrameSlot slot);
MaterializedFrame materialize();
}
Frames support all Java primitive types and objects.
SL String, SLFunction, SLNull are stored as Object.
Do not allocate frames or implement frames.
@NodeChild("valueNode")
@NodeField(name = "slot", type = FrameSlot.class)
public abstract class SLWriteLocalVariableNode extends SLExpressionNode {
protected abstract FrameSlot getSlot();
@Specialization(guards = "isLongOrIllegal(frame)")
protected long writeLong(VirtualFrame frame, long value) {
getSlot().setKind(FrameSlotKind.Long);
frame.setLong(getSlot(), value);
return value;
}
protected boolean isLongOrIllegal(VirtualFrame frame) {
return getSlot().getKind() == FrameSlotKind.Long || getSlot().getKind() == FrameSlotKind.Illegal;
}
...
@Specialization(contains = {"writeLong", " writeBoolean"})
protected Object write(VirtualFrame frame, Object value) {
getSlot().setKind(FrameSlotKind.Object);
frame.setObject(getSlot(), value);
return value;
}
}
@NodeField(name = "slot", type = FrameSlot.class)
public abstract class SLReadLocalVariableNode extends SLExpressionNode {
protected abstract FrameSlot getSlot();
@Specialization(guards = "isLong(frame)")
protected long readLong(VirtualFrame frame) {
return FrameUtil.getLongSafe(frame, getSlot());
}
protected boolean isLong(VirtualFrame frame) {
return getSlot().getKind() == FrameSlotKind.Long;
}
...
@Specialization(contains = {"readLong", "readBoolean"})
protected Object readObject(VirtualFrame frame) {
if (!frame.isObject(getSlot())) {
CompilerDirectives.transferToInterpreter();
Object result = frame.getValue(getSlot());
frame.setObject(getSlot(), result);
return result;
}
return FrameUtil.getObjectSafe(frame, getSlot());
}
} //There is no closing brace in the original text
--It starts automatically depending on the number of times the function is executed.
--All @Child and @Children fields are treated like final.
--Send back "deoptimization" to the interpreter? (Transfer back to the interpreter: “Deoptimization”) --Isn't the complex logic for node rewriting part of the compiled code? (Complex logic for node rewriting not part of compiled code) -Indispensable for excellent peak performance? (Essential for excellent peak performance)
--Would you like to dispatch any more between nodes? (No more dispatch between nodes) --Would you like to allocate more VirtualFrames? (No more allocation of VirtualFrame objects) --No more exceptions for inter-node control flow? (No more exceptions for inter-node control flow)
Recommended Posts