Mercurial > hg > graal-jvmci-8
changeset 3089:05b8a7787aaf
merge
line wrap: on
line diff
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalOptions.java Mon Jun 27 17:15:12 2011 +0200 @@ -54,6 +54,10 @@ public static int MaximumDesiredSize = 8000; public static int MaximumShortLoopSize = 5; + // escape analysis settings + public static int ForcedInlineEscapeWeight = 0; + public static int MaximumEscapeAnalysisArrayLength = 32; + // debugging settings public static boolean VerifyPointerMaps = ____; public static int MethodEndBreakpointGuards = 0; @@ -101,6 +105,7 @@ public static boolean TraceAssembler = ____; public static boolean TraceInlining = ____; public static boolean TraceDeadCodeElimination = ____; + public static boolean TraceEscapeAnalysis = ____; public static boolean TraceMemoryMaps = ____; public static int TraceBytecodeParserLevel = 0; public static boolean QuietBailout = ____;
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java Mon Jun 27 17:15:12 2011 +0200 @@ -402,7 +402,7 @@ @Override public void visitNewObjectArray(NewObjectArray x) { XirArgument length = toXirArgument(x.length()); - XirSnippet snippet = xir.genNewArray(site(x), length, CiKind.Object, x.elementClass(), x.exactType()); + XirSnippet snippet = xir.genNewArray(site(x), length, CiKind.Object, x.elementType(), x.exactType()); emitXir(snippet, x, stateFor(x), null, true); } @@ -1607,7 +1607,9 @@ private void walkStateValue(Value value) { if (value != null) { - if (value instanceof Phi && !((Phi) value).isDead()) { + if (value instanceof VirtualObject) { + walkVirtualObject((VirtualObject) value); + } else if (value instanceof Phi && !((Phi) value).isDead()) { // phi's are special operandForPhi((Phi) value); } else if (value.operand().isIllegal()) { @@ -1618,6 +1620,13 @@ } } + private void walkVirtualObject(VirtualObject value) { + walkStateValue(value.input()); + if (value.object() != null) { + walkVirtualObject(value.object()); + } + } + protected LIRDebugInfo stateFor(Value x) { assert lastState != null : "must have state before instruction for " + x; return stateFor(x, lastState);
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graph/IR.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/graph/IR.java Mon Jun 27 17:15:12 2011 +0200 @@ -90,7 +90,7 @@ //printGraph("After DeadCodeElimination", compilation.graph); if (GraalOptions.Inline) { - new InliningPhase(compilation, this, GraalOptions.TraceInlining).apply(compilation.graph); + new InliningPhase(compilation, this, null, GraalOptions.TraceInlining).apply(compilation.graph); } Graph graph = compilation.graph; @@ -99,8 +99,11 @@ new CanonicalizerPhase().apply(graph); new DeadCodeEliminationPhase().apply(graph); } -// -// new EscapeAnalysisPhase().apply(graph); + + new EscapeAnalysisPhase(compilation, this).apply(graph); + new DeadCodeEliminationPhase().apply(graph); + new CanonicalizerPhase().apply(graph); + new DeadCodeEliminationPhase().apply(graph); if (GraalOptions.OptLoops) { new LoopPhase().apply(graph);
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/Invoke.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/Invoke.java Mon Jun 27 17:15:12 2011 +0200 @@ -84,7 +84,6 @@ public final RiMethod target; public final RiType returnType; public final int bci; // XXX needed because we can not compute the bci from the sateBefore bci of this Invoke was optimized from INVOKEINTERFACE to INVOKESPECIAL - public final RiTypeProfile profile; /** * Constructs a new Invoke instruction. @@ -95,13 +94,12 @@ * @param isStatic {@code true} if this call is static (no receiver object) * @param target the target method being called */ - public Invoke(int bci, int opcode, CiKind result, Value[] args, RiMethod target, RiType returnType, RiTypeProfile profile, Graph graph) { + public Invoke(int bci, int opcode, CiKind result, Value[] args, RiMethod target, RiType returnType, Graph graph) { super(result, args.length, SUCCESSOR_COUNT, graph); this.opcode = opcode; this.target = target; this.returnType = returnType; this.bci = bci; - this.profile = profile; this.argumentCount = args.length; for (int i = 0; i < args.length; i++) { @@ -148,10 +146,6 @@ return target; } - public RiTypeProfile profile() { - return profile; - } - /** * Checks whether this invocation has a receiver object. * @return {@code true} if this invocation has a receiver object; {@code false} otherwise, if this is a @@ -201,7 +195,7 @@ @Override public Node copy(Graph into) { - Invoke x = new Invoke(bci, opcode, kind, new Value[argumentCount], target, returnType, profile, into); + Invoke x = new Invoke(bci, opcode, kind, new Value[argumentCount], target, returnType, into); return x; } }
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewArray.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewArray.java Mon Jun 27 17:15:12 2011 +0200 @@ -22,8 +22,15 @@ */ package com.oracle.max.graal.compiler.ir; +import java.util.*; + +import com.oracle.max.graal.compiler.*; +import com.oracle.max.graal.compiler.phases.EscapeAnalysisPhase.EscapeField; +import com.oracle.max.graal.compiler.phases.EscapeAnalysisPhase.EscapeOp; +import com.oracle.max.graal.compiler.value.*; import com.oracle.max.graal.graph.*; import com.sun.cri.ci.*; +import com.sun.cri.ri.*; /** * The {@code NewArray} class is the base of all instructions that allocate arrays. @@ -67,4 +74,158 @@ super(CiKind.Object, inputCount + INPUT_COUNT, successorCount + SUCCESSOR_COUNT, graph); setLength(length); } + + /** + * The list of instructions which produce input for this instruction. + */ + public Value dimension(int index) { + assert index == 0; + return length(); + } + + /** + * The rank of the array allocated by this instruction, i.e. how many array dimensions. + */ + public int dimensionCount() { + return 1; + } + + public abstract CiKind elementKind(); + + @SuppressWarnings("unchecked") + @Override + public <T extends Op> T lookup(Class<T> clazz) { + if (clazz == EscapeOp.class) { + return (T) ESCAPE; + } + return super.lookup(clazz); + } + + private static class NewArrayEscapeOp implements EscapeOp { + + @Override + public boolean canAnalyze(Node node) { + NewArray x = (NewArray) node; + CiConstant length = x.dimension(0).asConstant(); + return length != null && length.asInt() >= 0 && length.asInt() < GraalOptions.MaximumEscapeAnalysisArrayLength; + } + + @Override + public boolean escape(Node node, Node usage) { + if (usage instanceof IsNonNull) { + IsNonNull x = (IsNonNull) usage; + assert x.object() == node; + return false; + } else if (usage instanceof IsType) { + IsType x = (IsType) usage; + assert x.object() == node; + return false; + } else if (usage instanceof FrameState) { + FrameState x = (FrameState) usage; + assert x.inputs().contains(node); + return true; + } else if (usage instanceof LoadIndexed) { + LoadIndexed x = (LoadIndexed) usage; + assert x.array() == node; + CiConstant index = x.index().asConstant(); + CiConstant length = ((NewArray) node).dimension(0).asConstant(); + if (index == null || length == null || index.asInt() < 0 || index.asInt() >= length.asInt()) { + return true; + } + return false; + } else if (usage instanceof StoreField) { + StoreField x = (StoreField) usage; + assert x.value() == node; + return true; + } else if (usage instanceof StoreIndexed) { + StoreIndexed x = (StoreIndexed) usage; + CiConstant index = x.index().asConstant(); + CiConstant length = ((NewArray) node).dimension(0).asConstant(); + if (index == null || length == null || index.asInt() < 0 || index.asInt() >= length.asInt()) { + return true; + } + return x.value() == node; + } else if (usage instanceof AccessMonitor) { + AccessMonitor x = (AccessMonitor) usage; + assert x.object() == node; + return false; + } else if (usage instanceof ArrayLength) { + ArrayLength x = (ArrayLength) usage; + assert x.array() == node; + return false; + } else if (usage instanceof VirtualObject) { + VirtualObject x = (VirtualObject) usage; + return false; + } else { + return true; + } + } + + @Override + public void beforeUpdate(Node node, Node usage) { + if (usage instanceof IsNonNull) { + IsNonNull x = (IsNonNull) usage; + if (x.usages().size() == 1 && x.usages().get(0) instanceof FixedGuard) { + FixedGuard guard = (FixedGuard) x.usages().get(0); + guard.replace(guard.next()); + } + x.delete(); + } else if (usage instanceof IsType) { + IsType x = (IsType) usage; + assert x.type() == ((NewArray) node).exactType(); + if (x.usages().size() == 1 && x.usages().get(0) instanceof FixedGuard) { + FixedGuard guard = (FixedGuard) x.usages().get(0); + guard.replace(guard.next()); + } + x.delete(); + } else if (usage instanceof AccessMonitor) { + AccessMonitor x = (AccessMonitor) usage; + x.replace(x.next()); + } else if (usage instanceof ArrayLength) { + ArrayLength x = (ArrayLength) usage; + x.replace(((NewArray) node).dimension(0)); + } + } + + @Override + public void collectField(Node node, Node usage, Map<Object, EscapeField> fields) { + if (usage instanceof AccessIndexed) { + AccessIndexed x = (AccessIndexed) usage; + CiConstant index = x.index().asConstant(); + CiConstant length = ((NewArray) node).dimension(0).asConstant(); + assert index != null && length != null && index.asInt() >= 0 && index.asInt() < length.asInt(); + + Integer representation = index.asInt(); + if (!fields.containsKey(representation)) { + fields.put(representation, new EscapeField("[" + representation + "]", representation, ((NewArray) node).elementKind())); + } + } + } + + @Override + public void updateState(Node node, Node current, Map<Object, EscapeField> fields, Map<EscapeField, Node> fieldState) { + if (current instanceof AccessIndexed) { + int index = ((AccessIndexed) current).index().asConstant().asInt(); + EscapeField field = fields.get(index); + if (current instanceof LoadIndexed) { + LoadIndexed x = (LoadIndexed) current; + if (x.array() == node) { + for (Node usage : new ArrayList<Node>(x.usages())) { + assert fieldState.get(field) != null; + usage.inputs().replace(x, fieldState.get(field)); + } + assert x.usages().size() == 0; + x.replace(x.next()); + } + } else if (current instanceof StoreIndexed) { + StoreIndexed x = (StoreIndexed) current; + if (x.array() == node) { + fieldState.put(field, x.value()); + assert x.usages().size() == 0; + x.replace(x.next()); + } + } + } + } + } }
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewInstance.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewInstance.java Mon Jun 27 17:15:12 2011 +0200 @@ -22,7 +22,12 @@ */ package com.oracle.max.graal.compiler.ir; +import java.util.*; + import com.oracle.max.graal.compiler.debug.*; +import com.oracle.max.graal.compiler.phases.EscapeAnalysisPhase.EscapeField; +import com.oracle.max.graal.compiler.phases.EscapeAnalysisPhase.EscapeOp; +import com.oracle.max.graal.compiler.value.*; import com.oracle.max.graal.graph.*; import com.sun.cri.ci.*; import com.sun.cri.ri.*; @@ -31,6 +36,7 @@ * The {@code NewInstance} instruction represents the allocation of an instance class object. */ public final class NewInstance extends FloatingNode { + private static final EscapeOp ESCAPE = new NewInstanceEscapeOp(); private static final int INPUT_COUNT = 0; private static final int SUCCESSOR_COUNT = 0; @@ -85,4 +91,124 @@ NewInstance x = new NewInstance(instanceClass, cpi, constantPool, into); return x; } + + @SuppressWarnings("unchecked") + @Override + public <T extends Op> T lookup(Class<T> clazz) { + if (clazz == EscapeOp.class) { + return (T) ESCAPE; + } + return super.lookup(clazz); + } + + private static class NewInstanceEscapeOp implements EscapeOp { + + @Override + public boolean canAnalyze(Node node) { + return ((NewInstance) node).instanceClass().isResolved(); + } + + @Override + public boolean escape(Node node, Node usage) { + if (usage instanceof IsNonNull) { + IsNonNull x = (IsNonNull) usage; + assert x.object() == node; + return false; + } else if (usage instanceof IsType) { + IsType x = (IsType) usage; + assert x.object() == node; + return false; + } else if (usage instanceof FrameState) { + FrameState x = (FrameState) usage; + assert x.inputs().contains(node); + return true; + } else if (usage instanceof LoadField) { + LoadField x = (LoadField) usage; + assert x.object() == node; + return false; + } else if (usage instanceof StoreField) { + StoreField x = (StoreField) usage; + return x.value() == node; + } else if (usage instanceof StoreIndexed) { + StoreIndexed x = (StoreIndexed) usage; + assert x.value() == node; + return true; + } else if (usage instanceof AccessMonitor) { + AccessMonitor x = (AccessMonitor) usage; + assert x.object() == node; + return false; + } else if (usage instanceof VirtualObject) { + VirtualObject x = (VirtualObject) usage; + return false; + } else if (usage instanceof RegisterFinalizer) { + RegisterFinalizer x = (RegisterFinalizer) usage; + assert x.object() == node; + return false; + } else { + return true; + } + } + + @Override + public void beforeUpdate(Node node, Node usage) { + if (usage instanceof IsNonNull) { + IsNonNull x = (IsNonNull) usage; + if (x.usages().size() == 1 && x.usages().get(0) instanceof FixedGuard) { + FixedGuard guard = (FixedGuard) x.usages().get(0); + guard.replace(guard.next()); + } + x.delete(); + } else if (usage instanceof IsType) { + IsType x = (IsType) usage; + assert x.type() == ((NewInstance) node).instanceClass(); + if (x.usages().size() == 1 && x.usages().get(0) instanceof FixedGuard) { + FixedGuard guard = (FixedGuard) x.usages().get(0); + guard.replace(guard.next()); + } + x.delete(); + } else if (usage instanceof AccessMonitor) { + AccessMonitor x = (AccessMonitor) usage; + x.replace(x.next()); + } else if (usage instanceof RegisterFinalizer) { + RegisterFinalizer x = (RegisterFinalizer) usage; + x.replace(x.next()); + } + } + + @Override + public void collectField(Node node, Node usage, Map<Object, EscapeField> fields) { + if (usage instanceof AccessField) { + AccessField x = (AccessField) usage; + assert x.object() == node; + if (!fields.containsKey(x.field())) { + fields.put(x.field(), new EscapeField(x.field().name(), x.field(), x.field.kind().stackKind())); + } + } + } + + @Override + public void updateState(Node node, Node current, Map<Object, EscapeField> fields, Map<EscapeField, Node> fieldState) { + if (current instanceof AccessField) { + EscapeField field = fields.get(((AccessField) current).field()); + if (current instanceof LoadField) { + LoadField x = (LoadField) current; + if (x.object() == node) { + for (Node usage : new ArrayList<Node>(x.usages())) { + assert fieldState.get(field) != null; + usage.inputs().replace(x, fieldState.get(field)); + } + assert x.usages().size() == 0; + x.replace(x.next()); + } + } else if (current instanceof StoreField) { + StoreField x = (StoreField) current; + if (x.object() == node) { + fieldState.put(field, x.value()); + assert x.usages().size() == 0; + x.replace(x.next()); + } + } + } + } + } }
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewMultiArray.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewMultiArray.java Mon Jun 27 17:15:12 2011 +0200 @@ -50,6 +50,7 @@ /** * The list of instructions which produce input for this instruction. */ + @Override public Value dimension(int index) { assert index >= 0 && index < dimensionCount; return (Value) inputs().get(super.inputCount() + index); @@ -63,6 +64,7 @@ /** * The rank of the array allocated by this instruction, i.e. how many array dimensions. */ + @Override public int dimensionCount() { return dimensionCount; } @@ -105,6 +107,21 @@ } @Override + public CiKind elementKind() { + return elementType.kind(); + } + + @Override + public RiType exactType() { + return elementType.arrayOf(); + } + + @Override + public RiType declaredType() { + return exactType(); + } + + @Override public void print(LogStream out) { out.print("new multi array ["); for (int i = 0; i < dimensionCount; i++) {
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewObjectArray.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewObjectArray.java Mon Jun 27 17:15:12 2011 +0200 @@ -52,11 +52,16 @@ * Gets the type of the elements of the array. * @return the element type of the array */ - public RiType elementClass() { + public RiType elementType() { return elementClass; } @Override + public CiKind elementKind() { + return elementClass.kind(); + } + + @Override public RiType exactType() { return elementClass.arrayOf(); } @@ -73,7 +78,7 @@ @Override public void print(LogStream out) { - out.print("new object array [").print(length()).print("] ").print(CiUtil.toJavaName(elementClass())); + out.print("new object array [").print(length()).print("] ").print(CiUtil.toJavaName(elementType())); } @Override
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewTypeArray.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/NewTypeArray.java Mon Jun 27 17:15:12 2011 +0200 @@ -42,6 +42,7 @@ this.elementType = elementType; } + @Override public CiKind elementKind() { return elementType.kind(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/ir/VirtualObject.java Mon Jun 27 17:15:12 2011 +0200 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.compiler.ir; + +import java.util.*; + +import com.oracle.max.graal.compiler.debug.*; +import com.oracle.max.graal.compiler.phases.EscapeAnalysisPhase.EscapeField; +import com.oracle.max.graal.graph.*; +import com.sun.cri.ci.*; +import com.sun.cri.ri.*; + + +public class VirtualObject extends FloatingNode { + + private static final int INPUT_COUNT = 2; + private static final int INPUT_OBJECT = 0; + private static final int INPUT_INPUT = 1; + + private static final int SUCCESSOR_COUNT = 0; + + @Override + protected int inputCount() { + return super.inputCount() + INPUT_COUNT; + } + + @Override + protected int successorCount() { + return super.successorCount() + SUCCESSOR_COUNT; + } + + /** + * The instruction that specifies the old state of the virtual object. + */ + public VirtualObject object() { + return (VirtualObject) inputs().get(super.inputCount() + INPUT_OBJECT); + } + + public VirtualObject setObject(VirtualObject n) { + return (VirtualObject) inputs().set(super.inputCount() + INPUT_OBJECT, n); + } + + /** + * The instruction that contains the new state of the specified field. + */ + public Value input() { + return (Value) inputs().get(super.inputCount() + INPUT_INPUT); + } + + public Value setInput(Value n) { + return (Value) inputs().set(super.inputCount() + INPUT_INPUT, n); + } + + private EscapeField field; + private RiType type; + + /** + * Constructs a new ArrayLength instruction. + * @param array the instruction producing the array + * @param newFrameState the state after executing this instruction + */ + public VirtualObject(VirtualObject object, EscapeField field, RiType type, Graph graph) { + super(CiKind.Int, INPUT_COUNT, SUCCESSOR_COUNT, graph); + this.field = field; + this.type = type; + setObject(object); + } + + public EscapeField field() { + return field; + } + + public RiType type() { + return type; + } + + @Override + public void accept(ValueVisitor v) { + // nothing to do... + } + + @Override + public Map<Object, Object> getDebugProperties() { + Map<Object, Object> properties = super.getDebugProperties(); + properties.put("type", type); + properties.put("field", field); + return properties; + } + + @Override + public String shortName() { + return "VirtualObject " + field.name(); + } + + @Override + public void print(LogStream out) { + out.print(object()).print(".").print(field.name()).print("=").print(input()); + } + + @Override + public Node copy(Graph into) { + VirtualObject x = new VirtualObject(null, field, type, into); + return x; + } +}
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/DeadCodeEliminationPhase.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/DeadCodeEliminationPhase.java Mon Jun 27 17:15:12 2011 +0200 @@ -29,6 +29,7 @@ import com.oracle.max.graal.compiler.gen.*; import com.oracle.max.graal.compiler.ir.*; import com.oracle.max.graal.graph.*; +import com.sun.cri.ci.*; public class DeadCodeEliminationPhase extends Phase { @@ -53,20 +54,29 @@ } } // remove if nodes with constant-value comparison -// for (If ifNode : graph.getNodes(If.class)) { -// Compare compare = ifNode.compare(); -// if (compare.x().isConstant() && compare.y().isConstant()) { -// CiConstant constX = compare.x().asConstant(); -// CiConstant constY = compare.y().asConstant(); -// Boolean result = compare.condition().foldCondition(constX, constY, GraalCompilation.compilation().runtime); -// if (result != null) { -// Node actualSuccessor = result ? ifNode.trueSuccessor() : ifNode.falseSuccessor(); -// ifNode.replace(actualSuccessor); -// } else { -// TTY.println("if not removed %s %s %s (%s %s)", constX, compare.condition(), constY, constX.kind, constY.kind); -// } -// } -// } + for (If ifNode : graph.getNodes(If.class)) { + BooleanNode bool = ifNode.compare(); + if (bool instanceof Compare) { + Compare compare = (Compare) bool; + if (compare.x().isConstant() && compare.y().isConstant()) { + CiConstant constX = compare.x().asConstant(); + CiConstant constY = compare.y().asConstant(); + Boolean result = compare.condition().foldCondition(constX, constY, GraalCompilation.compilation().runtime); + if (result != null) { + Node actualSuccessor = result ? ifNode.trueSuccessor() : ifNode.falseSuccessor(); + ifNode.replace(actualSuccessor); + } else { + TTY.println("if not removed %s %s %s (%s %s)", constX, compare.condition(), constY, constX.kind, constY.kind); + } + } + } + } + // remove unnecessary FixedGuards + for (FixedGuard guard : graph.getNodes(FixedGuard.class)) { + if (guard.node() instanceof IsNonNull && ((IsNonNull) guard.node()).object() instanceof NewInstance) { + guard.replace(guard.next()); + } + } flood.add(graph.start());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/EscapeAnalysisPhase.java Mon Jun 27 17:15:12 2011 +0200 @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.max.graal.compiler.phases; + +import java.util.*; +import java.util.Map.Entry; + +import com.oracle.max.graal.compiler.*; +import com.oracle.max.graal.compiler.debug.*; +import com.oracle.max.graal.compiler.gen.*; +import com.oracle.max.graal.compiler.graph.*; +import com.oracle.max.graal.compiler.ir.*; +import com.oracle.max.graal.compiler.schedule.*; +import com.oracle.max.graal.compiler.value.*; +import com.oracle.max.graal.graph.*; +import com.sun.cri.ci.*; +import com.sun.cri.ri.*; + + +public class EscapeAnalysisPhase extends Phase { + + + public static class BlockExitState { + public final Map<EscapeField, Node> fieldState; + + public BlockExitState() { + this.fieldState = new HashMap<EscapeField, Node>(); + } + } + + public class EscapementFixup { + + private List<Block> blocks; + private final Map<Object, EscapeField> fields = new HashMap<Object, EscapeField>(); + private final Map<Block, BlockExitState> exitStates = new HashMap<Block, BlockExitState>(); + + private final EscapeOp op; + private Graph graph; + private final Node node; + + public EscapementFixup(EscapeOp op, Graph graph, Node node) { + this.op = op; + this.graph = graph; + this.node = node; + } + + public void apply() { + process(); + removeAllocation(); + } + + public void removeAllocation() { + final IdentifyBlocksPhase identifyBlocksPhase = new IdentifyBlocksPhase(true); + identifyBlocksPhase.apply(graph); + blocks = identifyBlocksPhase.getBlocks(); + + final HashMap<Phi, EscapeField> phis = new HashMap<Phi, EscapeField>(); + final Block startBlock = identifyBlocksPhase.getNodeToBlock().get(node); + assert startBlock != null; + final RiType type = ((Value) node).exactType(); + + Block.iteratePostOrder(blocks, new BlockClosure() { + + public void apply(Block block) { + if (GraalOptions.TraceEscapeAnalysis) { + TTY.println("Block %d", block.blockID()); + } +// for (Node n : block.getInstructions()) { +// TTY.println(" %d %s", n.id(), n.shortName()); +// } +// for (Block b : block.getSuccessors()) { +// TTY.print(" %d", b.blockID()); +// } +// TTY.println(); + + BlockExitState state = new BlockExitState(); + if (block == startBlock) { + for (EscapeField field : fields.values()) { + state.fieldState.put(field, Constant.defaultForKind(field.kind(), graph)); + } + } else { + List<Block> predecessors = block.getPredecessors(); + Set<EscapeField> mergedFields = new HashSet<EscapeField>(); + for (int i = 0; i < predecessors.size(); i++) { + BlockExitState exitState = exitStates.get(predecessors.get(i)); + if (exitState == null) { + mergedFields.addAll(fields.values()); + break; + } else { + for (EscapeField field : fields.values()) { + if (state.fieldState.get(field) == null) { + state.fieldState.put(field, exitState.fieldState.get(field)); + } else if (state.fieldState.get(field) != exitState.fieldState.get(field)) { + mergedFields.add(field); + } + } + } + } + if (!mergedFields.isEmpty()) { + assert block.firstNode() instanceof Merge : "unexpected: " + block.firstNode().shortName() + " " + block.firstNode().id(); + for (EscapeField field : mergedFields) { + Phi phi = new Phi(field.kind().stackKind(), (Merge) block.firstNode(), graph); + state.fieldState.put(field, phi); + phis.put(phi, field); + } + } + } + + Node current; + if (block.firstNode() instanceof StartNode) { + current = ((StartNode) block.firstNode()).start(); + } else { + current = block.firstNode(); + } + while (current != block.lastNode()) { + Node next = ((Instruction) current).next(); + op.updateState(node, current, fields, state.fieldState); + if (!current.isDeleted() && current instanceof Instruction) { + FrameState stateAfter = ((Instruction) current).stateAfter(); + if (stateAfter != null) { + updateFrameState(stateAfter, state, type, null); + } + } + current = next; + } + + if (GraalOptions.TraceEscapeAnalysis) { + TTY.print(" block end state: "); + for (Entry<EscapeField, Node> entry : state.fieldState.entrySet()) { + TTY.print("%s->%s ", entry.getKey().name(), entry.getValue()); + } + TTY.println(); + } + exitStates.put(block, state); + } + }, startBlock); + + for (Entry<Phi, EscapeField> entry : phis.entrySet()) { + Phi phi = entry.getKey(); + EscapeField field = entry.getValue(); + Block block = identifyBlocksPhase.getNodeToBlock().get(entry.getKey().merge()); + + List<Block> predecessors = block.getPredecessors(); + assert predecessors.size() > 0; + Node simple = exitStates.get(predecessors.get(0)).fieldState.get(field); + for (int i = 1; i < predecessors.size(); i++) { + BlockExitState exitState = exitStates.get(predecessors.get(i)); + if (exitState.fieldState.get(field) != simple) { + simple = null; + } + } + if (simple != null) { + for (Node usage : new ArrayList<Node>(phi.usages())) { + usage.inputs().replace(phi, simple); + } + phi.delete(); + } else { + for (int i = 0; i < predecessors.size(); i++) { + BlockExitState exitState = exitStates.get(predecessors.get(i)); + assert exitState != null; + Node value = exitState.fieldState.get(field); + if (GraalOptions.TraceEscapeAnalysis) { + TTY.println("fixing phi %d with %s", phi.id(), value); + } + if (value == null) { + phi.addInput(Constant.defaultForKind(field.kind(), graph)); + } else { + phi.addInput(value); + } + } + } + } + // the rest of the usages should be dead frame states... + for (Node usage : new ArrayList<Node>(node.usages())) { + assert usage instanceof FrameState || usage instanceof VirtualObject; + usage.inputs().replace(node, Node.Null); + } + + if (node instanceof Instruction) { + node.replace(((Instruction) node).next()); + } else { + node.delete(); + } + } + + private VirtualObject updateFrameState(FrameState frameState, BlockExitState currentState, RiType type, VirtualObject current) { + for (int i = 0; i < frameState.inputs().size(); i++) { + if (frameState.inputs().get(i) == node) { + if (current == null) { + for (EscapeField field : fields.values()) { + current = new VirtualObject(current, field, type, graph); + current.setInput((Value) currentState.fieldState.get(field)); + } + } + frameState.inputs().set(i, current); + } else if (frameState.inputs().get(i) instanceof VirtualObject) { + VirtualObject obj = (VirtualObject) frameState.inputs().get(i); + do { + current = updateVirtualObject(obj, currentState, type, current); + obj = obj.object(); + } while (obj != null); + } + } + if (frameState.outerFrameState() != null) { + current = updateFrameState(frameState.outerFrameState(), currentState, type, current); + } + return current; + } + + private VirtualObject updateVirtualObject(VirtualObject obj, BlockExitState currentState, RiType type, VirtualObject current) { + if (obj.input() == node) { + if (current == null) { + for (EscapeField field : fields.values()) { + current = new VirtualObject(current, field, type, graph); + current.setInput((Value) currentState.fieldState.get(field)); + } + } + obj.setInput(current); + } else if (obj.input() instanceof VirtualObject) { + VirtualObject obj2 = (VirtualObject) obj.input(); + do { + current = updateVirtualObject(obj2, currentState, type, current); + obj2 = obj2.object(); + } while (obj2 != null); + } + return current; + } + + private void process() { + for (Node usage : new ArrayList<Node>(node.usages())) { + op.beforeUpdate(node, usage); + if (!usage.isDeleted()) { + op.collectField(node, usage, fields); + } + } + } + } + + private final GraalCompilation compilation; + private final IR ir; + + public EscapeAnalysisPhase(GraalCompilation compilation, IR ir) { + this.compilation = compilation; + this.ir = ir; + } + + @Override + protected void run(Graph graph) { +// if (!compilation.method.holder().name().contains("jnt")) { +// return; +// } +// if (true) return; + for (Node node : graph.getNodes()) { + EscapeOp op = node.lookup(EscapeOp.class); + if (op != null && op.canAnalyze(node)) { + Set<Node> exits = new HashSet<Node>(); + Set<Invoke> invokes = new HashSet<Invoke>(); + int iterations = 0; + + int weight; + int minimumWeight = GraalOptions.ForcedInlineEscapeWeight; + do { + weight = analyze(op, node, exits, invokes); + if (exits.size() != 0) { + if (GraalOptions.TraceEscapeAnalysis) { + TTY.println("####### escaping object: %d %s in %s", node.id(), node.shortName(), compilation.method); + TTY.print("%d: new value: %d %s, weight %d, escapes at ", iterations, node.id(), node.shortName(), weight); + for (Node n : exits) { + TTY.print("%d %s, ", n.id(), n.shortName()); + } + for (Node n : invokes) { + TTY.print("%d %s, ", n.id(), n.shortName()); + } + TTY.println(); + } + break; + } + if (invokes.size() == 0) { + if (GraalOptions.TraceEscapeAnalysis) { + TTY.println("!!!!!!!! non-escaping object: %d %s in %s", node.id(), node.shortName(), compilation.method); + } + new EscapementFixup(op, graph, node).apply(); + new PhiSimplifier(graph); + break; + } + if (weight < minimumWeight) { + if (GraalOptions.TraceEscapeAnalysis) { + TTY.println("####### possibly escaping object: %d in %s (insufficient weight for inlining)", node.id(), compilation.method); + } + break; + } + new InliningPhase(compilation, ir, invokes, GraalOptions.TraceInlining).apply(graph); + exits.clear(); + invokes.clear(); + } while (iterations++ < 3); + } + } + } + + private int analyze(EscapeOp op, Node node, Collection<Node> exits, Collection<Invoke> invokes) { + int weight = 0; + for (Node usage : node.usages()) { + boolean escapes = op.escape(node, usage); + if (escapes) { + if (usage instanceof FrameState) { + // nothing to do... + } else if (usage instanceof Invoke) { + invokes.add((Invoke) usage); + } else { + exits.add(usage); + if (!GraalOptions.TraceEscapeAnalysis) { + break; + } + } + } else { + weight++; + } + } + return weight; + } + + public static class EscapeField { + + private String name; + private Object representation; + private CiKind kind; + + public EscapeField(String name, Object representation, CiKind kind) { + this.name = name; + this.representation = representation; + this.kind = kind; + } + + public String name() { + return name; + } + + public Object representation() { + return representation; + } + + public CiKind kind() { + return kind; + } + + @Override + public String toString() { + return name(); + } + } + + public static interface EscapeOp extends Op { + + boolean canAnalyze(Node node); + + boolean escape(Node node, Node usage); + + void beforeUpdate(Node node, Node usage); + + void collectField(Node node, Node usage, Map<Object, EscapeField> fields); + + void updateState(Node node, Node current, Map<Object, EscapeField> fields, Map<EscapeField, Node> fieldState); + + } +}
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/GraphBuilderPhase.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/GraphBuilderPhase.java Mon Jun 27 17:15:12 2011 +0200 @@ -963,7 +963,7 @@ append(deoptimize); frameState.pushReturn(resultType, Constant.defaultForKind(resultType, graph)); } else { - Invoke invoke = new Invoke(bci(), opcode, resultType.stackKind(), args, target, target.signature().returnType(method.holder()), method.typeProfile(bci()), graph); + Invoke invoke = new Invoke(bci(), opcode, resultType.stackKind(), args, target, target.signature().returnType(method.holder()), graph); Value result = appendWithBCI(invoke); invoke.setExceptionEdge(handleException(null, bci())); frameState.pushReturn(resultType, result);
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/InliningPhase.java Mon Jun 27 17:15:12 2011 +0200 @@ -32,25 +32,29 @@ import com.oracle.max.graal.compiler.ir.Deoptimize.DeoptAction; import com.oracle.max.graal.compiler.value.*; import com.oracle.max.graal.graph.*; +import com.sun.cri.bytecode.*; import com.sun.cri.ci.*; import com.sun.cri.ri.*; public class InliningPhase extends Phase { + public static HashMap<RiMethod, Integer> methodCount = new HashMap<RiMethod, Integer>(); + private final GraalCompilation compilation; private final IR ir; private final Queue<Invoke> invokes = new ArrayDeque<Invoke>(); private final Queue<RiMethod> methods = new ArrayDeque<RiMethod>(); - private final Map<Invoke, RiMethod> parentMethod = new HashMap<Invoke, RiMethod>(); private int inliningSize; private final boolean trace; + private final Collection<Invoke> hints; - public InliningPhase(GraalCompilation compilation, IR ir, boolean trace) { + public InliningPhase(GraalCompilation compilation, IR ir, Collection<Invoke> hints, boolean trace) { this.compilation = compilation; this.ir = ir; + this.hints = hints; this.trace = trace; } @@ -60,59 +64,33 @@ inliningSize += method.codeSize(); } - public static HashMap<RiMethod, Integer> methodCount = new HashMap<RiMethod, Integer>(); + private Queue<Invoke> newInvokes = new ArrayDeque<Invoke>(); + private Graph graph; + @Override protected void run(Graph graph) { + this.graph = graph; + float ratio = GraalOptions.MaximumInlineRatio; inliningSize = compilation.method.codeSize(); - for (int iterations = 0; iterations < GraalOptions.MaximumInlineLevel; iterations++) { + + if (hints != null) { + newInvokes.addAll(hints); + } else { for (Invoke invoke : graph.getNodes(Invoke.class)) { - RiMethod parent = parentMethod.get(invoke); - if (parent == null) { - parent = compilation.method; - } - RiTypeProfile profile = parent.typeProfile(invoke.bci); - if (!checkInvokeConditions(invoke)) { - continue; - } - if (invoke.target.canBeStaticallyBound()) { - if (checkTargetConditions(invoke.target, iterations) && checkSizeConditions(invoke.target, invoke, profile, ratio)) { - addToQueue(invoke, invoke.target); + newInvokes.add(invoke); + } + } + + for (int iterations = 0; iterations < GraalOptions.MaximumInlineLevel; iterations++) { + Queue<Invoke> queue = newInvokes; + newInvokes = new ArrayDeque<Invoke>(); + for (Invoke invoke : queue) { + if (!invoke.isDeleted()) { + inlineInvoke(invoke, iterations, ratio); + if (inliningSize > GraalOptions.MaximumInstructionCount) { + break; } - } else { - RiMethod concrete = invoke.target.holder().uniqueConcreteMethod(invoke.target); - if (concrete != null) { - if (checkTargetConditions(concrete, iterations) && checkSizeConditions(concrete, invoke, profile, ratio)) { - if (trace) { - String targetName = CiUtil.format("%H.%n(%p):%r", invoke.target, false); - String concreteName = CiUtil.format("%H.%n(%p):%r", concrete, false); - TTY.println("recording concrete method assumption: %s -> %s", targetName, concreteName); - } - compilation.assumptions.recordConcreteMethod(invoke.target, concrete); - addToQueue(invoke, concrete); - } - } else if (profile != null && profile.probabilities != null && profile.probabilities.length > 0 && profile.morphism == 1) { - if (GraalOptions.InlineWithTypeCheck) { - // type check and inlining... - concrete = profile.types[0].resolveMethodImpl(invoke.target); - if (concrete != null && checkTargetConditions(concrete, iterations) && checkSizeConditions(concrete, invoke, profile, ratio)) { - IsType isType = new IsType(invoke.receiver(), profile.types[0], compilation.graph); - FixedGuard guard = new FixedGuard(graph); - guard.setNode(isType); - assert invoke.predecessors().size() == 1; - invoke.predecessors().get(0).successors().replace(invoke, guard); - guard.setNext(invoke); - - if (trace) { - TTY.println("inlining with type check, type probability: %5.3f", profile.probabilities[0]); - } - addToQueue(invoke, concrete); - } - } - } - } - if (inliningSize > GraalOptions.MaximumInstructionCount) { - break; } } @@ -157,16 +135,65 @@ } } + private void inlineInvoke(Invoke invoke, int iterations, float ratio) { + RiMethod parent = invoke.stateAfter().method(); + RiTypeProfile profile = parent.typeProfile(invoke.bci); + if (!checkInvokeConditions(invoke)) { + return; + } + if (invoke.opcode() == Bytecodes.INVOKESPECIAL || invoke.target.canBeStaticallyBound()) { + if (checkTargetConditions(invoke.target, iterations) && checkSizeConditions(invoke.target, invoke, profile, ratio)) { + addToQueue(invoke, invoke.target); + } + } else { + RiMethod concrete = invoke.target.holder().uniqueConcreteMethod(invoke.target); + if (concrete != null) { + if (checkTargetConditions(concrete, iterations) && checkSizeConditions(concrete, invoke, profile, ratio)) { + if (trace) { + String targetName = CiUtil.format("%H.%n(%p):%r", invoke.target, false); + String concreteName = CiUtil.format("%H.%n(%p):%r", concrete, false); + TTY.println("recording concrete method assumption: %s -> %s", targetName, concreteName); + } + compilation.assumptions.recordConcreteMethod(invoke.target, concrete); + addToQueue(invoke, concrete); + } + } else if (profile != null && profile.probabilities != null && profile.probabilities.length > 0 && profile.morphism == 1) { + if (GraalOptions.InlineWithTypeCheck) { + // type check and inlining... + concrete = profile.types[0].resolveMethodImpl(invoke.target); + if (concrete != null && checkTargetConditions(concrete, iterations) && checkSizeConditions(concrete, invoke, profile, ratio)) { + IsType isType = new IsType(invoke.receiver(), profile.types[0], compilation.graph); + FixedGuard guard = new FixedGuard(graph); + guard.setNode(isType); + assert invoke.predecessors().size() == 1; + invoke.predecessors().get(0).successors().replace(invoke, guard); + guard.setNext(invoke); + + if (trace) { + TTY.println("inlining with type check, type probability: %5.3f", profile.probabilities[0]); + } + addToQueue(invoke, concrete); + } + } else { + if (trace) { + TTY.println("not inlining %s because GraalOptions.InlineWithTypeCheck is false", methodName(invoke.target, invoke)); + } + } + } else { + if (trace) { + TTY.println("not inlining %s because no monomorphic receiver could be found", methodName(invoke.target, invoke)); + } + } + } + } + private String methodName(RiMethod method) { return CiUtil.format("%H.%n(%p):%r", method, false) + " (" + method.codeSize() + " bytes)"; } private String methodName(RiMethod method, Invoke invoke) { if (invoke != null) { - RiMethod parent = parentMethod.get(invoke); - if (parent == null) { - parent = compilation.method; - } + RiMethod parent = invoke.stateAfter().method(); return parent.name() + "@" + invoke.bci + ": " + CiUtil.format("%H.%n(%p):%r", method, false) + " (" + method.codeSize() + " bytes)"; } else { return CiUtil.format("%H.%n(%p):%r", method, false) + " (" + method.codeSize() + " bytes)"; @@ -241,7 +268,11 @@ } private boolean checkStaticSizeConditions(RiMethod method, Invoke invoke) { - if (method.codeSize() > GraalOptions.MaximumInlineSize) { + int maximumSize = GraalOptions.MaximumInlineSize; + if (hints != null && hints.contains(invoke)) { + maximumSize = GraalOptions.MaximumFreqInlineSize; + } + if (method.codeSize() > maximumSize) { if (trace) { TTY.println("not inlining %s because of code size (size: %d, max size: %d)", methodName(method, invoke), method.codeSize(), GraalOptions.MaximumInlineSize); } @@ -254,10 +285,7 @@ int maximumSize = GraalOptions.MaximumTrivialSize; float ratio = 0; if (profile != null && profile.count > 0) { - RiMethod parent = parentMethod.get(invoke); - if (parent == null) { - parent = compilation.method; - } + RiMethod parent = invoke.stateAfter().method(); ratio = profile.count / (float) parent.invocationCount(); if (ratio >= GraalOptions.FreqInlineRatio) { maximumSize = GraalOptions.MaximumFreqInlineSize; @@ -265,6 +293,9 @@ maximumSize = GraalOptions.MaximumInlineSize; } } + if (hints != null && hints.contains(invoke)) { + maximumSize = GraalOptions.MaximumFreqInlineSize; + } if (method.codeSize() > maximumSize) { if (trace) { TTY.println("not inlining %s because of code size (size: %d, max size: %d, ratio %5.3f, %s)", methodName(method, invoke), method.codeSize(), maximumSize, ratio, profile); @@ -359,7 +390,7 @@ for (Node node : duplicates.values()) { if (node instanceof Invoke) { - parentMethod.put((Invoke) node, method); + newInvokes.add((Invoke) node); } }
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/schedule/Block.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/schedule/Block.java Mon Jun 27 17:15:12 2011 +0200 @@ -24,6 +24,7 @@ import java.util.*; +import com.oracle.max.graal.compiler.debug.*; import com.oracle.max.graal.compiler.ir.*; import com.oracle.max.graal.graph.*; @@ -178,4 +179,46 @@ public void setInstructions(List<Node> instructions) { this.instructions = instructions; } + + public static void iteratePostOrder(List<Block> blocks, BlockClosure closure) { + ArrayList<Block> startBlocks = new ArrayList<Block>(); + for (Block block : blocks) { + if (block.getPredecessors().size() == 0) { + startBlocks.add(block); + } + } + iteratePostOrder(blocks, closure, startBlocks.toArray(new Block[startBlocks.size()])); + } + + public static void iteratePostOrder(List<Block> blocks, BlockClosure closure, Block... startBlocks) { + BitMap visited = new BitMap(blocks.size()); + LinkedList<Block> workList = new LinkedList<Block>(); + for (Block block : startBlocks) { + workList.add(block); + visited.set(block.blockID()); + } + + while (!workList.isEmpty()) { + Block b = workList.remove(); + + closure.apply(b); + + for (Block succ : b.getSuccessors()) { + if (!visited.get(succ.blockID())) { + boolean delay = false; + for (Block pred : succ.getPredecessors()) { + if (!visited.get(pred.blockID()) && !(pred.lastNode instanceof LoopEnd)) { + delay = true; + break; + } + } + + if (!delay) { + visited.set(succ.blockID()); + workList.add(succ); + } + } + } + } + } }
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/value/FrameState.java Fri Jun 24 15:01:20 2011 +0200 +++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/value/FrameState.java Mon Jun 27 17:15:12 2011 +0200 @@ -121,6 +121,10 @@ return rethrowException; } + public RiMethod method() { + return method; + } + /** * Gets a copy of this frame state. */