Created
July 26, 2018 09:45
-
-
Save manuelleduc/feb7430d8c5dd17f54ced82fcbed9d05 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package fr.mleduc.simplelanguage.revisitor.revisitors; | |
import com.oracle.truffle.api.CompilerAsserts; | |
import com.oracle.truffle.api.frame.FrameSlot; | |
import com.oracle.truffle.api.frame.FrameSlotKind; | |
import com.oracle.truffle.api.frame.FrameUtil; | |
import com.oracle.truffle.api.frame.VirtualFrame; | |
import com.oracle.truffle.api.nodes.ExplodeLoop; | |
import com.oracle.truffle.api.profiles.BranchProfile; | |
import com.oracle.truffle.api.profiles.ConditionProfile; | |
import fr.mleduc.simplelanguage.revisitor.lang.FunDef; | |
import fr.mleduc.simplelanguage.revisitor.model.objects.SLNull; | |
import fr.mleduc.simplelanguage.revisitor.model.stmt.*; | |
import fr.mleduc.simplelanguage.revisitor.model.stmt.expr.*; | |
import fr.mleduc.simplelanguage.revisitor.model.stmt.expr.values.*; | |
import fr.mleduc.simplelanguage.revisitor.revisitor.SLRevisitor; | |
import fr.mleduc.simplelanguage.revisitor.revisitors.exceptions.SLBreakException; | |
import fr.mleduc.simplelanguage.revisitor.revisitors.exceptions.SLContinueException; | |
import fr.mleduc.simplelanguage.revisitor.revisitors.exceptions.SLReturnException; | |
import fr.mleduc.simplelanguage.revisitor.revisitors.semantics.*; | |
import java.math.BigInteger; | |
import java.util.Objects; | |
import static com.oracle.truffle.api.CompilerDirectives.*; | |
public interface ExecSLRevisitor extends SLRevisitor<AndT, BigIntValueT, BinaryT, BlockT, BoolValueT, BreakT, ContinueT, DivideT, | |
EqualT, ExprT, FunctionBodyT, FunctionLiteralT, GreaterT, GreaterEqualT, IfT, IntValueT, InvokeT, LongValueT, | |
LowerT, LowerEqualT, MinusT, MultiplyT, NotT, NotEqualT, OrT, PlusT, ReadArgumentT, | |
ReadLocalVariableT, ReturnT, StmtT, StringValueT, UnboxT, WhileT, WriteLocalVariableT> { | |
@Override | |
default IntValueT _intValue(IntValue it) { | |
return frame -> it.getValue(); | |
} | |
@Override | |
default ReturnT _return(Return it) { | |
return frame -> { | |
final Object result; | |
if (it.getExpr() != null) { | |
result = $(it.getExpr()).eval(frame); | |
} else { | |
result = SLNull.SINGLETON; | |
} | |
throw new SLReturnException(result); | |
}; | |
} | |
@Override | |
default GreaterEqualT _greaterEqual(GreaterEqual it) { | |
return frame -> { | |
final Object left = $(it.getLeft()).eval(frame); | |
final Object right = $(it.getRight()).eval(frame); | |
if (left instanceof Integer && right instanceof Integer) { | |
return (int) left >= (int) right; | |
} else if (left instanceof Long && right instanceof Long) { | |
return (long) left >= (long) right; | |
} else if (left instanceof Long && right instanceof Integer) { | |
return (long) left >= (int) right; | |
} else if (left instanceof Integer && right instanceof Long) { | |
return (int) left >= (long) right; | |
} | |
throw new RuntimeException("GreaterEqual type error"); | |
}; | |
} | |
@Override | |
default StringValueT _stringValue(StringValue it) { | |
return frame -> it.getValue(); | |
} | |
@Override | |
default MinusT _minus(Minus it) { | |
return frame -> { | |
// TODO: probably slow | |
final Object left = $(it.getLeft()).eval(frame); | |
final Object right = $(it.getRight()).eval(frame); | |
if (left instanceof Integer && right instanceof Integer) { | |
return Math.subtractExact((int) left, (int) right); | |
} else if (left instanceof Long && right instanceof Long) { | |
return Math.subtractExact((long) left, (long) right); | |
} else if (left instanceof Long && right instanceof Integer) { | |
return Math.subtractExact((long) left, (int) right); | |
} else if (left instanceof Integer && right instanceof Long) { | |
return Math.subtractExact((int) left, (long) right); | |
} | |
throw new RuntimeException("Minus type error"); | |
}; | |
} | |
@Override | |
default BlockT _block(Block it) { | |
return new BlockT() { | |
@Override | |
@ExplodeLoop | |
public void execute(VirtualFrame frame) { | |
CompilerAsserts.compilationConstant(it.getStmts().length); | |
for (Stmt stmt : it.getStatements()) { | |
final StmtT $ = $(stmt); | |
$.execute(frame); | |
} | |
} | |
}; | |
} | |
@Override | |
default NotEqualT _notEqual(NotEqual it) { | |
return frame -> { | |
// TODO: probably slow | |
final Object left = $(it.getLeft()).eval(frame); | |
final Object right = $(it.getRight()).eval(frame); | |
return !Objects.equals(left, right); | |
}; | |
} | |
@Override | |
default IfT _if(If it) { | |
return new IfT() { | |
private final ConditionProfile condition = ConditionProfile.createCountingProfile(); | |
@Override | |
public void execute(VirtualFrame frame) { | |
final boolean cond = evaluateCondition(frame); | |
if (condition.profile(cond)) { | |
final StmtT $ = $(it.getThenBranch()); | |
$.execute(frame); | |
} else if (it.getElseBranch() != null) { | |
final StmtT $ = $(it.getElseBranch()); | |
$.execute(frame); | |
} | |
} | |
private boolean evaluateCondition(VirtualFrame frame) { | |
final ExprT $ = $(it.getCond()); | |
final Object eval = $.eval(frame); | |
if (eval instanceof Boolean) | |
return (boolean) eval; | |
else throw new RuntimeException("If type error, boolean expected"); | |
} | |
}; | |
} | |
@Override | |
default LowerT _lower(Lower it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
if (left instanceof Integer && right instanceof Integer) { | |
return (int) left < (int) right; | |
} else if (left instanceof Long && right instanceof Long) { | |
return (long) left < (long) right; | |
} else if (left instanceof Long && right instanceof Integer) { | |
return (long) left < (int) right; | |
} else if (left instanceof Integer && right instanceof Long) { | |
return (int) left < (long) right; | |
} | |
throw new RuntimeException("Lower type error"); | |
}; | |
} | |
@Override | |
default LowerEqualT _lowerEqual(LowerEqual it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
if (left instanceof Integer && right instanceof Integer) { | |
return (int) left <= (int) right; | |
} else if (left instanceof Long && (right instanceof Long)) { | |
return (long) left <= (long) right; | |
} else if ((left instanceof Long && right instanceof Integer)) { | |
return (long) left <= (int) right; | |
} else if ((left instanceof Integer && right instanceof Long)) { | |
return (int) left <= (long) right; | |
} | |
throw new RuntimeException("LowerEqual type error"); | |
}; | |
} | |
@Override | |
default MultiplyT _multiply(Multiply it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
try { | |
if (left instanceof Integer && right instanceof Integer) { | |
return Math.multiplyExact((int) left, (int) right); | |
} else if (left instanceof Long && right instanceof Long) { | |
return Math.multiplyExact((long) left, (long) right); | |
} else if (left instanceof Long && right instanceof Integer) { | |
return Math.multiplyExact((long) left, (int) right); | |
} else if (left instanceof Integer && right instanceof Long) { | |
return Math.multiplyExact((int) left, (long) right); | |
} else if (left instanceof BigInteger || right instanceof BigInteger) { | |
return CastUtil.toBigInteger(left).multiply(CastUtil.toBigInteger(right)); | |
} | |
} catch (ArithmeticException e) { | |
if (left instanceof Integer && right instanceof Integer) { | |
return BigInteger.valueOf((Integer) left).multiply(BigInteger.valueOf((Integer) right)); | |
} else if (left instanceof Long && right instanceof Long) { | |
return BigInteger.valueOf((Long) left).multiply(BigInteger.valueOf((Long) right)); | |
} else if (left instanceof Long) { | |
return BigInteger.valueOf((Long) left).multiply(BigInteger.valueOf((Integer) right)); | |
} else { | |
return BigInteger.valueOf((Integer) left).multiply(BigInteger.valueOf((Long) right)); | |
} | |
} | |
throw new RuntimeException("Multiply type error"); | |
}; | |
} | |
@Override | |
default AndT _and(And it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
if (left instanceof Boolean && right instanceof Boolean) { | |
return (boolean) left && (boolean) right; | |
} | |
throw new RuntimeException("And type error"); | |
}; | |
} | |
@Override | |
default DivideT _divide(Divide it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
if (left instanceof Integer && right instanceof Integer) { | |
return (int) ((int) left / (int) right); | |
} else if (left instanceof Long && right instanceof Long) { | |
return (long) ((long) left / (long) right); | |
} else if (left instanceof Long && right instanceof Integer) { | |
return (long) ((long) left / (int) right); | |
} else if (left instanceof Integer && right instanceof Long) { | |
return (long) ((int) left / (long) right); | |
} | |
throw new RuntimeException("Divide type error"); | |
}; | |
} | |
@Override | |
default EqualT _equal(Equal it) { | |
return frame -> { | |
// TODO: probably slow | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
return Objects.equals(left, right); | |
}; | |
} | |
@Override | |
default GreaterT _greater(Greater it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
if (left instanceof Integer && right instanceof Integer) { | |
return (Integer) left > (Integer) right; | |
} else if (left instanceof Long && right instanceof Long) { | |
return (long) left > (long) right; | |
} else if (left instanceof Long && right instanceof Integer) { | |
return (long) left > (int) right; | |
} else if (left instanceof Integer && right instanceof Long) { | |
return (int) left > (long) right; | |
} | |
throw new RuntimeException("LowerEqual type error"); | |
}; | |
} | |
@Override | |
default BoolValueT _boolValue(BoolValue it) { | |
return frame -> it.getValue(); | |
} | |
@Override | |
default ContinueT _continue(Continue it) { | |
return frame -> { | |
throw SLContinueException.SINGLETON; | |
}; | |
} | |
@Override | |
default WhileT _while(While it) { | |
return new WhileT() { | |
@Override | |
public void execute(VirtualFrame frame) { | |
try { | |
while (evaluateCondition(frame)) { | |
/* Normal exit of the loop when loop condition is false. */ | |
try { | |
final StmtT $ = $(it.getBody()); | |
$.execute(frame); | |
} catch (SLContinueException ex) { | |
} | |
} | |
} catch (SLBreakException e) { | |
} | |
} | |
private boolean evaluateCondition(VirtualFrame frame) { | |
final ExprT $ = $(it.getCond()); | |
final Object eval = $.eval(frame); | |
if (eval instanceof Boolean) | |
return (boolean) eval; | |
else throw new RuntimeException("If type error, boolean expected"); | |
} | |
}; | |
} | |
@Override | |
default OrT _or(Or it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
if (left instanceof Boolean && right instanceof Boolean) { | |
return (boolean) left || (boolean) right; | |
} | |
throw new RuntimeException("Or type error"); | |
}; | |
} | |
@Override | |
default BreakT _break(Break it) { | |
return frame -> { | |
throw new RuntimeException("Not implemeneted"); | |
}; | |
} | |
@Override | |
default PlusT _plus(Plus it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object left = $.eval(frame); | |
final ExprT $1 = $(it.getRight()); | |
final Object right = $1.eval(frame); | |
try { | |
if (left instanceof Integer && right instanceof Integer) { | |
return Math.addExact((int) left, (int) right); | |
} else if (left instanceof Long && right instanceof Long) { | |
return Math.addExact((long) left, (long) right); | |
} else if (left instanceof Long && right instanceof Integer) { | |
return Math.addExact((long) left, (int) right); | |
} else if (left instanceof Integer && right instanceof Long) { | |
return Math.addExact((int) left, (long) right); | |
} else if (left instanceof BigInteger || right instanceof BigInteger) { | |
return CastUtil.toBigInteger(left).add(CastUtil.toBigInteger(right)); | |
} | |
} catch (ArithmeticException e) { | |
if (left instanceof Integer && right instanceof Integer) { | |
return BigInteger.valueOf((Integer) left).add(BigInteger.valueOf((Integer) right)); | |
} else if (left instanceof Long && right instanceof Long) { | |
return BigInteger.valueOf((Long) left).add(BigInteger.valueOf((Long) right)); | |
} else if (left instanceof Long) { | |
return BigInteger.valueOf((Long) left).add(BigInteger.valueOf((Integer) right)); | |
} else { | |
return BigInteger.valueOf((Integer) left).add(BigInteger.valueOf((Long) right)); | |
} | |
} | |
throw new RuntimeException("Plus type error"); | |
}; | |
} | |
@Override | |
default ReadArgumentT _readArgument(ReadArgument it) { | |
return new ReadArgumentT() { | |
private final BranchProfile outOfBoundsTaken = BranchProfile.create(); | |
@Override | |
public Object eval(VirtualFrame frame) { | |
final Object[] args = frame.getArguments(); | |
final int index = it.getIndex(); | |
if (index < args.length) { | |
return args[index]; | |
} else { | |
/* In the interpreter, record profiling information that the branch was used. */ | |
outOfBoundsTaken.enter(); | |
/* Use the default null value. */ | |
return SLNull.SINGLETON; | |
} | |
} | |
}; | |
} | |
@Override | |
default UnboxT _unbox(Unbox it) { | |
return frame -> { | |
final ExprT $ = $(it.getLeft()); | |
final Object val = $.eval(frame); | |
// TODO: do a real unboxing... | |
if (val instanceof Integer || val instanceof Long || val instanceof Boolean) { | |
return val; | |
} else { | |
return val; | |
} | |
}; | |
} | |
@Override | |
default FunctionLiteralT _functionLiteral(FunctionLiteral it) { | |
return new FunctionLiteralT() { | |
@CompilationFinal | |
private FunDef cachedFunction; | |
@Override | |
public Object eval(VirtualFrame frame) { | |
if (cachedFunction == null) { | |
/* We are about to change a @CompilationFinal field. */ | |
transferToInterpreterAndInvalidate(); | |
/* First execution of the node: lookup the function in the function registry. */ | |
cachedFunction = it.getReference().get().getFunctionRegistry().lookup(it.getFunctionName(), true); | |
} | |
return cachedFunction; | |
} | |
}; | |
} | |
@Override | |
default WriteLocalVariableT _writeLocalVariable(WriteLocalVariable it) { | |
return frame -> { | |
final ExprT $ = $(it.getValue()); | |
final Object value = $.eval(frame); | |
final FrameSlot slot = it.getSlot(); | |
if (value instanceof String) { | |
slot.setKind(FrameSlotKind.Object); | |
frame.setObject(slot, value); | |
} else if (value instanceof Integer) { | |
slot.setKind(FrameSlotKind.Int); | |
frame.setInt(slot, (Integer) value); | |
} else if (value instanceof Long) { | |
slot.setKind(FrameSlotKind.Long); | |
frame.setLong(slot, (Long) value); | |
} else if (value instanceof Boolean) { | |
slot.setKind(FrameSlotKind.Boolean); | |
frame.setBoolean(slot, (Boolean) value); | |
} else if (value instanceof BigInteger) { | |
slot.setKind(FrameSlotKind.Object); | |
frame.setObject(slot, value); | |
} | |
return value; | |
}; | |
} | |
@Override | |
default InvokeT _invoke(Invoke it) { | |
return frame -> { | |
final ExprT $ = $(it.getFunctionNode()); | |
final Object function = $.eval(frame); | |
/* | |
* The number of arguments is constant for one invoke node. During compilation, the loop is | |
* unrolled and the execute methods of all arguments are inlined. This is triggered by the | |
* ExplodeLoop annotation on the method. The compiler assertion below illustrates that the | |
* array length is really constant. | |
*/ | |
final Expr[] argumentNodes = it.getArgumentNodes(); | |
CompilerAsserts.compilationConstant(argumentNodes.length); | |
final Object[] argumentValues = new Object[argumentNodes.length]; | |
for (int i = 0; i < argumentNodes.length; i++) { | |
argumentValues[i] = $(argumentNodes[i]).eval(frame); | |
} | |
return it.getDispatchNode().executeDispatch(function, argumentValues); | |
}; | |
} | |
@Override | |
default ReadLocalVariableT _readLocalVariable(ReadLocalVariable it) { | |
return frame -> { | |
if (!frame.isObject(it.getSlot())) { | |
/* | |
* The FrameSlotKind has been set to Object, so from now on all writes to the local | |
* variable will be Object writes. However, now we are in a frame that still has an old | |
* non-Object value. This is a slow-path operation: we read the non-Object value, and | |
* write it immediately as an Object value so that we do not hit this path again | |
* multiple times for the same variable of the same frame. | |
*/ | |
transferToInterpreter(); | |
final Object result = frame.getValue(it.getSlot()); | |
frame.setObject(it.getSlot(), result); | |
return result; | |
} | |
final Object objectSafe = FrameUtil.getObjectSafe(frame, it.getSlot()); | |
if (objectSafe == null) throw new RuntimeException("variable is null"); | |
return objectSafe; | |
}; | |
} | |
@Override | |
default FunctionBodyT _functionBody(FunctionBody it) { | |
return new FunctionBodyT() { | |
private final BranchProfile exceptionTaken = BranchProfile.create(); | |
private final BranchProfile nullTaken = BranchProfile.create(); | |
@Override | |
public Object eval(VirtualFrame frame) { | |
try { | |
/* Execute the function body. */ | |
Stmt bodyNode = it.getBodyNode(); | |
// if (!Objects.equals(bodyNode.getClass(), fr.mleduc.simplelanguage.revisitor.model.stmt.Block.class)) | |
// System.out.println(bodyNode.getClass()); | |
final StmtT $ = $(bodyNode); | |
// System.out.println($); | |
// System.out.println(frame.getClass()); | |
$.execute(frame); | |
} catch (SLReturnException ex) { | |
/* | |
* In the interpreter, record profiling information that the function has an explicit | |
* return. | |
*/ | |
exceptionTaken.enter(); | |
/* The exception transports the actual return value. */ | |
return ex.getResult(); | |
} | |
/* | |
* In the interpreter, record profiling information that the function ends without an | |
* explicit return. | |
*/ | |
nullTaken.enter(); | |
/* Return the default null value. */ | |
return SLNull.SINGLETON; | |
} | |
}; | |
} | |
@Override | |
default NotT _not(Not it) { | |
return frame -> { | |
final ExprT $ = $(it.getChild()); | |
final Object val = $.eval(frame); | |
if (val instanceof Boolean) return !(boolean) val; | |
throw new RuntimeException("Type error Not"); | |
}; | |
} | |
@Override | |
default BigIntValueT _bigIntValue(BigIntValue it) { | |
return frame -> it.getValue(); | |
} | |
@Override | |
default LongValueT _longValue(LongValue it) { | |
return frame -> it.getValue(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment