Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline Execution #8148

Merged
merged 24 commits into from
Nov 13, 2023
Merged

Inline Execution #8148

merged 24 commits into from
Nov 13, 2023

Conversation

4e6
Copy link
Contributor

@4e6 4e6 commented Oct 25, 2023

Pull Request Description

close #8132

Update the executionContext/executeExpression request to execute expressions in the local scope.

Important Notes

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • All code follows the
    Scala,
    Java,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • All code has been tested:
    • Unit tests have been written where possible.

@4e6 4e6 added the CI: No changelog needed Do not require a changelog entry for this PR. label Oct 25, 2023
@4e6 4e6 self-assigned this Oct 25, 2023
Copy link
Member

@JaroslavTulach JaroslavTulach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Marcin, this is what we should do...

@4e6 4e6 force-pushed the wip/db/8132-inline-execution branch from fc5a0c3 to 1bba491 Compare November 1, 2023 11:40
@4e6 4e6 force-pushed the wip/db/8132-inline-execution branch from 1bba491 to a77971e Compare November 3, 2023 17:07
val compilerOutput = runCompilerPhasesInline(ir, newContext)
runErrorHandlingInline(compilerOutput, source, newContext)
Some((newContext, compilerOutput, source))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing flatMap and then Some is strange. map is better.

Copy link
Collaborator

@hubertp hubertp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current state looks OK to me - the changes seems mostly cosmetic. I will leave the final word to @JaroslavTulach

idExecutionInstrument.map(
service ->
service.bind(
module, call.getFunction().getCallTarget(), callbacks, this.timer));
Copy link
Member

@JaroslavTulach JaroslavTulach Nov 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@4e6 spent quite a long time to explain me that there are multiple visualizations hidden behind this single bind. With such information it is clear that my original API design proposal cannot work. Here is the modified one:

