Mercurial > hg > graal-compiler
changeset 11950:be0a33a631fa
Truffle: fix node rewrite issue that can occur when a currently executing node is replaced in a recursive call.
author | Andreas Woess <andreas.woess@jku.at> |
---|---|
date | Wed, 09 Oct 2013 22:21:49 +0200 |
parents | f2fbdf89a1a5 |
children | c0c616fe3588 |
files | graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java |
diffstat | 2 files changed, 47 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Wed Oct 09 20:03:43 2013 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Wed Oct 09 22:21:49 2013 +0200 @@ -39,6 +39,8 @@ private SourceSection sourceSection; + private Node replacedWith; + /** * Marks array fields that are children of this node. */ @@ -170,8 +172,8 @@ * @param reason a description of the reason for the replacement * @return the new node */ - @SuppressWarnings({"unchecked"}) public final <T extends Node> T replace(T newNode, String reason) { + CompilerDirectives.transferToInterpreter(); if (this.getParent() == null) { throw new IllegalStateException("This node cannot be replaced, because it does not yet have a parent."); } @@ -180,13 +182,31 @@ newNode.assignSourceSection(sourceSection); } onReplace(newNode, reason); - return (T) this.getParent().replaceChild(this, newNode); + if (NodeUtil.replaceChild(parent, this, newNode)) { + parent.adoptChild(newNode); + this.replacedWith = newNode; + } else if (replacedWith != null) { + replaceFailedHelper(newNode); + } else { + throw new IllegalStateException("Child not found in parent."); + } + return newNode; } - private <T extends Node> T replaceChild(T oldChild, T newChild) { - NodeUtil.replaceChild(this, oldChild, newChild); - adoptChild(newChild); - return newChild; + private void replaceFailedHelper(Node newNode) { + Node lastReplacedWith = replacedWith; + assert lastReplacedWith != this; + while (lastReplacedWith.replacedWith != null) { + lastReplacedWith = lastReplacedWith.replacedWith; + assert lastReplacedWith != this; + } + newNode.parent = parent; + newNode.replacedWith = lastReplacedWith; + for (Node child : newNode.getChildren()) { + if (child != null) { + child.parent = lastReplacedWith; + } + } } /**
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Wed Oct 09 20:03:43 2013 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Wed Oct 09 22:21:49 2013 +0200 @@ -347,14 +347,14 @@ return nodes; } - public static void replaceChild(Node parent, Node oldChild, Node newChild) { + public static boolean replaceChild(Node parent, Node oldChild, Node newChild) { NodeClass nodeClass = NodeClass.get(parent.getClass()); for (long fieldOffset : nodeClass.getChildOffsets()) { if (unsafe.getObject(parent, fieldOffset) == oldChild) { assert assertAssignable(nodeClass, fieldOffset, newChild); unsafe.putObject(parent, fieldOffset, newChild); - return; + return true; } } @@ -367,11 +367,12 @@ if (array[i] == oldChild) { assert assertAssignable(nodeClass, fieldOffset, newChild); array[i] = newChild; - return; + return true; } } } } + return false; } private static boolean assertAssignable(NodeClass clazz, long fieldOffset, Object newValue) { @@ -798,4 +799,21 @@ } return ""; } + + public static boolean verify(Node root) { + Iterable<Node> children = root.getChildren(); + for (Node child : children) { + if (child != null) { + if (child.getParent() != root) { + throw new AssertionError(toStringWithClass(child) + ": actual parent=" + toStringWithClass(child.getParent()) + " expected parent=" + toStringWithClass(root)); + } + verify(child); + } + } + return true; + } + + private static String toStringWithClass(Object obj) { + return obj == null ? "null" : obj + "(" + obj.getClass().getName() + ")"; + } }