Replacing nodes

This post will cover a powerful feature of Truffle, which allows one to replace a node dynamically with a different node. This is extremely useful if you have a node that will start in one state and rarely change. That node’s state should be a static field to allow the JIT to aggressively optimize it for performance. If that node needs to change, it can be replaced using the functionality below. Since replacements have a cost, it may be worthwhile to “cap” the number of replacements possible on a node, and when that cap is reached, replace it with a node whose state is stored in non-final fields. I will cover this in a later post.

package com.wordpress.hextruffle.tests;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleRuntime;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;

public class NodeReplacementDemo {
	static class IntAdderTestRootNode extends RootNode {

		@Children
		private final IntNode[] children;

		public IntAdderTestRootNode(IntNode[] children) {
			super(null);
			this.children = children;
		}

		@Override
		public Object execute(VirtualFrame frame) {
			int sum = 0;
			for (IntNode child : children) {
				sum += child.execute();
			}
			return sum;
		}
	}

	static abstract class IntNode extends Node {

		public IntNode() {
			super(null);
		}

		abstract int execute();
	}

	static class UnresolvedIntNode extends IntNode {

		private final String value;

		public UnresolvedIntNode(String value) {
			this.value = value;
		}

		@Override
		int execute() {
			int intValue = Integer.parseInt(value);
			ResolvedIntNode newNode = this.replace(new ResolvedIntNode(intValue));
			return newNode.execute();
		}
	}

	static class ResolvedIntNode extends IntNode {

		private final int value;

		ResolvedIntNode(int value) {
			this.value = value;
		}

		@Override
		int execute() {
			return value;
		}
	}

	public static void main(String[] args) {
		TruffleRuntime runtime = Truffle.getRuntime();
		UnresolvedIntNode left = new UnresolvedIntNode("1");
		UnresolvedIntNode right = new UnresolvedIntNode("2");
		IntAdderTestRootNode root = new IntAdderTestRootNode(new IntNode[]{left, right});
		root.getChildren().forEach((node) -> {
			System.out.println("Currently there is a child node with type "+node.getClass().getName());
		});
	        CallTarget target = runtime.createCallTarget(root);
	        System.out.println("Now we call the root node, which tells us that 1 + 2 = " + target.call());
	        root.getChildren().forEach((node) -> {
			System.out.println("Currently there is a child node with type "+node.getClass().getName());
		});
                System.out.println("Now we call the root node, which tells us that 1 + 2 = " + target.call());
	}
}

This code will first start with two unresolved nodes that have final string fields. When it is time to execute them, they will be resolved at that point (which may be useful for cases where you need to late-bind something in your guest language’s scope, for example from a lambda or when performing a polymorphic call that needs to be resolved once at that point). Once the resolution happens, any further calls to the target node will result in the resolved nodes being used.

As you can see when you run this, the two child nodes are first UnresolvedIntNodes and after the first call of the call target, they are found to be ResolvedIntNodes.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s