diff --git engine/polyglot-api/src/main/java/org/enso/polyglot/debugger/IdExecutionService.java engine/polyglot-api/src/main/java/org/enso/polyglot/debugger/IdExecutionService.java
index fc8e712796..eac7025a08 100644
--- engine/polyglot-api/src/main/java/org/enso/polyglot/debugger/IdExecutionService.java
+++ engine/polyglot-api/src/main/java/org/enso/polyglot/debugger/IdExecutionService.java
@@ -9,37 +9,56 @@ import java.util.UUID;
 public interface IdExecutionService {
   String INSTRUMENT_ID = "id-value-extractor";
 
+  public abstract class Info {
+    protected Info() {
+      assert getClass().getName().equals("org.enso....InfoImpl");
+    }
+
+    /** @return UUID of the node, never {@code null} */
+    public abstract UUID getUuid();
+    /** @return associated result or {@code null} if there is no associated result */
+    public abstract Object getResult();
+
+    /** @return {@code true} when the result is panic, {@code false} otherwise */
+    public abstract boolean isPanic();
+
+    /** @return time (in nanoseconds) needed to compute the result or {@code -1} when not available */
+    public abstract long getElapsedTime();
+
+    /** Evaluates given code in the context of current UUID location.
+     * @param code the Enso code to evaluate
+     * @return result of the evaluation
+     */
+    public abstract Object eval(String code);
+  }
+
   public interface Callbacks {
 
     /**
      * Finds out previously computed result for given id. If a result is returned, then the
      * execution of given node is skipped and the value is returned back.
      *
-     * @param nodeId identification of the node to be computed
+     * @param info info with UUID the node to be computed
      * @return {@code null} should the execution of the node be performed; any other value to skip
      *     the execution and return the value as a result.
      */
-    Object findCachedResult(Object materializedFrame, Object node, UUID nodeId);
+    Object findCachedResult(Info info);
 
     /**
      * Notifies when an execution of a node is over.
      *
-     * @param nodeId expression node id that has been computed
-     * @param result the just computed result
-     * @param isPanic was the result a panic?
-     * @param nanoElapsedTime how long it took to compute the result?
+     * @param info info with node id, {@link Info#getResult()}, {@link Info#isPanic()} and {@link Info#getElapsedTime()}
      */
-    void updateCachedResult(UUID nodeId, Object result, boolean isPanic, long nanoElapsedTime);
+    void updateCachedResult(Info info);
 
     /**
      * Notification when a returned value is a function.
      *
-     * @param nodeId identification of the node to be computed
-     * @param result info about function call
+     * @param info with identification of the node and {@link Info#getResult() info about function call}
      * @return {@code null} should the execution of the node be performed; any other value to skip
      *     the execution and return the value as a result.
      */
-    Object onFunctionReturn(UUID nodeId, TruffleObject result);
+    Object onFunctionReturn(Info info);
   }
 
   /**

The new suggestion adds Info.eval(String) method which can be used by visualization to evaluate any code at given UUID location. Each visualization with associated expression can call this method with its own code to evaluate. The implementation inserts EvalNode into IdExecutionEventNode and uses it to evaluate the code.

There is a new Info class - an implementation of API vs. SPI pattern which is going to be implemented by IdExecutionInstrument and will have a reference to appropriate IdExecutionEventNode. Having an Info class there shall help us with future evolution of additional arguments - shall more of them be needed.

@4e6 4e6 marked this pull request as ready for review November 11, 2023 17:46
@4e6 4e6 requested a review from Akirathan as a code owner November 11, 2023 17:46
@4e6 4e6 requested a review from JaroslavTulach November 11, 2023 17:48
Copy link
Member

@JaroslavTulach JaroslavTulach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I am concerned about "speed" of eval at current state. Please measure.
  • I'd like to keep consistency of Meta.Instrumentor - add the same functionality there
  • otherwise OK, approving.

fun1 x = x.to_text
```

- You can execute an expression in the context of a function body. In this case,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- You can execute an expression in the context of a function body. In this case,
- You can execute an expression in the context of 'main' function body. In this case,

```

- You can execute an expression in the context of a function body. In this case,
the `expressionId` should point to the body of a function. E.g. in the context
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the `expressionId` should point to the body of a function. E.g. in the context
the `expressionId` should point to the body of the `main` function. Then the available symbols


- You can execute an expression in the context of a function body. In this case,
the `expressionId` should point to the body of a function. E.g. in the context
of `main` available symbols are `operator1`, `operator2` and `fun1`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
of `main` available symbols are `operator1`, `operator2` and `fun1`.
are `operator1`, `operator2` and `fun1`.

* @return {@code null} should the execution of the node be performed; any other value to skip
* the execution and return the value as a result.
*/
Object onFunctionReturn(UUID nodeId, TruffleObject result);
Object onFunctionReturn(Info info);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @4e6, for trying to use this design with Info.

Object visualizationResult = null;
Throwable visualizationError = null;
try {
visualizationResult = info.eval(oneshotExpression.expression());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the usage of Info.eval, OK.

@4e6 4e6 added the CI: Ready to merge This PR is eligible for automatic merge label Nov 13, 2023
@mergify mergify bot merged commit 565a858 into develop Nov 13, 2023
33 of 35 checks passed
@mergify mergify bot deleted the wip/db/8132-inline-execution branch November 13, 2023 16:05
@hubertp hubertp mentioned this pull request Nov 16, 2023
3 tasks
mergify bot pushed a commit that referenced this pull request Nov 17, 2023
Fixes a random crash (*) during instrumentation. Notice how `onTailCallReturn` calls `onReturnValue` with `null` frame.

Bonus: noticed that for some reason we weren't getting logs for `ExecutionService`. This turned out to be the problem with the logger name which by default was `[enso]` not
`[enso.org.enso.interpreter.service.ExecutionService`] and there is some logic there that normalizes the name and assumed a dot after `enso`. This change fixes the logic.

(*)
```
[enso.org.enso.interpreter.service.ExecutionService] Execution of function main failed (Cannot invoke "com.oracle.truffle.api.frame.VirtualFrame.materialize()" because "frame" is null).
java.lang.NullPointerException: Cannot invoke "com.oracle.truffle.api.frame.VirtualFrame.materialize()" because "frame" is null
at org.enso.interpreter.instrument.IdExecutionInstrument$IdEventNodeFactory$IdExecutionEventNode.onReturnValue(IdExecutionInstrument.java:246)
at org.enso.interpreter.instrument.IdExecutionInstrument$IdEventNodeFactory$IdExecutionEventNode.onTailCallReturn(IdExecutionInstrument.java:274)
at org.enso.interpreter.instrument.IdExecutionInstrument$IdEventNodeFactory$IdExecutionEventNode.onReturnExceptional(IdExecutionInstrument.java:258)
at org.graalvm.truffle/com.oracle.truffle.api.instrumentation.ProbeNode$EventProviderChainNode.innerOnReturnExceptional(ProbeNode.java:1395)
at org.graalvm.truffle/com.oracle.truffle.api.instrumentation.ProbeNode$EventChainNode.onReturnExceptional(ProbeNode.java:1031)
at org.graalvm.truffle/com.oracle.truffle.api.instrumentation.ProbeNode.onReturnExceptionalOrUnwind(ProbeNode.java:296)
at org.enso.interpreter.node.ExpressionNodeWrapper.executeGeneric(ExpressionNodeWrapper.java:119)
at org.enso.interpreter.node.ClosureRootNode.execute(ClosureRootNode.java:85)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:718)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:641)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callBoundary(OptimizedCallTarget.java:574)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.doInvoke(OptimizedCallTarget.java:558)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callDirect(OptimizedCallTarget.java:504)
at jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime.OptimizedDirectCallNode.call(OptimizedDirectCallNode.java:69)
at org.enso.interpreter.node.callable.thunk.ThunkExecutorNode.doCached(ThunkExecutorNode.java:69)
at org.enso.interpreter.node.callable.thunk.ThunkExecutorNodeGen.executeAndSpecialize(ThunkExecutorNodeGen.java:207)
at org.enso.interpreter.node.callable.thunk.ThunkExecutorNodeGen.executeThunk(ThunkExecutorNodeGen.java:167)
...
```

# Important Notes
Fixes regressions introduced in #8148 and #8162
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: No changelog needed Do not require a changelog entry for this PR. CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inline Execution
3 participants