From 8f85a374988e5477334a8f8d0a36e55ed2c8f2cb Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 5 Oct 2024 16:41:22 +0100 Subject: [PATCH] Added supernodes for < and > comparsion of an expression or argument with a constant Signed-off-by: Stefan Marr --- .../src/trufflesom/compiler/ParserAst.java | 18 +++ .../compare/GreaterThanIntNode.java | 62 ++++++++++ .../supernodes/compare/LessThanIntNode.java | 62 ++++++++++ .../compare/LocalArgGreaterThanInt.java | 107 ++++++++++++++++++ .../compare/LocalArgLessThanInt.java | 106 +++++++++++++++++ .../supernodes/LesserGreaterThanTests.java | 48 ++++++++ 6 files changed, 403 insertions(+) create mode 100644 src/trufflesom/src/trufflesom/interpreter/supernodes/compare/GreaterThanIntNode.java create mode 100644 src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LessThanIntNode.java create mode 100644 src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgGreaterThanInt.java create mode 100644 src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgLessThanInt.java create mode 100644 tests/trufflesom/supernodes/LesserGreaterThanTests.java diff --git a/src/trufflesom/src/trufflesom/compiler/ParserAst.java b/src/trufflesom/src/trufflesom/compiler/ParserAst.java index d205e7f0..9e48a9c7 100644 --- a/src/trufflesom/src/trufflesom/compiler/ParserAst.java +++ b/src/trufflesom/src/trufflesom/compiler/ParserAst.java @@ -43,6 +43,10 @@ import trufflesom.interpreter.nodes.literals.GenericLiteralNode; import trufflesom.interpreter.nodes.literals.IntegerLiteralNode; import trufflesom.interpreter.nodes.literals.LiteralNode; +import trufflesom.interpreter.supernodes.compare.GreaterThanIntNodeGen; +import trufflesom.interpreter.supernodes.compare.LessThanIntNodeGen; +import trufflesom.interpreter.supernodes.compare.LocalArgGreaterThanInt; +import trufflesom.interpreter.supernodes.compare.LocalArgLessThanInt; import trufflesom.interpreter.supernodes.compare.LocalFieldStringEqualsNode; import trufflesom.interpreter.supernodes.LocalVariableSquareNodeGen; import trufflesom.interpreter.supernodes.compare.NonLocalFieldStringEqualsNode; @@ -329,6 +333,20 @@ protected ExpressionNode binaryMessage(final MethodGenerationContext mgenc, } else if (msg == SymbolTable.symMinus && operand instanceof IntegerLiteralNode lit) { long litValue = lit.executeLong(null); return IncExpWithValueNodeGen.create(-litValue, true, receiver).initialize(coordWithL); + } else if (binSelector.equals(">") && operand instanceof IntegerLiteralNode lit) { + long litValue = lit.executeLong(null); + + if (receiver instanceof LocalArgumentReadNode arg) { + return new LocalArgGreaterThanInt(arg.getArg(), litValue).initialize(coordWithL); + } + return GreaterThanIntNodeGen.create(litValue, receiver).initialize(coordWithL); + } else if (binSelector.equals("<") && operand instanceof IntegerLiteralNode lit) { + long litValue = lit.executeLong(null); + + if (receiver instanceof LocalArgumentReadNode arg) { + return new LocalArgLessThanInt(arg.getArg(), litValue).initialize(coordWithL); + } + return LessThanIntNodeGen.create(litValue, receiver).initialize(coordWithL); } ExpressionNode inlined = diff --git a/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/GreaterThanIntNode.java b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/GreaterThanIntNode.java new file mode 100644 index 00000000..365c71c1 --- /dev/null +++ b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/GreaterThanIntNode.java @@ -0,0 +1,62 @@ +package trufflesom.interpreter.supernodes.compare; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import trufflesom.interpreter.bc.RespecializeException; +import trufflesom.interpreter.nodes.ExpressionNode; +import trufflesom.interpreter.nodes.GenericMessageSendNode; +import trufflesom.interpreter.nodes.MessageSendNode; +import trufflesom.interpreter.nodes.bc.BytecodeLoopNode; +import trufflesom.interpreter.nodes.literals.IntegerLiteralNode; +import trufflesom.interpreter.nodes.nary.UnaryExpressionNode; +import trufflesom.vm.SymbolTable; +import trufflesom.vm.VmSettings; +import trufflesom.vmobjects.SSymbol; + + +public abstract class GreaterThanIntNode extends UnaryExpressionNode { + private final long intValue; + + public GreaterThanIntNode(final long intValue) { + this.intValue = intValue; + } + + @Override + public abstract ExpressionNode getReceiver(); + + @Specialization + public final boolean doLong(final long rcvr) { + return rcvr > intValue; + } + + @Specialization + public final boolean doDouble(final double rcvr) { + return rcvr > intValue; + } + + @Fallback + public final Object makeGenericSend(final VirtualFrame frame, + final Object receiver) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + return makeGenericSend(SymbolTable.symbolFor(">")).doPreEvaluated(frame, + new Object[] {receiver, intValue}); + } + + @Override + protected GenericMessageSendNode makeGenericSend(final SSymbol selector) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + GenericMessageSendNode send = MessageSendNode.createGeneric(selector, + new ExpressionNode[] {getReceiver(), new IntegerLiteralNode(intValue)}, sourceCoord); + + if (VmSettings.UseAstInterp) { + replace(send); + send.notifyDispatchInserted(); + return send; + } + + assert getParent() instanceof BytecodeLoopNode : "This node was expected to be a direct child of a `BytecodeLoopNode`."; + throw new RespecializeException(send); + } +} diff --git a/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LessThanIntNode.java b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LessThanIntNode.java new file mode 100644 index 00000000..07ce26c6 --- /dev/null +++ b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LessThanIntNode.java @@ -0,0 +1,62 @@ +package trufflesom.interpreter.supernodes.compare; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import trufflesom.interpreter.bc.RespecializeException; +import trufflesom.interpreter.nodes.ExpressionNode; +import trufflesom.interpreter.nodes.GenericMessageSendNode; +import trufflesom.interpreter.nodes.MessageSendNode; +import trufflesom.interpreter.nodes.bc.BytecodeLoopNode; +import trufflesom.interpreter.nodes.literals.IntegerLiteralNode; +import trufflesom.interpreter.nodes.nary.UnaryExpressionNode; +import trufflesom.vm.SymbolTable; +import trufflesom.vm.VmSettings; +import trufflesom.vmobjects.SSymbol; + + +public abstract class LessThanIntNode extends UnaryExpressionNode { + private final long intValue; + + public LessThanIntNode(final long intValue) { + this.intValue = intValue; + } + + @Override + public abstract ExpressionNode getReceiver(); + + @Specialization + public final boolean doLong(final long rcvr) { + return rcvr < intValue; + } + + @Specialization + public final boolean doDouble(final double rcvr) { + return rcvr < intValue; + } + + @Fallback + public final Object makeGenericSend(final VirtualFrame frame, + final Object receiver) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + return makeGenericSend(SymbolTable.symbolFor("<")).doPreEvaluated(frame, + new Object[] {receiver, intValue}); + } + + @Override + protected GenericMessageSendNode makeGenericSend(final SSymbol selector) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + GenericMessageSendNode send = MessageSendNode.createGeneric(selector, + new ExpressionNode[] {getReceiver(), new IntegerLiteralNode(intValue)}, sourceCoord); + + if (VmSettings.UseAstInterp) { + replace(send); + send.notifyDispatchInserted(); + return send; + } + + assert getParent() instanceof BytecodeLoopNode : "This node was expected to be a direct child of a `BytecodeLoopNode`."; + throw new RespecializeException(send); + } +} diff --git a/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgGreaterThanInt.java b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgGreaterThanInt.java new file mode 100644 index 00000000..5f814edf --- /dev/null +++ b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgGreaterThanInt.java @@ -0,0 +1,107 @@ +package trufflesom.interpreter.supernodes.compare; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.UnexpectedResultException; +import trufflesom.bdt.inlining.ScopeAdaptationVisitor; +import trufflesom.bdt.inlining.ScopeAdaptationVisitor.ScopeElement; +import trufflesom.compiler.Variable.Argument; +import trufflesom.interpreter.bc.RespecializeException; +import trufflesom.interpreter.nodes.ArgumentReadNode.LocalArgumentReadNode; +import trufflesom.interpreter.nodes.ExpressionNode; +import trufflesom.interpreter.nodes.GenericMessageSendNode; +import trufflesom.interpreter.nodes.MessageSendNode; +import trufflesom.interpreter.nodes.bc.BytecodeLoopNode; +import trufflesom.interpreter.nodes.literals.IntegerLiteralNode; +import trufflesom.vm.SymbolTable; +import trufflesom.vm.VmSettings; + + +public final class LocalArgGreaterThanInt extends ExpressionNode { + private final Argument arg; + private final int argIdx; + private final long intValue; + + public LocalArgGreaterThanInt(final Argument arg, final long intValue) { + this.arg = arg; + this.argIdx = arg.index; + this.intValue = intValue; + } + + @Override + public Object executeGeneric(final VirtualFrame frame) { + Object arg = frame.getArguments()[argIdx]; + if (arg instanceof Long) { + long argVal = (Long) arg; + return argVal > intValue; + } + + CompilerDirectives.transferToInterpreterAndInvalidate(); + return fallbackGeneric(frame); + } + + @Override + public Object doPreEvaluated(final VirtualFrame frame, final Object[] args) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new UnsupportedOperationException(); + } + + @Override + public boolean executeBoolean(final VirtualFrame frame) throws UnexpectedResultException { + Object arg = frame.getArguments()[argIdx]; + if (arg instanceof Long) { + long argVal = (Long) arg; + return argVal > intValue; + } + + CompilerDirectives.transferToInterpreterAndInvalidate(); + return fallbackBool(frame); + } + + private boolean fallbackBool(final VirtualFrame frame) throws UnexpectedResultException { + Object result = makeGenericSend().doPreEvaluated(frame, + new Object[] {arg, intValue}); + if (result instanceof Boolean) { + return (Boolean) result; + } + throw new UnexpectedResultException(result); + } + + private Object fallbackGeneric(final VirtualFrame frame) { + return makeGenericSend().doPreEvaluated(frame, + new Object[] {arg, intValue}); + } + + protected GenericMessageSendNode makeGenericSend() { + CompilerDirectives.transferToInterpreterAndInvalidate(); + GenericMessageSendNode send = + MessageSendNode.createGeneric(SymbolTable.symbolFor(">"), new ExpressionNode[] { + new LocalArgumentReadNode(arg), new IntegerLiteralNode(intValue)}, sourceCoord); + + if (VmSettings.UseAstInterp) { + replace(send); + send.notifyDispatchInserted(); + return send; + } + + assert getParent() instanceof BytecodeLoopNode : "This node was expected to be a direct child of a `BytecodeLoopNode`."; + throw new RespecializeException(send); + } + + @Override + public void replaceAfterScopeChange(final ScopeAdaptationVisitor inliner) { + ScopeElement se = inliner.getAdaptedVar(arg); + if (se.var != arg || se.contextLevel < 0) { + if (se.var instanceof Argument arg) { + replace(new LocalArgGreaterThanInt(arg, intValue).initialize(sourceCoord)); + } else { + replace(MessageSendNode.createGeneric(SymbolTable.symbolFor(">"), + new ExpressionNode[] {se.var.getReadNode(se.contextLevel, sourceCoord), + new IntegerLiteralNode(intValue)}, + sourceCoord)); + } + } else { + assert 0 == se.contextLevel; + } + } +} diff --git a/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgLessThanInt.java b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgLessThanInt.java new file mode 100644 index 00000000..f28b9f0e --- /dev/null +++ b/src/trufflesom/src/trufflesom/interpreter/supernodes/compare/LocalArgLessThanInt.java @@ -0,0 +1,106 @@ +package trufflesom.interpreter.supernodes.compare; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.UnexpectedResultException; +import trufflesom.bdt.inlining.ScopeAdaptationVisitor; +import trufflesom.bdt.inlining.ScopeAdaptationVisitor.ScopeElement; +import trufflesom.compiler.Variable.Argument; +import trufflesom.interpreter.bc.RespecializeException; +import trufflesom.interpreter.nodes.ArgumentReadNode.LocalArgumentReadNode; +import trufflesom.interpreter.nodes.ExpressionNode; +import trufflesom.interpreter.nodes.GenericMessageSendNode; +import trufflesom.interpreter.nodes.MessageSendNode; +import trufflesom.interpreter.nodes.bc.BytecodeLoopNode; +import trufflesom.interpreter.nodes.literals.IntegerLiteralNode; +import trufflesom.vm.SymbolTable; +import trufflesom.vm.VmSettings; + + +public final class LocalArgLessThanInt extends ExpressionNode { + private final Argument arg; + private final int argIdx; + private final long intValue; + + public LocalArgLessThanInt(final Argument arg, final long intValue) { + this.arg = arg; + this.argIdx = arg.index; + this.intValue = intValue; + } + + @Override + public Object executeGeneric(final VirtualFrame frame) { + Object arg = frame.getArguments()[argIdx]; + if (arg instanceof Long) { + long argVal = (Long) arg; + return argVal < intValue; + } + + CompilerDirectives.transferToInterpreterAndInvalidate(); + return fallbackGeneric(frame); + } + + @Override + public Object doPreEvaluated(final VirtualFrame frame, final Object[] args) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new UnsupportedOperationException(); + } + + @Override + public boolean executeBoolean(final VirtualFrame frame) throws UnexpectedResultException { + Object arg = frame.getArguments()[argIdx]; + if (arg instanceof Long) { + long argVal = (Long) arg; + return argVal < intValue; + } + + CompilerDirectives.transferToInterpreterAndInvalidate(); + return fallbackBool(frame); + } + + private boolean fallbackBool(final VirtualFrame frame) throws UnexpectedResultException { + Object result = makeGenericSend().doPreEvaluated(frame, + new Object[] {arg, intValue}); + if (result instanceof Boolean) { + return (Boolean) result; + } + throw new UnexpectedResultException(result); + } + + private Object fallbackGeneric(final VirtualFrame frame) { + return makeGenericSend().doPreEvaluated(frame, new Object[] {arg, intValue}); + } + + protected GenericMessageSendNode makeGenericSend() { + CompilerDirectives.transferToInterpreterAndInvalidate(); + GenericMessageSendNode send = + MessageSendNode.createGeneric(SymbolTable.symbolFor("<"), new ExpressionNode[] { + new LocalArgumentReadNode(arg), new IntegerLiteralNode(intValue)}, sourceCoord); + + if (VmSettings.UseAstInterp) { + replace(send); + send.notifyDispatchInserted(); + return send; + } + + assert getParent() instanceof BytecodeLoopNode : "This node was expected to be a direct child of a `BytecodeLoopNode`."; + throw new RespecializeException(send); + } + + @Override + public void replaceAfterScopeChange(final ScopeAdaptationVisitor inliner) { + ScopeElement se = inliner.getAdaptedVar(arg); + if (se.var != arg || se.contextLevel < 0) { + if (se.var instanceof Argument a) { + replace(new LocalArgLessThanInt(a, intValue).initialize(sourceCoord)); + } else { + replace(MessageSendNode.createGeneric(SymbolTable.symbolFor("<"), + new ExpressionNode[] {se.var.getReadNode(se.contextLevel, sourceCoord), + new IntegerLiteralNode(intValue)}, + sourceCoord)); + } + } else { + assert 0 == se.contextLevel; + } + } +} diff --git a/tests/trufflesom/supernodes/LesserGreaterThanTests.java b/tests/trufflesom/supernodes/LesserGreaterThanTests.java new file mode 100644 index 00000000..4d2e49a8 --- /dev/null +++ b/tests/trufflesom/supernodes/LesserGreaterThanTests.java @@ -0,0 +1,48 @@ +package trufflesom.supernodes; + +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.Test; +import trufflesom.interpreter.nodes.ExpressionNode; +import trufflesom.interpreter.nodes.SequenceNode; +import trufflesom.interpreter.supernodes.compare.GreaterThanIntNode; +import trufflesom.interpreter.supernodes.compare.LessThanIntNode; +import trufflesom.interpreter.supernodes.compare.LocalArgGreaterThanInt; +import trufflesom.interpreter.supernodes.compare.LocalArgLessThanInt; +import trufflesom.tests.AstTestSetup; + + +public class LesserGreaterThanTests extends AstTestSetup { + @SuppressWarnings("unchecked") + private T assertThatMainNodeIs(final String test, final Class expectedNode) { + SequenceNode seq = (SequenceNode) parseMethod( + "test: arg = ( | l1 l2 l3 l4 | \n" + test + " )"); + + ExpressionNode testExpr = read(seq, "expressions", 0); + assertThat(testExpr, instanceOf(expectedNode)); + return (T) testExpr; + } + + @Test + public void testGreaterThan() { + assertThatMainNodeIs("(1 + 3) > 0", GreaterThanIntNode.class); + + assertThatMainNodeIs("l1 > 0", GreaterThanIntNode.class); + assertThatMainNodeIs("3 > 0", GreaterThanIntNode.class); + assertThatMainNodeIs("3 > 0", GreaterThanIntNode.class); + + assertThatMainNodeIs("arg > 0", LocalArgGreaterThanInt.class); + } + + @Test + public void testLesserThan() { + assertThatMainNodeIs("(1 + 3) < 0", LessThanIntNode.class); + + assertThatMainNodeIs("l1 < 0", LessThanIntNode.class); + assertThatMainNodeIs("3 < 0", LessThanIntNode.class); + assertThatMainNodeIs("3 < 0", LessThanIntNode.class); + + assertThatMainNodeIs("arg < 0", LocalArgLessThanInt.class); + } +}