Mercurial > hg > truffle
view graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/phases/EscapeAnalysisPhase.java @ 3211:76507b87dd25
global absolute probability analysis:
* added switch probability, changed branch probability from int to double
* absolute probability on each FixedNode computed by new ComputeProbabilityPhase
* loopFrequency estimation on LoopBegin nodes
* added GraalOptions.ProbabilityAnalysis flag: builds probability information and let inlining and escape analysis use it
author | Lukas Stadler <lukas.stadler@jku.at> |
---|---|
date | Tue, 12 Jul 2011 17:00:25 +0200 |
parents | ad788d3b0dc4 |
children | 3d66094eeda1 |
line wrap: on
line source
/* * 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 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.ir.Phi.PhiType; import com.oracle.max.graal.compiler.observer.*; 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.*; public class EscapeAnalysisPhase extends Phase { public interface MergeableState <T> { T clone(); boolean merge(Merge merge, Collection<T> withStates); void loopBegin(LoopBegin loopBegin); void loopEnd(LoopEnd loopEnd, T loopEndState); void afterSplit(FixedNode node); } public abstract static class PostOrderNodeIterator<T extends MergeableState<T>> { private final NodeBitMap visitedEnds; private final Deque<FixedNode> nodeQueue; private final HashMap<FixedNode, T> nodeStates; private final FixedNode start; protected T state; public PostOrderNodeIterator(FixedNode start, T initialState) { visitedEnds = start.graph().createNodeBitMap(); nodeQueue = new ArrayDeque<FixedNode>(); nodeStates = new HashMap<FixedNode, T>(); this.start = start; this.state = initialState; } public void apply() { FixedNode current = start; do { if (current instanceof Invoke) { invoke((Invoke) current); queueSuccessors(current); current = nextQueuedNode(); } else if (current instanceof LoopBegin) { state.loopBegin((LoopBegin) current); nodeStates.put(current, state); state = state.clone(); loopBegin((LoopBegin) current); current = ((LoopBegin) current).next(); assert current != null; } else if (current instanceof LoopEnd) { T loopBeginState = nodeStates.get(((LoopEnd) current).loopBegin()); if (loopBeginState != null) { loopBeginState.loopEnd((LoopEnd) current, state); } loopEnd((LoopEnd) current); current = nextQueuedNode(); } else if (current instanceof Merge) { merge((Merge) current); current = ((Merge) current).next(); assert current != null; } else if (current instanceof FixedNodeWithNext) { FixedNode next = ((FixedNodeWithNext) current).next(); node(current); current = next; assert current != null; } else if (current instanceof EndNode) { end((EndNode) current); queueMerge((EndNode) current); current = nextQueuedNode(); } else if (current instanceof Deoptimize) { deoptimize((Deoptimize) current); current = nextQueuedNode(); } else if (current instanceof Return) { returnNode((Return) current); current = nextQueuedNode(); } else if (current instanceof Unwind) { unwind((Unwind) current); current = nextQueuedNode(); } else if (current instanceof ControlSplit) { controlSplit((ControlSplit) current); queueSuccessors(current); current = nextQueuedNode(); } else { assert false : current.shortName(); } } while(current != null); } private void queueSuccessors(FixedNode x) { nodeStates.put(x, state); for (Node node : x.successors()) { if (node != null) { nodeQueue.addFirst((FixedNode) node); } } } private FixedNode nextQueuedNode() { int maxIterations = nodeQueue.size(); while (maxIterations-- > 0) { FixedNode node = nodeQueue.removeFirst(); if (node instanceof Merge) { Merge merge = (Merge) node; state = nodeStates.get(merge.endAt(0)).clone(); ArrayList<T> states = new ArrayList<T>(merge.endCount() - 1); for (int i = 1; i < merge.endCount(); i++) { T other = nodeStates.get(merge.endAt(i)); assert other != null; states.add(other); } boolean ready = state.merge(merge, states); if (ready) { return merge; } else { nodeQueue.addLast(merge); } } else { assert node.predecessors().size() == 1; state = nodeStates.get(node.predecessors().get(0)).clone(); state.afterSplit(node); return node; } } return null; } private void queueMerge(EndNode end) { assert !visitedEnds.isMarked(end); assert !nodeStates.containsKey(end); nodeStates.put(end, state); visitedEnds.mark(end); Merge merge = end.merge(); boolean endsVisited = true; for (int i = 0; i < merge.endCount(); i++) { if (!visitedEnds.isMarked(merge.endAt(i))) { endsVisited = false; break; } } if (endsVisited) { nodeQueue.add(merge); } } protected abstract void node(FixedNode node); protected void end(EndNode endNode) { node(endNode); } protected void merge(Merge merge) { node(merge); } protected void loopBegin(LoopBegin loopBegin) { node(loopBegin); } protected void loopEnd(LoopEnd loopEnd) { node(loopEnd); } protected void deoptimize(Deoptimize deoptimize) { node(deoptimize); } protected void controlSplit(ControlSplit controlSplit) { node(controlSplit); } protected void returnNode(Return returnNode) { node(returnNode); } protected void invoke(Invoke invoke) { node(invoke); } protected void unwind(Unwind unwind) { node(unwind); } } public static class BlockExitState implements MergeableState<BlockExitState> { public final Value[] fieldState; public final VirtualObject virtualObject; public FloatingNode virtualObjectField; public BlockExitState(EscapeField[] fields, VirtualObject virtualObject) { this.fieldState = new Value[fields.length]; this.virtualObject = virtualObject; this.virtualObjectField = null; for (int i = 0; i < fields.length; i++) { fieldState[i] = Constant.defaultForKind(fields[i].kind(), virtualObject.graph()); virtualObjectField = new VirtualObjectField(virtualObject, virtualObjectField, fieldState[i], i, virtualObject.graph()); } } public BlockExitState(BlockExitState state) { this.fieldState = state.fieldState.clone(); this.virtualObject = state.virtualObject; this.virtualObjectField = state.virtualObjectField; } public void updateField(int fieldIndex) { virtualObjectField = new VirtualObjectField(virtualObject, virtualObjectField, fieldState[fieldIndex], fieldIndex, virtualObject.graph()); } @Override public BlockExitState clone() { return new BlockExitState(this); } @Override public boolean merge(Merge merge, Collection<BlockExitState> withStates) { Phi vobjPhi = null; Phi[] valuePhis = new Phi[fieldState.length]; for (BlockExitState other : withStates) { if (virtualObjectField != other.virtualObjectField && vobjPhi == null) { vobjPhi = new Phi(CiKind.Illegal, merge, PhiType.Virtual, virtualObject.graph()); vobjPhi.addInput(virtualObjectField); virtualObjectField = vobjPhi; } for (int i2 = 0; i2 < fieldState.length; i2++) { if (fieldState[i2] != other.fieldState[i2] && valuePhis[i2] == null) { valuePhis[i2] = new Phi(fieldState[i2].kind, merge, PhiType.Value, virtualObject.graph()); valuePhis[i2].addInput(fieldState[i2]); fieldState[i2] = valuePhis[i2]; } } } for (BlockExitState other : withStates) { if (vobjPhi != null) { vobjPhi.addInput(other.virtualObjectField); } for (int i2 = 0; i2 < fieldState.length; i2++) { if (valuePhis[i2] != null) { valuePhis[i2].addInput(other.fieldState[i2]); } } } assert vobjPhi == null || vobjPhi.valueCount() == withStates.size() + 1; for (int i2 = 0; i2 < fieldState.length; i2++) { if (valuePhis[i2] != null) { virtualObjectField = new VirtualObjectField(virtualObject, virtualObjectField, valuePhis[i2], i2, virtualObject.graph()); assert valuePhis[i2].valueCount() == withStates.size() + 1; } } return true; } @Override public void loopBegin(LoopBegin loopBegin) { Phi vobjPhi = null; vobjPhi = new Phi(CiKind.Illegal, loopBegin, PhiType.Virtual, virtualObject.graph()); vobjPhi.addInput(virtualObjectField); virtualObjectField = vobjPhi; for (int i2 = 0; i2 < fieldState.length; i2++) { Phi valuePhi = new Phi(fieldState[i2].kind, loopBegin, PhiType.Value, virtualObject.graph()); valuePhi.addInput(fieldState[i2]); fieldState[i2] = valuePhi; updateField(i2); } } @Override public void loopEnd(LoopEnd x, BlockExitState loopEndState) { while (!(virtualObjectField instanceof Phi)) { virtualObjectField = ((VirtualObjectField) virtualObjectField).lastState(); } ((Phi) virtualObjectField).addInput(loopEndState.virtualObjectField); assert ((Phi) virtualObjectField).valueCount() == 2; for (int i2 = 0; i2 < fieldState.length; i2++) { ((Phi) fieldState[i2]).addInput(loopEndState.fieldState[i2]); assert ((Phi) fieldState[i2]).valueCount() == 2; } } @Override public void afterSplit(FixedNode node) { // nothing to do... } } public class EscapementFixup { private List<Block> blocks; private final Map<Object, Integer> fields = new HashMap<Object, Integer>(); private final Map<Block, BlockExitState> exitStates = new HashMap<Block, BlockExitState>(); private final EscapeOp op; private final Graph graph; private final Node node; private EscapeField[] escapeFields; 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() { assert node instanceof FixedNodeWithNext; escapeFields = op.fields(node); for (int i = 0; i < escapeFields.length; i++) { fields.put(escapeFields[i].representation(), i); } final VirtualObject virtual = new VirtualObject(((Value) node).exactType(), escapeFields, graph); if (GraalOptions.TraceEscapeAnalysis || GraalOptions.PrintEscapeAnalysis) { TTY.println("new virtual object: " + virtual); } node.replaceAtUsages(virtual); final FixedNode next = ((FixedNodeWithNext) node).next(); node.replaceAndDelete(next); final BlockExitState startState = new BlockExitState(escapeFields, virtual); final PostOrderNodeIterator<?> iterator = new PostOrderNodeIterator<BlockExitState>(next, startState) { @Override protected void node(FixedNode node) { int changedField = op.updateState(virtual, node, fields, state.fieldState); if (changedField != -1) { state.updateField(changedField); } if (!node.isDeleted() && node instanceof StateSplit && ((StateSplit) node).stateAfter() != null) { if (state.virtualObjectField != null) { ((StateSplit) node).stateAfter().addVirtualObjectMapping(state.virtualObjectField); } } } }; iterator.apply(); } private void process() { ArrayList<Node> arrayList = new ArrayList<Node>(node.usages()); for (Node usage : arrayList) { op.beforeUpdate(node, usage); } } } 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) { 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 minimumWeight = GraalOptions.ForcedInlineEscapeWeight; do { double weight = analyze(op, node, exits, invokes); if (exits.size() != 0) { if (GraalOptions.TraceEscapeAnalysis || GraalOptions.PrintEscapeAnalysis) { TTY.println("%n####### escaping object: %d %s (%s) in %s", node.id(), node.shortName(), ((Value) node).exactType(), compilation.method); if (GraalOptions.TraceEscapeAnalysis) { 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 (compilation.compiler.isObserved()) { compilation.compiler.fireCompilationEvent(new CompilationEvent(compilation, "Before escape " + node.id(), graph, true, false)); } if (GraalOptions.TraceEscapeAnalysis || GraalOptions.PrintEscapeAnalysis) { TTY.println("%n!!!!!!!! non-escaping object: %d %s (%s) in %s", node.id(), node.shortName(), ((Value) node).exactType(), compilation.method); } new EscapementFixup(op, graph, node).apply(); if (compilation.compiler.isObserved()) { compilation.compiler.fireCompilationEvent(new CompilationEvent(compilation, "After escape", graph, true, false)); } new PhiSimplifier(graph); break; } if (weight < minimumWeight) { if (GraalOptions.TraceEscapeAnalysis || GraalOptions.PrintEscapeAnalysis) { TTY.println("%n####### possibly escaping object: %d in %s (insufficient weight for inlining)", node.id(), compilation.method); } break; } if (!GraalOptions.Inline) { break; } if (GraalOptions.TraceEscapeAnalysis || GraalOptions.PrintEscapeAnalysis) { TTY.println("Trying inlining to get a non-escaping object for %d", node.id()); } new InliningPhase(compilation, ir, invokes).apply(graph); new DeadCodeEliminationPhase().apply(graph); if (node.isDeleted()) { if (GraalOptions.TraceEscapeAnalysis || GraalOptions.PrintEscapeAnalysis) { TTY.println("%n!!!!!!!! object died while performing escape analysis: %d %s (%s) in %s", node.id(), node.shortName(), ((Value) node).exactType(), compilation.method); } break; } exits.clear(); invokes.clear(); } while (iterations++ < 3); } } } private double analyze(EscapeOp op, Node node, Collection<Node> exits, Collection<Invoke> invokes) { double 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 { if (GraalOptions.ProbabilityAnalysis && usage instanceof FixedNode) { weight += ((FixedNode) usage).probability(); } 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 abstract static class EscapeOp implements Op { public abstract boolean canAnalyze(Node node); 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 AccessMonitor) { AccessMonitor x = (AccessMonitor) usage; assert x.object() == node; return false; } else { return true; } } public abstract EscapeField[] fields(Node node); 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.replaceAndDelete(guard.next()); } x.delete(); } else if (usage instanceof IsType) { IsType x = (IsType) usage; assert x.type() == ((Value) node).exactType(); if (x.usages().size() == 1 && x.usages().get(0) instanceof FixedGuard) { FixedGuard guard = (FixedGuard) x.usages().get(0); guard.replaceAndDelete(guard.next()); } x.delete(); } else if (usage instanceof AccessMonitor) { AccessMonitor x = (AccessMonitor) usage; x.replaceAndDelete(x.next()); } } public abstract int updateState(Node node, Node current, Map<Object, Integer> fieldIndex, Value[] fieldState); } }